B    Socket and XTI Programming Examples

This appendix contains annotated files for a sample server/client [Footnote 15] credit card authorization program. Clients access a server on the merchant's behalf and request authorization from the server to put a charge on the client's credit card. The server maintains a database of authorized merchants and their passwords, as well as a database of credit card customers, their credit limit, and current balance. It either authorizes or rejects a client request based on the information in its database.

Several variations on the credit card authorization program are presented, including connection-oriented and connectionless modes. The connection-oriented and connectionless modes each contain socket and XTI code for the server and client portions of the program.

Although the program uses network programming in a real world application, it has the following limitations:

The information is organized as follows:

You can obtain copies of these example programs from /usr/examples/network_programming directory.

B.1    Connection-Oriented Programs

This section contains sockets and XTI variations of the same server and client programs, written for connection-oriented modes communication.

B.1.1    Socket Server Program

Example B-1 implements a server using the socket interface.

Example B-1:  Connection-Oriented Socket Server Program

/*
 *
 * This file contains the main socket server code
 * for a connection-oriented mode of communication.
 *
 * Usage:       socketserver
 *
 */
 
#include "server.h"
 
char                    *parse(char *);
struct transaction      *verifycustomer(char *, int, char *);
 
main(int argc, char *argv[])
{
        int                   sockfd;
        int                   newsockfd;
        struct sockaddr_in    serveraddr;
        struct sockaddr_in    clientaddr;
        int                   clientaddrlen = sizeof(clientaddr);
        struct hostent        *he;
        int                   pid;
 
 
        signal(SIGCHLD, SIG_IGN);
 
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)    [1]
        {
                perror("socket_create");
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,
              sizeof(struct sockaddr_in));                     [2]
        serveraddr.sin_family      = AF_INET;
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);        [3]
        serveraddr.sin_port        = htons(SERVER_PORT);       [4]
 
 
        if ( bind(sockfd,                                      [5]
                  (struct sockaddr *)&serveraddr,
                  sizeof(struct sockaddr_in)) < 0) {
                perror("socket_bind");
                exit(2);
        }
 
        listen(sockfd, 8);                                     [6]
 
        while(1) {
 
                if ((newsockfd =
                      accept(sockfd,                           [7]
                             (struct sockaddr *) &clientaddr,
                             &clientaddrlen)) < 0) {
                        if (errno == EINTR) {
                                printf("Bye...\n");
                                exit(0);
                        } else {
                                perror("socket_accept");
                                exit(3);
                        }
                }
 
                pid = fork();
 
                switch(pid) {
                        case -1:        /* error */
                                perror("dosession_fork");
                                break;
                        default:
                                close(newsockfd);
                                break;
                        case 0:         /* child */
 
                                close(sockfd);
                                transactions(newsockfd);
 
                                close(newsockfd);
                                return(0);
 
                }
        }
 
}
 
 
transactions(int fd)                                           [8]
{
        int     bytes;
        char    *reply;
        int     dcount;
        char    datapipe[MAXBUFSIZE+1];
 
        /*
         * Look at the data buffer and parse commands,
         * keep track of the collected data through
         * transaction_status.
         *
         */
         while (1) {
                if ((dcount=recv(fd, datapipe, MAXBUFSIZE, 0))    [9]
                    < 0) {
                        perror("transactions_receive");
                        break;
                }
                if (dcount == 0) {
                        return(0);
                }
 
                datapipe[dcount] = '\0';
 
                if ((reply=parse(datapipe)) != NULL) {
                        send(fd, reply, strlen(reply), 0);     [10]
                }
         }
}

  1. Create a socket with the socket call.

    AF_INET specifies the Internet communication domain. Alternatively, if OSI transport were supported, a corresponding constant such as AF_OSI would be required here. The socket type SOCK_STREAM is specified for TCP or connection-oriented communication. This parameter indicates that the socket is connection-oriented. Contrast the socket call with the t_open call in the XTI server example (Section B.1.3). [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an entity on the network. For the TCP/IP and UDP/IP this is the Internet address of the server and the port number on which it is listening.

    Note that the information contained in the sockaddr_in structure is dependent on the address family, which is AF_INET in this example. If AF_OSI were used instead of AF_INET, then sockaddr_osi would be required for the bind call instead of sockaddr_in. [Return to example]

  3. INADDRANY signifies any attached interface adapter on the system. All numbers must be converted to the network format using appropriate macros. See the following reference pages for more information: htonl(3), htons(3), ntohl(3), and ntohs(3). [Return to example]

  4. SERVER_PORT is defined in the common.h header file. It is a short integer, which helps identify the server process from other application processes. Numbers from 0 to 1024 are reserved. [Return to example]

  5. Bind the server's address to this socket with the bind call. The combination of the address and port number identify it uniquely on the network. [Return to example]

  6. Specify the number of pending connections the server can queue while it finishes processing the previous accept call. This value governs the success rate of connections while the server processes accept calls. Use a larger number to obtain a better success rate if multiple clients are sending the server connect requests. The operating system imposes a ceiling on this value. [Return to example]

  7. Accept connections on this socket. For each connection, the server forks a child process to handle the session to completion. The server then resumes listening for new connection requests. This is an example of a concurrent server. You can also have an iterative server, meaning that the server handles the data itself. See Section B.2 for an example of iterative servers. [Return to example]

  8. Each incoming message packet is accepted and passed to the parse function, which tracks the information provided, such as the merchant's login ID, password, and customer's credit card number. This process is repeated until the parse function identifies a complete transaction and returns a response packet, to be sent to the client program.

    The client program can send information packets in any order (and in one or more packets), so the parse function is designed to remember state information sufficient to deal with this unstructured message stream.

    Since the program uses a connection-oriented protocol for data transfer, this function uses send and recv to send and receive messages, respectively. [Return to example]

  9. Receive data with the recv call. [Return to example]

  10. Send data with the send call. [Return to example]

B.1.2    Socket Client Program

Example B-2 implements a client program that can communicate with the socketserver interface shown in Example B-1.

Example B-2:  Connection-Oriented Socket Client Program

/*
 *
 * This generates the client program.
 *
 * usage: client [serverhostname]
 *
 * If a host name is not specified, the local
 * host is assumed.
 *
 */
 
 
#include "client.h"
 
main(int argc, char *argv[])
{
        int                     sockfd;
        struct sockaddr_in      serveraddr;
        struct hostent          *he;
        int                     n;
        char                    *serverhost = "localhost";
        struct hostent          *serverhostp;
        char                    buffer[1024];
        char                    inbuf[1024];
 
 
        if (argc>1) {
                serverhost = argv[1];
        }
 
        init();
 
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)    [1]
        {
                perror("socket_create");
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,
              sizeof(struct sockaddr_in));                     [2]
        serveraddr.sin_family      = AF_INET;
 
        if ((serverhostp = gethostbyname(serverhost)) ==       [3]
            (struct hostent *)NULL) {
                fprintf(stderr,"gethostbyname on %s failed\n",
                        serverhost);
                exit(1);
        }
        bcopy(serverhostp->h_addr,
              (char *)&(serveraddr.sin_addr.s_addr),
                          serverhostp->h_length);
 
        serveraddr.sin_port        = htons(SERVER_PORT);       [4]
 
        /* Now connect to the server */
        if (connect(sockfd, &serveraddr, sizeof(serveraddr))   [5]
            < 0) {
                perror ("connect");
                exit(2);
        }
 
 
        while(1) {
                /* Merchant record */
                sprintf(buffer, "%%%%m%s##%%%%p%s##",
                        merchantname, password);
 
                printf("\n\nSwipe card, enter amount: ");
                fflush(stdout);
                if (scanf("%s", inbuf) == EOF) {
                        printf("bye...\n");
                        exit(0);
                }
                soundbytes();
 
                sprintf(buffer, "%s%%%%a%s##%%%%n%s##",
                        buffer, inbuf, swipecard());
 
                if (send(sockfd, buffer, strlen(buffer), 0)    [6]
                    < 0) {
                        perror("send");
                        exit(1);
                }
 
                /* receive info */
                if ((n = recv(sockfd, buffer, 1024, 0)) < 0) {    [7]
                        perror("recv");
                        exit(1);
                }
                buffer[n] = '\0';
 
                if ((n=analyze(buffer))== 0) {
                        printf("transaction failure,"
                               " try again\n");
                } else if (n<0) {
                        printf("login failed, try again\n");
                        init();
                }
        }
 
}

  1. Create a socket with the socket call.

    AF_INET is the socket type for the Internet communication domain. Note that this parameter must match the protocol and type selected in the corresponding server program.

    Contrast the socket call with the t_open call in the XTI client example (Section B.1.4). [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an entity on the network. For the TCP/IP protocol suite, this is the Internet address of the server and the port number on which it is listening.

    Note that the information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]

  3. Getting information about the server depends on the protocol or the address family. To get the IP address of the server, you can use the gethostbyname routine. [Return to example]

  4. SERVER_PORT is defined in the <common.h> header file. It is imperative that the same port number be used to connect to the socket server program. The server and client select the port number, which functions as a well known address for communication. [Return to example]

  5. Client issues a connect call to connect to the server. When the connect call is used with a connection-oriented protocol, it allows the client to build a connection with the server before sending data. This is analogous to dialing a phone number. [Return to example]

  6. Send data with the send call. [Return to example]

  7. Receive data with the recv call. [Return to example]

