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:
Error handling is not robust
Accepts only integer amounts
Performs no child process clean up
In the case of the connection-oriented protocol examples in Section B.1, for each request received, the server program forks a child process to handle the request. The database information is "detached" in the child process' private data area. When the child process analyzes the request and reduces the customer's credit balance appropriately, it needs to update this information in the original server's data area (and on some persistent storage as well) so that the next request for the same customer is handled correctly. To avoid unnecessary complexity, this logic is not included in the program.
The information is organized as follows:
Connection-oriented mode programs
Socket
Server
Client
XTI
Server
Client
Connectionless mode programs
Socket
Server
Client
XTI
Server
Client
Common files
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] } } }
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]
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]
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]
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]
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]
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]
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]
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]
Receive data with the
recv
call.
[Return to example]
Send data with the
send
call.
[Return to example]
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(); } } }
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]
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]
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]
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]
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]
Send data with the
send
call.
[Return to example]
Receive data with the
recv
call.
[Return to example]
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; } } } }
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]
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]
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]
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]
Specify the number of pending connections the server can queue while it processes the last request. [Return to example]
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]
Listen for connection requests with the
t_listen
function.
[Return to example]
Create a new transport endpoint with another
call to the
t_open
function.
[Return to example]
Accept the connection request with the
t_accept
function.
[Return to example]
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]
Receive data with the
t_rcv
function.
[Return to example]
Send data with the
t_snd
function.
[Return to example]
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); } }
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]
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]
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]
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]
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]
Bind the server address with the
t_bind
function to enable the client to start sending and receiving
data.
[Return to example]
Initiate a connection with the server using
the
t_connect
function.
[Return to example]
Send data with the
t_snd
function.
[Return to example]
Receive data with the
t_rcv
function.
[Return to example]
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"); } } } }
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]
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]
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]
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]
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]
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]
Receive data with the
recvfrom
call.
[Return to example]
Send data with the
sendto
call.
[Return to example]
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(); } } }
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]
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]
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]
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]
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]
Send data with the
sendto
call.
[Return to example]
Receive data with the
recvfrom
call.
[Return to example]
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); } }
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]
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]
AF_INET specifies the Internet communication domain or address family. [Return to example]
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]
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]
Specify the number of pending connections the server can queue while it processes the last request. [Return to example]
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]
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]
Receive data with the
t_rcvudata
function.
[Return to example]
Send data with the
t_sndudata
function.
[Return to example]
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); } }
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]
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]
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]
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]
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]
Bind the server address with the
t_bind
function to enable the client to start sending and receiving
data.
[Return to example]
Send data with the
t_sndudata
function.
[Return to example]
Receive data with the
t_rcvudata
function.
[Return to example]
The following header and database files are required for all or several of the client and server portions of this application:
<common.h>
<server.h>
serverauth.c
serverdb.c
xtierror.c
<client.h>
clientauth.c
clientdb.c
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
List of header files to include. [Return to example]
These statements define constants that allow more effective parsing of data exchanged between the server and client. [Return to example]
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]
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
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); }
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]
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;
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[];
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); }
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]); }