B.1.3    XTI Server Program

Example B-3 implements a server using the XTI library for network communication. It is an alternative design for a communication program that makes it transport independent. Compare this program with the socket server program in Section B.1.1. This program has the same limitations described at the beginning of the appendix.

Example B-3:  Connection-Oriented XTI Server Program

/*
 *
 *
 * This file contains the main XTI server code
 * for a connection-oriented mode of communication.
 *
 * Usage:       xtiserver
 *
 */
#include "server.h"
 
char                    *parse(char *);
struct transaction      *verifycustomer(char *, int, char *);
 
main(int argc, char *argv[])
{
        int                     xtifd;
        int                     newxtifd;
        struct sockaddr_in      serveraddr;
        struct hostent          *he;
        int                     pid;
        struct t_bind           *bindreqp;
        struct t_call           *call;
 
 
        signal(SIGCHLD, SIG_IGN);
 
 
        if ((xtifd = t_open("/dev/streams/xtiso/tcp+", O_RDWR,  [1]
                            NULL)) < 0) {
                xerror("xti_open", xtifd);
                exit(1);
        }
 
 
        bzero((char *) &serveraddr, sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;                  [2]
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);        [3]
        serveraddr.sin_port        = htons(SERVER_PORT);       [4]
 
        /* allocate structures for the t_bind and t_listen call */
        if (((bindreqp=(struct t_bind *)
                       t_alloc(xtifd, T_BIND, T_ALL))
                == NULL) ||
            ((call=(struct t_call *)
                       t_alloc(xtifd, T_CALL, T_ALL))
                == NULL)) {
                xerror("xti_alloc", xtifd);
                exit(3);
        }
 
 
        bindreqp->addr.buf      = (char *)&serveraddr;
        bindreqp->addr.len      = sizeof(serveraddr);
 
        /*
         * Specify how many pending connections can be
         * maintained, until finish accept processing
         *
         */
        bindreqp->qlen          = 8;                           [5]
 
        if (t_bind(xtifd, bindreqp, (struct t_bind *)NULL)     [6]
            < 0) {
                xerror("xti_bind", xtifd);
                exit(4);
        }
 
        /*
         * Now the socket is ready to accept connections.
         * For each connection, fork a child process in charge
         * of the session, and then resume accepting connections.
         *
         */
 
        while(1) {
 
                if (t_listen(xtifd, &call) < 0) {              [7]
                        if (errno == EINTR) {
                                printf("Bye...\n");
                                exit(0);
                        } else {
                                xerror("t_listen", xtifd);
                                exit(4);
                        }
                }
 
                /*
                 * Create a new transport endpoint on which
                 * to accept a connection
                 *
                 */
                if ((newxtifd=t_open("/dev/streams/xtiso/tcp+", [8]
                                     O_RDWR, NULL)) < 0) {
                        xerror("xti_newopen", xtifd);
                        exit(5);
                }
 
                /* accept connection */
                if (t_accept(xtifd, newxtifd, call) < 0) {    [9]
                        xerror("xti_accept", xtifd);
                        exit(7);
                }
                pid = fork();
 
                switch(pid) {
                        case -1:        /* error */
                                xerror("dosession_fork", xtifd);
                                break;
                        default:
                                t_close(newxtifd);
                                break;
                        case 0:         /* child */
 
                                t_close(xtifd);
                                transactions(newxtifd);
                                if ((t_free((char *)bindreqp,
                                     T_BIND) < 0) ||
                                     (t_free((char *)call,
                                     T_CALL) < 0)) {
                                          xerror("xti_free", xtifd);
                                          exit(3);
                                }
 
                                t_close(newxtifd);
                                return(0);
 
                }
        }
 
}
 
 
transactions(int fd)                                           [10]
{
        int     bytes;
        char    *reply;
        int     dcount;
        int     flags;
        char    datapipe[MAXBUFSIZE+1];
 
        /*
         * Look at the data buffer and parse commands, if more data
         * required go get it
         * Since the protocol is SOCK_STREAM oriented, no data
         * boundaries will be preserved.
         *
         */
         while (1) {
                if ((dcount=t_rcv(fd, datapipe, MAXBUFSIZE,    [11]
                                  &flags)) < 0){
                        /* if disconnected bid a goodbye */
                        if (t_errno == TLOOK) {
                                int tmp = t_look(fd);
 
                                if (tmp != T_DISCONNECT) {
                                        t_scope(tmp);
                                } else {
                                        exit(0);
                                }
                        }
                        xerror("transactions_receive", fd);
                        break;
                }
                if (dcount == 0) {
                        /* consolidate all transactions */
                        return(0);
                }
 
                datapipe[dcount] = ' ';
 
                if ((reply=parse(datapipe)) != NULL) {
                        if (t_snd(fd, reply, strlen(reply), 0) [12]
                            < 0) {
                                xerror("xti_send", fd);
                                break;
                        }
                }
         }
}

  1. The t_open call specifies a device special file name; for example /dev/streams/xtiso/tcp+. This file name provides the necessary abstraction for the TCP transport protocol over IP. Unlike the socket interface, where you specify the address family (for example, AF_INET), this information is already represented in the choice of the device special file. The /dev/streams/xtiso/tcp+ file implies both TCP transport and IP. See the Chapter 5 for information about STREAMS devices.

    As mentioned in Section B.1.1, if the OSI transport were available you would use a device such as /dev/streams/xtiso/cots.

    Contrast the t_open call with the socket call in Section B.1.1. [Return to example]

  2. Selection of the address depends on the choice of the transport protocol. Note that in the socket example the address family was the same as used in the socket system call. With XTI, the choice is not obvious and you must know the appropriate mapping from the transport protocol to sockaddr. See Chapter 3 for more information. [Return to example]

  3. INADDRANY signifies any attached interface adapter on the system. All numbers must be converted to the network format using appropriate macros. See the following reference pages for more information: htonl(3), htons(3), ntohl(3), ntohs(3). [Return to example]

  4. SERVER_PORT is defined in the <common.h> header file. It has a data type of short integer which helps identify the server process from other application processes. Numbers from 0 to 1024 are reserved. [Return to example]

  5. Specify the number of pending connections the server can queue while it processes the last request. [Return to example]

  6. Bind the server's address with the t_bind call. The combination of the address and port number uniquely identify it on the network. After the server process' address is bound, the server process is registered on the system and can be identified by the lower level kernel functions to which to direct any requests. [Return to example]

  7. Listen for connection requests with the t_listen function. [Return to example]

  8. Create a new transport endpoint with another call to the t_open function. [Return to example]

  9. Accept the connection request with the t_accept function. [Return to example]

  10. Each incoming message packet is accepted and passed to the parse function, which tracks the information provided (such as the merchant's login ID, password, and customer's credit card number). This process is repeated until the parse function identifies a complete transaction and returns a response packet to be sent to the client program.

    The client program can send information packets in any order (and in one or more packets), so the parse function is designed to remember state information sufficient to deal with this unstructured message stream.

    Since the program uses a connection-oriented protocol for data transfer, this function uses t_snd and t_rcv to send and receive data, respectively. [Return to example]

  11. Receive data with the t_rcv function. [Return to example]

  12. Send data with the t_snd function. [Return to example]

B.1.4    XTI Client Program

Example B-4 implements a client program that can communicate with the xtiserver interface shown in Section B.1.3. Compare this program with the socket client program in Example B-3.

Example B-4:  Connection-Oriented XTI Client Program

/*
 *
 * This file contains the main XTI client code
 * for a connection-oriented mode of communication.
 *
 * Usage: xticlient [serverhostname]
 *
 * If a host name is not specified, the local
 * host is assumed.
 *
 */
 
#include "client.h"
 
main(int argc, char *argv[])
{
        int                     xtifd;
        struct sockaddr_in      serveraddr;
        int                     n;
        char                    *serverhost = "localhost";
        struct hostent          *serverhostp;
        char                    buffer[1024];
        char                    inbuf[1024];
        struct t_call           *sndcall;
        int                     flags = 0;
 
 
        if (argc>1) {
                serverhost = argv[1];
        }
 
        init();
 
        if ((xtifd = t_open("/dev/streams/xtiso/tcp+", O_RDWR, [1]
                            NULL)) < 0) {
                xerror("xti_open", xtifd);
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,                           [2]
              sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;                 [3]
        if ((serverhostp = gethostbyname(serverhost)) ==      [4]
            (struct hostent *)NULL) {
                fprintf(stderr,"gethostbyname on %s failed\n",
                        serverhost);
                exit(1);
        }
        bcopy(serverhostp->h_addr,
              (char *)&(serveraddr.sin_addr.s_addr),
                          serverhostp->h_length);
        serveraddr.sin_port        = htons(SERVER_PORT);      [5]
 
        if (t_bind(xtifd, (struct t_bind *)NULL,              [6]
                   (struct t_bind *)NULL) < 0) {
                xerror("bind", xtifd);
                exit(2);
        }
 
        /* Allocate structures for the t_bind and t_listen calls */
        if ((sndcall=(struct t_call *)t_alloc(xtifd, T_CALL,
              T_ALL)) == NULL) {
              xerror("xti_alloc", xtifd);
              exit(3);
        }
 
        sndcall.opt.maxlen      = 0;
        sndcall.udata.maxlen    = 0;
        sndcall.addr.buf        = (char *)&serveraddr;
        sndcall.addr.len        = sizeof(serveraddr);
 
        if (t_connect(xtifd, sndcall,                         [7]
                      (struct t_call *)NULL) < 0) {
                xerror ("t_connect", xtifd);
                exit(3);
        }
 
 
        while(1) {
                /* Merchant record */
                sprintf(buffer, "%%%%m%s##%%%%p%s##",
                        merchantname, password);
 
                printf("\n\nSwipe card, enter amount: ");
                fflush(stdout);
                if (scanf("%s", inbuf) == EOF) {
                        printf("bye...\n");
                        exit(0);
                }
                soundbytes();
 
                sprintf(buffer, "%s%%%%a%s##%%%%n%s##",
                        buffer, inbuf, swipecard());
 
                if (t_snd(xtifd, buffer, strlen(buffer), 0)   [8]
                    < 0) {
                        xerror("t_snd", xtifd);
                        exit(1);
                }
 
                if ((n = t_rcv(xtifd, buffer, 1024, &flags))  [9]
                    < 0) {
                        xerror("t_rcv", xtifd);
                        exit(1);
                }
 
                buffer[n] = '\0';
 
                if ((n=analyze(buffer))== 0) {
                        printf("transaction failure,"
                               " try again\n");
                } else if (n<0) {
                        printf("login failed, try again\n");
                        init();
                }
        }
        if (t_free((char *)sndcall, T_CALL) < 0) {
                xerror("xti_free", xtifd);
                exit(3);
        }
}

  1. AF_INET is the socket type for the Internet communication domain. If AF_OSI were supported, it could be used to create a socket for OSI communications. The socket type SOCK_STREAM is specified for TCP or connection-oriented communication.

    The t_open call specifies a special device file name instead of the socket address family, socket type, and protocol that the socket call requires.

    Contrast the socket call in Section B.1.2 with the t_open call. [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an entity on the network. For the TCP/IP protocol suite, which includes UDP, this is the Internet address of the server and the port number on which it is listening.

    Note that the information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]

  3. AF_INET specifies the Internet communication domain. If AF_OSI were supported, it could be used to create a socket for OSI communications. [Return to example]

  4. Obtaining information about the server depends on the protocol or the address family. To get the IP address of the server, you can use the gethostbyname routine. [Return to example]

  5. SERVER_PORT is defined in the <common.h> header file. It is imperative that the same port number be used to connect to the XTI server program. Numbers from 0 through 1024 are reserved. [Return to example]

  6. Bind the server address with the t_bind function to enable the client to start sending and receiving data. [Return to example]

  7. Initiate a connection with the server using the t_connect function. [Return to example]

  8. Send data with the t_snd function. [Return to example]

  9. Receive data with the t_rcv function. [Return to example]

B.2    Connectionless Programs

This section contains sockets and XTI variations of the same server and client programs, written for connectionless modes of communication.

B.2.1    Socket Server Program

Example B-5 implements the server portion of the application in a manner similar to the socket server described in Section B.1.1. Instead of using a connection-oriented paradigm, this program uses a connectionless (datagram/UDP) paradigm for communicating with client programs. This program has the limitations described at the beginning of the appendix.

Example B-5:  Connectionless Socket Server Program

/*
 *
 * This file contains the main socket server code
 * for a connectionless mode of communication.
 *
 * Usage:       socketserverDG
 *
 */
#include "server.h"
 
char                    *parse(char *);
struct transaction      *verifycustomer(char *, int, char *);
 
main(int argc, char *argv[])
{
        int                     sockfd;
        int                     newsockfd;
        struct sockaddr_in      serveraddr;
        int                     serveraddrlen = sizeof(serveraddr);
        struct sockaddr_in      clientaddr;
        int                     clientaddrlen = sizeof(clientaddr);
        struct hostent          *he;
        int                     pid;
 
        signal(SIGCHLD, SIG_IGN);
 
        /* Create a socket for the communications */
        if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0))        [1]
            < 0) {
                perror("socket_create");
                exit(1);
        }
 
        bzero((char *) &serveraddr,                          [2]
              sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);      [3]
        serveraddr.sin_port        = htons(SERVER_PORT);     [4]
 
        if ( bind(sockfd,                                    [5]
                  (struct sockaddr *)&serveraddr,
                  sizeof(struct sockaddr_in)) < 0) {
                perror("socket_bind");
                exit(2);
        }
 
        transactions(sockfd);
 
}
 
transactions(int fd)                                         [6]
{
        int                   bytes;
        char                  *reply;
        int                   dcount;
        char                  datapipe[MAXBUFSIZE+1];
        struct sockaddr_in    serveraddr;
        int                   serveraddrlen = sizeof(serveraddr);
 
        bzero((char *) &serveraddr, sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
        serveraddr.sin_port        = htons(SERVER_PORT);
 
        /* Look at the data buffer and parse commands.
         * Keep track of the collected data through
         * transaction_status.
         */
         while (1) {
                if ((dcount=recvfrom(fd, datapipe,           [7]
                                MAXBUFSIZE, 0,
                                (struct sockaddr *)&serveraddr,
                                &serveraddrlen)) < 0){
                        perror("transactions_receive");
                        break;
                }
                if (dcount == 0) {
                        return(0);
                }
 
                datapipe[dcount] = '\0';
 
                if ((reply=parse(datapipe)) != NULL) {
                        if (sendto(fd, reply, strlen(reply), [8]
                            0,
                            (struct sockaddr *)&serveraddr,
                            serveraddrlen) < 0) {
                                perror("transactions_sendto");
                        }
                }
         }
}

  1. Create a socket with the socket call.

    AF_INET specifies the Internet communication domain. The socket type SOCK_DGRAM is specified for UDP or connectionless communication. This parameter indicates that the program is connectionless.

    Contrast the socket call with the t_open call in the XTI server example (Section B.2.3). [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an entity on the network. For the TCP/IP protocol suite, which includes UDP, this is the Internet address of the server and the port number on which it is listening.

    The information contained in the sockaddr_in structure is dependent on the address family, which is AF_INET in this example. If AF_OSI were used instead of AF_INET, then sockaddr_osi would be required for the bind call instead of sockaddr_in. [Return to example]

  3. INADDRANY signifies any attached interface adapter on the system. All numbers must be converted to the network format using appropriate macros. See the following reference pages for more information: htonl(3), htons(3), ntohl(3), and ntohs(3). [Return to example]

  4. SERVER_PORT is defined in the <common.h> header file. It has a data type of short integer which helps identify the server process from other application processes. [Return to example]

  5. Bind the server's address to this socket with the bind call. The combination of the address and port number identify it uniquely on the network.

    After the server process' address is bound, the server process is registered on the system and can be identified by the lower level kernel functions to which to direct requests. [Return to example]

  6. Each incoming message packet is accepted and passed to the parse function, which tracks the information provided (such as the merchant's login ID, password, and customer's credit card number). This process is repeated until the parse function identifies a complete transaction and returns a response packet, to be sent to the client program.

    Since this program uses a connectionless (datagram) protocol, it uses sendto and recvfrom to send and receive data, respectively. [Return to example]

  7. Receive data with the recvfrom call. [Return to example]

  8. Send data with the sendto call. [Return to example]

B.2.2    Socket Client Program

Example B-6 implements a socket client that can communicate with the socket server in Example B-5. Section B.2.1. It uses the socket interface in the connectionless, or datagram, mode.

Example B-6:  Connectionless Socket Client Program

/*
 *
 * This file contains the main client socket code
 * for a connectionless mode of communication.
 *
 * usage: socketclientDG [serverhostname]
 *
 * If a host name is not specified, the local
 * host is assumed.
 *
 */
#include "client.h"
 
main(int argc, char *argv[])
{
        int                     sockfd;
        struct sockaddr_in      serveraddr;
        int                     serveraddrlen;
        struct hostent          *he;
        int                     n;
        char                    *serverhost = "localhost";
        struct hostent          *serverhostp;
        char                    buffer[1024];
        char                    inbuf[1024];
 
 
        if (argc>1) {
                serverhost = argv[1];
        }
 
        init();
 
 
        /* Create a socket for the communications */
        if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)    [1]
        {
                perror("socket_create");
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,                           [2]
              sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;
 
        if ((serverhostp = gethostbyname(serverhost)) ==      [3]
            (struct hostent *)NULL) {
                fprintf(stderr,"gethostbyname on %s failed\n",
                        serverhost);
                exit(1);
        }
        bcopy(serverhostp->h_addr,
              (char *)&(serveraddr.sin_addr.s_addr),
              serverhostp->h_length);
 
        serveraddr.sin_port        = htons(SERVER_PORT);      [4]
 
        /* Now connect to the server
        if (connect(sockfd, &serveraddr,                      [5]
                    sizeof(serveraddr)) < 0) {
                perror ("connect");
                exit(2);
        }
        */
 
 
        while(1) {
                /* Merchant record */
                sprintf(buffer, "%%%%m%s##%%%%p%s##",
                        merchantname, password);
 
                printf("\n\nSwipe card, enter amount: ");
                fflush(stdout);
                if (scanf("%s", inbuf) == EOF) {
                        printf("bye...\n");
                        exit(0);
                }
                soundbytes();
 
                sprintf(buffer, "%s%%%%a%s##%%%%n%s##",
                        buffer, inbuf, swipecard());
 
                if (sendto(sockfd, buffer, strlen(buffer),    [6]
                           0,
                           &serveraddr, sizeof(serveraddr)) < 0) {
                        perror("sendto");
                        exit(1);
                }
 
                /* receive info */
                if ((n = recvfrom(sockfd, buffer, 1024, 0,    [7]
                                  &serveraddr, &serveraddrlen))
                    < 0) {
                        perror("recvfrom");
                        exit(1);
                }
                buffer[n] = '\0';
 
                if ((n=analyze(buffer))== 0) {
                        printf("transaction failure, "
                               "try again\n");
                } else if (n<0) {
                        printf("login failed, try again\n");
                        init();
                }
        }
 
}

  1. Create a socket with the socket call.

    AF_INET specifies the Internet communication domain. If AF_OSI were supported, it could be used to create a socket for OSI communications. The socket type SOCK_DGRAM is specified for UDP or connectionless communication.

    Contrast the socket call with the t_open call in the XTI client example (Section B.2.4). [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an entity on the network. For the TCP/IP protocol suite, which includes UDP, this is the Internet address of the server and the port number on which it is listening.

    Note that the information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]

  3. Getting information about the server depends on the protocol or the address family. To get the IP address of the server, you can use the gethostbyname routine. [Return to example]

  4. SERVER_PORT is defined in the <common.h> header file. It is a short integer, which helps identify the server process from other application processes. [Return to example]

  5. Client issues a connect call to connect to the server. When the connect call is used with a connectionless protocol, it allows the client to store the server's address locally. This means that the client does not have to specify the server's address each time it sends a message. [Return to example]

  6. Send data with the sendto call. [Return to example]

  7. Receive data with the recvfrom call. [Return to example]

B.2.3    XTI Server Program

Example B-7 implements a server using the XTI library for network communication. It is an alternative design for a communication program that makes it transport independent. Compare this program with the socket server program in Example B-5. This program has the limitations described at the beginning of the appendix.

Example B-7:  Connectionless XTI Server Program

/*
 *
 * This file contains the main XTI server code
 * for a connectionless mode of communication.
 *
 * Usage:       xtiserverDG
 *
 */
#include "server.h"
 
char                    *parse(char *);
struct transaction      *verifycustomer(char *, int, char *);
 
main(int argc, char *argv[])
{
        int                     xtifd;
        int                     newxtifd;
        struct sockaddr_in      serveraddr;
        struct hostent          *he;
        int                     pid;
        struct t_bind           *bindreqp;
 
 
        signal(SIGCHLD, SIG_IGN);
 
 
        /* Create a transport endpoint for the communications */
        if ((xtifd = t_open("/dev/streams/xtiso/udp+",         [1]
                            O_RDWR, NULL)) < 0) {
                xerror("xti_open", xtifd);
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,                           [2]
              sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;                 [3]
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);       [4]
        serveraddr.sin_port        = htons(SERVER_PORT)       [5]
 
        /* allocate structures for the t_bind call */
        if ((bindreqp=(struct t_bind *)t_alloc(xtifd,
                                                T_BIND,
                                                T_ALL))
                == NULL) {
                xerror("xti_alloc", xtifd);
                exit(3);
        }
 
 
        bindreqp->addr.buf      = (char *)&serveraddr;
        bindreqp->addr.len      = sizeof(serveraddr);
 
        /*
         * Specify how many pending connections can be
         * maintained, while we finish "accept" processing
         *
         */
        bindreqp->qlen          = 8;                          [6]
 
        if (t_bind(xtifd, bindreqp, (struct t_bind *)NULL)    [7]
            < 0) {
                xerror("xti_bind", xtifd);
                exit(4);
        }
 
        /*
         * Now the server is ready to accept connections
         * on this socket.  For each connection, fork a child
         * process in charge of the session, and then resume
         * accepting connections.
         *
         */
        transactions(xtifd);
 
        if (t_free((char *)bindreqp, T_BIND) < 0) {
             xerror("xti_free", xtifd);
             exit(3);
        }
}
 
 
transactions(int fd)                                          [8]
{
        int                     bytes;
        char                    *reply;
        int                     dcount;
        int                     flags;
        char                    datapipe[MAXBUFSIZE+1];
        struct t_unitdata       *unitdata;
        struct sockaddr_in      clientaddr;
 
        /* Allocate structures for t_rcvudata and t_sndudata call */
        if ((unitdata=(struct t_unitdata *)t_alloc(fd,
                                                   T_UNITDATA,
                                                   T_ALL))
                == NULL) {
                xerror("xti_alloc", fd);
                exit(3);
        }
 
        /*
         * Look at the data buffer and parse commands.
         * If more data required, go get it.
         *
         */
         while (1) {
                unitdata->udata.maxlen   = MAXBUFSIZE;
                unitdata->udata.buf      = datapipe;
                unitdata->addr.maxlen    = sizeof(clientaddr);
                unitdata->addr.buf       = (char *)&clientaddr;
                unitdata->opt.maxlen     = 0;
 
                if ((dcount=t_rcvudata(fd, &unitdata, &flags))[9]
                    < 0) {
                        /* if disconnected bid a goodbye */
                        if (t_errno == TLOOK) {
                                int tmp = t_look(fd);
 
                                if (tmp != T_DISCONNECT) {
                                        t_scope(tmp);
                                } else {
                                        exit(0);
                                }
                        }
                        xerror("transactions_receive", fd);
                        break;
                }
                if (unitdata->udata.len == 0) {
 
                        return(0);
                }
 
                datapipe[unitdata->udata.len] = '\0';
 
                if ((reply=parse(datapipe)) != NULL) {
 
                        /* sender's addr is in the unitdata */
                        unitdata->udata.len = strlen(reply);
                        unitdata->udata.buf = reply;
 
                        if (t_sndudata(fd, unitdata) < 0) {  [10]
                                xerror("xti_send", fd);
                                break;
                        }
                }
         }
         if (t_free((char *)unitdata, T_UNITDATA) < 0) {
              xerror("xti_free", fd);
              exit(3);
         }
}

  1. The t_open call specifies a device special file name, which is /dev/streams/xtiso/udp+ in this example. This file name provides the necessary abstraction for the UDP transport protocol over IP. Unlike the socket interface, where you specify the address family (for example, AF_INET), this information is already represented in the choice of the device special file. The /dev/streams/xtiso/udp+ file implies both UDP transport and Internet Protocol. See the Chapter 5 for information about STREAMS devices. Contrast the t_open call with the socket call in Section B.2.1. [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain or address family of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an application entity on the network. For TCP/IP and UDP/IP this is the Internet address of the server and the port number on which it is listening.

    The information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]

  3. AF_INET specifies the Internet communication domain or address family. [Return to example]

  4. INADDRANY signifies any attached interface adapter on the system. All numbers must be converted to the network format using appropriate macros. See the following reference pages for more information: htonl(3), htons(3), ntohl(3), and ntohs(3). [Return to example]

  5. SERVER_PORT is defined in the <common.h> header file. It is a short integer, which helps identify the server process from other application processes. Numbers from 0 to 124 are reserved. [Return to example]

  6. Specify the number of pending connections the server can queue while it processes the last request. [Return to example]

  7. Bind the server's address with the t_bind call. The combination of the address and port number identify it uniquely on the network. After the server process' address is bound, the server process is registered on the system and can be identified by the lower level kernel functions to which to direct any requests. [Return to example]

  8. Each incoming message packet is accepted and passed to the parse function, which tracks the information provided, such as the merchant's login ID, password, and customer's credit card number. This process is repeated until the parse function identifies a complete transaction and returns a response packet, to be sent to the client program.

    The client program can send information packets in any order (and in one or more packets), so the parse function is designed to remember state information sufficient to deal with this unstructured message stream.

    Since this program uses a connectionless (datagram) protocol, it uses t_sndudata and t_rcvudata to send and receive data, respectively. [Return to example]

  9. Receive data with the t_rcvudata function. [Return to example]

  10. Send data with the t_sndudata function. [Return to example]

B.2.4    XTI Client Program

Example B-8 implements an XTI client that can communicate with the XTI server in Example B-7. It uses the XTI interface in the connectionless, or datagram, mode.

Example B-8:  Connectionless XTI Client Program

/*
 *
 * This file contains the main XTI client code
 * for a connectionless mode of communication.
 *
 * usage: client [serverhostname]
 *
 */
#include "client.h"
 
main(int argc, char *argv[])
{
        int                     xtifd;
        struct sockaddr_in      serveraddr;
        struct hostent          *he;
        int                     n;
        char                    *serverhost = "localhost";
        struct hostent          *serverhostp;
        char                    buffer[MAXBUFSIZE+1];
        char                    inbuf[MAXBUFSIZE+1];
        struct t_unitdata       *unitdata;
        int                     flags = 0;
 
 
        if (argc>1) {
                serverhost = argv[1];
        }
 
        init();
 
 
        if ((xtifd = t_open("/dev/streams/xtiso/udp+",       [1]
                            O_RDWR, NULL)) < 0) {
                xerror("xti_open", xtifd);
                exit(1);
        }
 
 
        bzero((char *) &serveraddr,                          [2]
              sizeof(struct sockaddr_in));
        serveraddr.sin_family      = AF_INET;                [3]
 
        if ((serverhostp = gethostbyname(serverhost)) ==     [4]
            (struct hostent *)NULL) {
                fprintf(stderr,"gethostbyname on %s failed\n",
                        serverhost);
                exit(1);
        }
        bcopy(serverhostp->h_addr,
              (char *)&(serveraddr.sin_addr.s_addr),
              serverhostp->h_length);
        /*
         * SERVER_PORT is a short which identifies
         * the server process from other sources.
         *
         */
        serveraddr.sin_port        = htons(SERVER_PORT);     [5]
 
        if (t_bind(xtifd, (struct t_bind *)NULL,             [6]
                   (struct t_bind *)NULL) < 0) {
                xerror("bind", xtifd);
                exit(2);
        }
 
        /* Allocate structures for t_rcvudata and t_sndudata call */
        if ((unitdata=(struct t_unitdata *)t_alloc(xtifd,
                                                   T_UNITDATA,
                                                   T_ALL))
                == NULL) {
                xerror("xti_alloc", fd);
                exit(3);
        }
 
        while(1) {
                /* Merchant record */
                sprintf(buffer, "%%%%m%s##%%%%p%s##",
                        merchantname, password);
 
                printf("\n\nSwipe card, enter amount: ");
                fflush(stdout);
 
                if (scanf("%s", inbuf) == EOF) {
                        printf("bye...\n");
                        exit(0);
                }
 
                soundbytes();
 
                sprintf(buffer, "%s%%%%a%s##%%%%n%s##",
                        buffer, inbuf, swipecard());
 
                unitdata->addr.buf       = (char *)&serveraddr;
                unitdata->addr.len       = sizeof(serveraddr);
                unitdata->udata.buf      = buffer;
                unitdata->udata.len      = strlen(buffer);
                unitdata->opt.len        = 0;
 
                if (t_sndudata(xtifd, unitdata) < 0) {      [7]
                        xerror("t_snd", xtifd);
                        exit(1);
                }
 
                unitdata->udata.maxlen   = MAXBUFSIZE;
                unitdata->addr.maxlen    = sizeof(serveraddr);
 
                /* receive info */
                if ((t_rcvudata(xtifd, unitdata, &flags))   [8]
                    < 0) {
                        xerror("t_rcv", xtifd);
                        exit(1);
                }
 
                buffer[unitdata->udata.len] = '\0';
 
                if ((n=analyze(buffer))== 0) {
                        printf("transaction failure, "
                               "try again\n");
                } else if (n<0) {
                        printf("login failed, try again\n");
                        init();
                }
        }
         if (t_free((char *)unitdata, T_UNITDATA) < 0) {
              xerror("xti_free", fd);
              exit(3);
         }
}

  1. The t_open call specifies a device special file name; for example /dev/streams/xtiso/udp+. This file name provides the necessary abstraction for the UDP transport protocol over IP. Unlike the socket interface, where you specify the address family (for example, AF_INET), this information is already represented in the choice of the device special file. The /dev/streams/xtiso/udp+ file implies both UDP transport and Internet Protocol. See the Chapter 5 for information about STREAMS devices.

    Contrast the t_open call with the socket call in Section B.2.2. [Return to example]

  2. The serveraddr is of type sockaddr_in, which is dictated by the communication domain of the socket (AF_INET). The socket address for the Internet communication domain contains an Internet address and a 16-bit port number, which uniquely identifies an entity on the network. For the TCP/IP protocol suite, which includes UDP, this is the Internet address of the server and the port number on which it is listening.

    The information contained in the sockaddr_in structure is dependent on the address family (or the protocol). [Return to example]

  3. AF_INET specifies the Internet communication domain. If AF_OSI were supported it could be used to create a socket for OSI communications. [Return to example]

  4. Getting information about the server depends on the protocol or the address family. To get the IP address of the server, you can use the gethostbyname(3) routine. [Return to example]

  5. SERVER_PORT is defined in the <common.h> header file. It is a short integer, which helps identify the server process from other application processes. [Return to example]

  6. Bind the server address with the t_bind function to enable the client to start sending and receiving data. [Return to example]

  7. Send data with the t_sndudata function. [Return to example]

  8. Receive data with the t_rcvudata function. [Return to example]

B.3    Common Code

The following header and database files are required for all or several of the client and server portions of this application:

B.3.1    The common.h Header File

Example B-9 shows the <common.h> header file. It contains common header files and constants required by all sample programs.

Example B-9:  The common.h Header File

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>             [1]
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <fcntl.h>
#include <xti.h>
 
 
#define SEPARATOR       ','
#define PREAMBLE        "%%"
#define PREAMBLELEN     2       [2]
#define POSTAMBLE       "##"
#define POSTAMBLELEN    2
 
 
/* How to contact the server */
#define SERVER_PORT     1234    [3]
/* How to contact the client (for datagram only) */
#define CLIENT_PORT     1235
 
#define MAXBUFSIZE 4096

  1. List of header files to include. [Return to example]

  2. These statements define constants that allow more effective parsing of data exchanged between the server and client. [Return to example]

  3. SERVER_PORT is a well known port that is arbitrarily assigned by the programmer so that clients can communicate with the server. SERVER_PORT is used to identify the service to which you want to connect. Port numbers 0 through 1024 are reserved for the system. Programmers can choose a number, as long as it does not conflict with any other applications. While debugging, this number is chosen randomly (and by trial and error). For a well-distributed application, some policy must be used to avoid conflicts with other applications. [Return to example]

B.3.2    The server.h Header File

Example B-10 shows the <server.h> header file. It contains the data structures for accessing the server's database, as well as the data structures for analyzing and synthesizing messages to and from clients.

Example B-10:  The server.h Header File

#include "common.h"
 
struct merchant {
        char *name;
        char *passwd;
};
 
struct customer {
        char                    *cardnum;
        char                    *name;
        int                     limit;
        int                     balance;
        struct transaction      *tlist;
        /* presumably other data */
};
 
struct transaction {
        struct transaction      *nextcust;
        struct transaction      *nextglob;
        struct customer         *whose;
        char                    *merchantname;
        int                     amount;
        char                    *verification;
};
 
extern struct transaction *alltransactions;
extern struct merchant  merchant[];
extern int              merchantcount;
extern struct customer  customer[];
extern int              customercount;
 
#define INVALID (struct transaction *)1
 
#define MERCHANTAUTHERROR       "%%A##"
#define USERAUTHERROR           "%%U##"
#define USERAMOUNTERROR         "%%V##"
#define TRANSMITERROR           "deadbeef"
 
/* define transaction_status flags */
#define NAME                    0x01
#define PASS                    0x02
#define AMOUNT                  0x04
#define NUMBER                  0x08
 
#define AUTHMASK                0x03
#define VERIMASK                0x0C

B.3.3    The serverauth.c File

Example B-11 shows the serverauth.c file.

Example B-11:  The serverauth.c File

/*
 *
 * Authorization information (not related to the
 * networking interface)
 *
 */
 
#include "server.h"
 
 
/*
 * Currently a simple non-encrypted password method to search db
 *
 */
authorizemerchant(char *merch, char *password)
{
        struct merchant *mp;
 
        for(mp = merchant; (mp)->name != (char *)NULL; mp++) {
                if (!strcmp(merch, (mp)->name)) {
                        return (!strcmp(password, (mp)->passwd));
                }
        }
        return(0);
}
 
 
struct transaction *
verifycustomer(char *num, int amount, char *merchant)
{
        char buf[64];
        struct customer         *cp;
        struct transaction      *tp;
 
        for(cp = customer; (cp)->cardnum != NULL; cp++) {
                if (!strcmp(num, (cp)->cardnum)) {
                        if (amount <= (cp)->balance) {
                                (cp)->balance -= amount;
                                if ((tp = malloc(sizeof(
                                           struct transaction)))
                                    == NULL) {
                                        printf("Malloc error\n");
                                        return(NULL);
                                }
                                tp->merchantname = merchant;
                                tp->amount = amount;
                                sprintf(buf, "v%012d", time(0));
                                if ((tp->verification =
                                     malloc(strlen(buf)+1))
                                    == NULL) {
                                        printf("Malloc err\n");
                                        return(NULL);
                                }
                                strcpy(tp->verification, buf);
                                tp->nextcust = cp->tlist;
                                tp->whose = cp;
                                cp->tlist = tp;
                                tp->nextglob = alltransactions;
                                alltransactions = tp;
                                return(tp);
                        } else {
                                return(NULL);
                        }
                }
        }
        return(INVALID);
 
}
 
int                     transaction_status;
int                     authorized = 0;
int                     amount = 0;
char                    number[256];
char                    Merchant[256];
char                    password[256];
 
char *
parse(char *cp)
{
        char            *dp, *ep;
        unsigned char   type;
        int             doauth = 0;
        char            *buffer;
 
        dp = cp;
        if ((buffer=malloc(256)) == NULL) {
                return(TRANSMITERROR);
        }
        while (*dp) {
                /* terminate the string at the postamble */
                if (!(ep=strstr(dp, POSTAMBLE))) {
                        return(TRANSMITERROR);
                }
                *ep = '\0';
                ep = ep + POSTAMBLELEN;               [1]
 
                /* search for preamble */
                if (!(dp=strstr(dp, PREAMBLE))) {
                        return(TRANSMITERROR);
                }
                dp += PREAMBLELEN;
 
                /* Now get the token */
                type = *dp++;
 
                switch(type) {
                        case 'm':
                                strcpy(Merchant, dp);
                                transaction_status |= NAME;
                                break;
                        case 'p':
                                strcpy(password, dp);
                                transaction_status |= PASS;
                                break;
                        case 'n':
                                transaction_status |= NUMBER;
                                strcpy(number, dp);
                                break;
                        case 'a':
                                transaction_status |= AMOUNT;
                                amount = atoi(dp);
                                break;
                        default:
                                printf("Bad command\n");
                                return(TRANSMITERROR);
                }
                if ((transaction_status & AUTHMASK) == AUTHMASK) {
                        transaction_status &= ~AUTHMASK;
                        authorized = authorizemerchant(
                                      Merchant, password);
                        if (!authorized) {
                                printf("Merchant not"
                                       " authorized\n");
                                return(MERCHANTAUTHERROR);
                        }
                }
                /* If both amount and number gathered,
                 * do verification     */
                if ((authorized) &&
                    ((transaction_status&VERIMASK)
                     ==VERIMASK)) {
                        struct transaction *tp;
                        transaction_status &= ~VERIMASK;
                        /* send a verification back */
                        if ((tp=verifycustomer(number,
                                               amount,
                                               Merchant))
                            == NULL) {
                                return(USERAMOUNTERROR);
                        } else if (tp==INVALID) {
                                return(USERAUTHERROR);
                        } else {
                                sprintf(buffer,
                                   "%%%%%s##%%%%c%s##%%%%m%s##",
                                   tp->verification,
                                   tp->whose->name,
                                   tp->merchantname);
                                return(buffer);
                        }
                }
                dp = ep;
        }
        return(NULL);
}

  1. This function parses the incoming data, which includes the merchant authorization information, customer's credit card number, and the amount the customer is charging. Note that the function can not assume that all of the information is available in one message because the underlying TCP protocol is stream-oriented. This function can be simplified if a datagram type service is used or if a protocol that uses sequenced packets (SEQPACKET) is used. The function is designed to accept pieces of information in any order and in one or more message blocks. [Return to example]

B.3.4    The serverdb.c File

Example B-12 shows the serverdb.c file.

Example B-12:  The serverdb.c File

/*
 *
 * Database of valid merchants and credit card customers with the
 * credit limits, etc.
 *
 *
 */
#include "server.h"
 
struct merchant merchant[] = {
        {"abc",                 "abc"},
        {"magic",               "magic"},
        {"gasco",               "gasco"},
        {"furnitureco",         "abc"},
        {"groceryco",           "groceryco"},
        {"bakeryco",            "bakeryco"},
        {"restaurantco",        "restaurantco"},
        {NULL,                  NULL}
};
 
int merchantcount = sizeof(merchant)/sizeof(struct merchant)-1;
 
struct customer customer[] = {
        { "4322546789701000", "John Smith",     1000,   800 },
        { "4322546789701001", "Bill Stone",     2000,   200 },
        { "4322546789701002", "Dave Adams",     1500,   500 },
        { "4322546789701003", "Ray Jones",      1200,   800 },
        { "4322546789701004", "Tony Zachry",    1000,   100 },
        { "4322546789701005", "Danny Einstein", 5000,   50  },
        { "4322546789701006", "Steve Simonyi",  10000,  5800},
        { "4322546789701007", "Mary Ming",      1100,   700 },
        { "4322546789701008", "Joan Walters",   800,    780 },
        { "4322546789701009", "Gail Newton",    1000,   900 },
        { "4322546789701010", "Jon Robertson",  1000,   1000},
        { "4322546789701011", "Ellen Bloop",    1300,   800 },
        { "4322546789701012", "Sue Svelter",    1400,   347 },
        { "4322546789701013", "Suzette Ring",   1200,   657 },
        { "4322546789701014", "Daniel Mattis",  1600,   239 },
        { "4322546789701015", "Robert Esconis", 1800,   768 },
        { "4322546789701016", "Lisa Stiles",    1100,   974 },
        { "4322546789701017", "Bill Brophy",    1050,   800 },
        { "4322546789701018", "Linda Smitten",  4000,   200 },
        { "4322546789701019", "John Norton",    1400,   900 },
        { "4322546789701020", "Danielle Smith", 2000,   640 },
        { "4322546789701021", "Amy Olds",       1300,   100 },
        { "4322546789701022", "Steve Smith",    2000,   832 },
        { "4322546789701023", "Robert Smart",   3000,   879 },
        { "4322546789701024", "Jon Harris",     500,    146 },
        { "4322546789701025", "Adam Gershner",  1600,   111 },
        { "4322546789701026", "Mary Papadimis", 2000,   382 },
        { "4322546789701027", "Linda Jones",    1300,   578 },
        { "4322546789701028", "Lucy Barret",    1400,   865 },
        { "4322546789701029", "Marie Gilligan", 1000,   904 },
        { "4322546789701030", "Kim Coyne",      3000,   403 },
        { "4322546789701031", "Mike Storm",     7500,   5183},
        { "4322546789701032", "Cliff Clayden",  750,    430 },
        { "4322546789701033", "John Turing",    4000,   800 },
        { "4322546789701034", "Jane Joyce",     10000,  8765},
        { "4322546789701035", "Jim Roberts",    4000,   3247},
        { "4322546789701036", "Stevw Stephano", 1750,   894 },
        {NULL, NULL}
};
 
struct transaction *
alltransactions   = NULL;
int customercount = sizeof(customer)/sizeof(struct customer)-1;

B.3.5    The xtierror.c File

Example B-13 shows the xtierror.c file. It is used to generate a descriptive message in case of an error. Note that for asynchronous errors or events the t_look function is used to get more information.

Example B-13:  The xtierror.c File

#include <xti.h>
#include <stdio.h>
 
void t_scope();
 
void
xerror(char *marker, int fd)
{
        fprintf(stderr, "%s error [%d]\n", marker, t_errno);
        t_error("Transport Error");
        if (t_errno == TLOOK) {
                t_scope(t_look(fd));
        }
 
}
 
void
t_scope(int tlook)
{
        char *tmperr;
 
        switch(tlook) {
                case T_LISTEN:
                        tmperr = "connection indication";
                        break;
                case T_CONNECT:
                        tmperr = "connect confirmation";
                        break;
                case T_DATA:
                        tmperr = "normal data received";
                        break;
                case T_EXDATA:
                        tmperr = "expedited data";
                        break;
                case T_DISCONNECT:
                        tmperr = "disconnect received";
                        break;
                case T_UDERR:
                        tmperr = "datagram error";
                        break;
                case T_ORDREL:
                        tmperr = "orderly release indication";
                        break;
                case T_GODATA:
                        tmperr = "flow control restriction lifted";
                        break;
                case T_GOEXDATA:
                        tmperr = "flow control restriction "
                                 "on expedited data lifted";
                        break;
                default:
                        tmperr = "unknown event";
        }
                        fprintf(stderr,
                                "Asynchronous event: %s\n",
                                tmperr);
}

B.3.6    The client.h Header File

Example B-14 shows the client.h header file.

Example B-14:  The client.h File

#include "common.h"
 
extern char     merchantname[];
extern char     password[];

B.3.7    The clientauth.c File

Example B-15 shows the clientauth.c file. It contains the code that obtains the merchant's authorization, as well as the logic to analyze the message sent from the server. The resulting message is interpreted to see if the authorization was granted or rejected by the server.

Example B-15:  The clientauth.c File

#include "client.h"
 
init()
{
        printf("\nlogin: "); fflush(stdout);
        scanf("%s", merchantname);
 
        printf("Password: "); fflush(stdout);
        scanf("%s", password);
 
        srandom(time(0));
}
 
/* simulate some network activity via sound */
soundbytes()
{
        int i;
 
        for(i=0;i<11;i++) {
                printf();
                fflush(stdout);
                usleep(27000*(random()%10+1));
        }
}
 
analyze(char *cp)
{
 
        char *dp, *ep;
        unsigned char type;
        char customer[128];
        char verification[128];
 
        customer[0] = verification[0] = '\0';
 
        dp = cp;
 
        while ((dp!=NULL) && (*dp)) {
                /* terminate the string at the postamble */
                if (!(ep=strstr(dp, POSTAMBLE))) {
                        return(0);
                }
                *ep = '\0';
                ep = ep + POSTAMBLELEN;
 
                /* search for preamble */
                if (!(dp=strstr(dp, PREAMBLE))) {
                        return(0);
                }
                dp += PREAMBLELEN;
 
                /* Now get the token */
                type = *dp++;
 
                switch(type) {
                        case 'm':
                                if (strcmp(merchantname, dp)) {
                                        return(0);
                                }
                                break;
                        case 'c':
                                strcpy(customer, dp);
                                break;
                        case 'U':
                                printf("Authorization denied\n");
                                return(1);
                        case 'V':
                                printf("Amount exceeded\n");
                                return(1);
                        case 'A':
                                return(-1);
                        case 'v':
                                strcpy(verification, dp);
                                break;
                        default:
                                return(0);
                }
                dp = ep;
        }
        if (*customer && *verification) {
                printf("%s, verification ID: %s\n",
                       customer, verification);
                return(1);
        }
        return(0);
}

B.3.8    The clientdb.c File

Example B-16 shows the clientdb.c file. It contains a database of customer credit card numbers used to simulate the card swapping action. In a real world application, a magnetic reader reads the numbers through an appropriate interface. Also, the number cache is not required for a real world application.

Example B-16:  The clientdb.c File

/*
 *
 * Database of customer credit card numbers to simulate
 * the card swapping action. In practice the numbers
 * will be read by magnetic readers through an
 * appropriate interface.
 */
 
#include <time.h>
 
char    merchantname[256];
char    password[256];
 
 
char *numbercache[] = {
        "4322546789701000",
        "4322546789701001",
        "4322546789701002",
        "4222546789701002",             /* fake id */
        "4322546789701003",
        "4322546789701004",
        "4322546789701005",
        "4322546789701006",
        "4322546789701007",
        "4322546789701008",
        "4322546789701009",
        "4322546789701010",
        "4322546789701011",
        "4322546789701012",
        "4322546789701013",
        "4322546789701014",
        "4322546789701015",
        "4322546789701016",
        "4322546789701017",
        "4322546789701018",
        "4222546789701018",             /* fake id */
        "4322546789701019",
        "4322546789701020",
        "4322546789701021",
        "4322546789701022",
        "4322546789701023",
        "4322546789701024",
        "4322546789701025",
        "2322546789701025",             /* fake id */
        "4322546789701026",
        "4322546789701027",
        "4322546789701028",
        "4322546789701029",
        "4322546789701030",
        "4322546789701031",
        "4322546789701032",
        "4322546789701033",
        "4322546789701034",
        "4322546789701035",
        "4322546789701036",
};
 
#define CACHEENTRIES (sizeof(numbercache)/sizeof(char *))
 
 
char *
swipecard()
{
        return(numbercache[random()%CACHEENTRIES]);
}