Socket Programming
Socket Programming
ERserver
iSeries
Socket programming
ERserver
iSeries
Socket programming
© Copyright International Business Machines Corporation 2001, 2002. All rights reserved.
US Government Users Restricted Rights – Use, duplication or disclosure restricted by GSA ADP Schedule Contract
with IBM Corp.
Contents
Part 1. Socket programming . . . . . . . . . . . . . . . . . . . . . . . . . . 1
| Chapter 8. Socket scenario: Create an application to accept IPv4 and IPv6 clients . . . . . . 81
| Example: Accept connections from both IPv6 and IPv4 clients . . . . . . . . . . . . . . . . 82
| Example: IPv4 or IPv6 client . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Socket programming shows how to use socket APIs to establish communication links between remote and
local processes. Programmers who use Integrated Language Environment (ILE) C can use the information
to develop socket applications. You can also code to the sockets API from other ILE languages, such as
RPG. For more information on ILE RPG, see the IBM Redbook Who Knew You Could Do That with RPG
IV? A Sorcerer’s Guide to System Access and More.
| Java also supports a socket programming interface. See the Java topic in the Information Center for
| details.
The following topics provide concepts, design recommendations, and examples to help you develop socket
applications. see:
| v What’s new for V5R2
| Use this page to learn about new functions that are related to socket programming. This topic provides
| links to other pages that provide more detailed information on these enhancements.
| v Print this topic
| Use this page to print or download a Portable Document Format (PDF) version of the Sockets
| programming information.
| v Prerequisites for sockets programming
This topic discusses the required tasks that must be completed before writing applications with socket
APIs.
v Basic socket design
This topic provides an overview for example programs for the most basic types of sockets. Use the links
to sample programs for examples that illustrate basic socket design strategies.
| v Socket concepts
| This topic describes more advanced socket concepts, such as Asynchronous input/output (I/O) and
| Global Secure Toolkit (GSKit). Use the links in this topic to review sample programs that are associated
| with these concepts.
| v Socket Scenario: Create an application to accept IPv4 and IPv6 clients
| This topic describes a typical situation in which you may want to use the AF_INET6 address family. New
| for V5R2, this address family provides support for Internet Protocol version 6 (IPv6). IPv6 supports
| 128–bit IP addresses. This topic also provides planning information and links to example programs that
| you can use to implement socket applications that use the AF_INET6 address family.
| v Socket application design recommendations
This topic provides hints to designing more effective socket applications.
v Examples: Socket application design
This topic provides example socket programs that you can use to create socket applications.
| IPv6 Support
| New for this release, programmers can now write applications that use IPv6 addresses, using the new
| AF_INET6 address family. In addition to the AF_INET6 address family, existing APIs have been changed
| to support the new address family, new APIs have been added, and new structures have been defined.
| v AF_INET6 address family
| This topic describes the new address family and its address structure.
| v Scenario: Create an application to accept IPv4 and IPv6 clients
| This topic describes a customer situation where a programmer plans to design and write applications
| that use the AF_INET6 address family to accept IPv6 connections. This topic contains sample programs
| that programmers can change to suit their own needs.
| OS/400 sockets provides compatibly support with X/Open Single UNIX Specification. OS/400 sockets is
| changing structures, type definitions, and function prototypes to match these specifications. Programmers
| can choose to use the default OS/400 sockets, which are based on Berkeley Software Distributions (BSD)
| 4.3 sockets or specify the _XOPEN_SOURCE macro to select the UNIX 98 compatible interface.
| v UNIX 98 compatibility
| This topic describes the differences and compatibility issues between UNIX 98 and base OS/400
| sockets.
| New for this release, several performance improvements have been included to speed up secure
| connections. Currently application programmers can access SSL support through three separate interfaces
| on the iSeries:
| 1. Global Secure Toolkit (GSKit) APIs
| 2. SSL_ APIs
| 3. Java SSL interface
| New for this release, Xsockets, an interactive tool shipped with QUSRTOOL, can now be accessed
| through a web-browser interface. New instructions have been added to show you how to set-up the tool to
| use a web browser. You can still work with the tool from the command line interface; however, new
| functions, such as GSKit can only be accessed through this new interface. For details on these changes,
| see the following topic:
| v Xsockets tool
| As of V5R2, IBM no longer supports IPX and SPX protocols. As a result, the AF_NS address family will be
| disabled in V5R2. When an application attempts to open an AF_NS (IPX/SPX) socket using the socket()
from http://www.adobe.com/prodindex/acrobat/readstep.html .
To view or download the PDF version, select Socket programming. (444KB and 132 pages)
| Compiler requirements
| 1. Install QSYSINC library. This library provides necessary header files that are needed when compiling
| socket applications.
| 2. Install the C Compiler licensed program (5722–CX2).
| Requirements for Secure Sockets Layer (SSL) and Global Secure Toolkit (GSKit) APIs
| In addition to compiler and AF_INET and AF_INET6 address requirements, you must complete the
| following tasks to work with secure sockets:
| 1. Install and configure Digital Certificate Manager licensed program (5722–SS1 Option 34). See Digital
| Certificate Management in the Information Center for details.
| 2. Install Cryptographic Access Provider licensed program (5722– AC3).
| 3. If you want to use SSL with the cryptographic hardware, you will need to install and configure either
| the 2058 Cryptographic Accelerator for iSeries or the 4758 PCI Cryptographic Coprocessor for iSeries.
| The 2058 Cryptographic Accelerator allows you to offload SSL cryptographic processing from the
| operating system to the card. See 2058 Cryptographic Accelerator for iSeries for a complete
| description of the 2058 Cryptographic Accelerator and its features. The 4758 Cryptographic
| Coprocessor can be used for SSL cryptographic processing; however, unlike the 2058, this card
| provides a more cryptographic functions, like encrypting and decrypting keys. See 4758 PCI
| Cryptographic Coprocessor for iSeries for features and configuration steps for this card.
| If you plan to design AF_TELEPHONY sockets, which uses phone lines, you must complete the following
| steps in addition to general requirements.
| 1. Plan IDSN service to determine your needs for an ISDN connection.
| 2. Configure IDSN environment based on your planning information.
A socket has a typical flow of events. In a connection-oriented client-to-server model, the socket on the
server process waits for requests from a client. To do this, the server first establishes (binds) an address
that clients can use to find the server. When the address is established, the server waits for clients to
request a service. The client-to-server data exchange takes place when a client connects to the server
through a socket. The server performs the client’s request and sends the reply back to the client.
| Note: Currently, IBM supports two versions of most sockets APIs. The default OS/400 sockets use
| Berkeley Socket Distribution (BSD) 4.3 structures and syntax. The differences between the base
| OS/400 sockets and BSD 4.3 are outlined in Berkeley Socket Distribution (BSD) compatibility. The
| other version of sockets uses syntax and structures compatible with BSD 4.4 and the UNIX 98
| programming interface specifications. Programmers can specify _XOPEN_SOURCE macro to use
| the UNIX98 compatible interface. See UNIX 98 compatibility for descriptions of these API and
| structural differences.
The following figure shows the typical flow of events (and the sequence of issued functions) for a
connection-oriented socket session. An explanation of each event follows the figure.
Note:
The socket APIs are located in the communications model between the application layer and the
transport layer. The socket APIs are not a layer in the communication model. Socket APIs allow
applications to interact with the transport or networking layers of the typical communications model.
The arrows in the following figure show the position of a socket, and the communication layer that
the socket provides.
Typically, a network configuration does not allow connections between a secure internal network and a
less secure external network. However, you can enable sockets to communicate with server programs that
run on a system outside a firewall (a very secure host).
| The Information Center allows you to access API reference information several ways. The topic Socket API
| provides an overview of socket functions and structures. If you want to search for specific API or to search
| for categories of APIs, the API information provides an interactive API finder to assist you.
Connectionless communication implies that no connection is established over which a dialog or data
transfer can take place. Instead, the server program designates a name that identifies where to reach it
(much like a post office box). If you send a letter to a post office box, you cannot be absolutely sure the
receiver got the letter. You may need to wait for a response to your letter. There is no active, real time
connection in which data is exchanged.
When an application creates a socket with the socket() function, it must identify the socket by specifying
these parameters:
v Socket address family determines the format of the address structure for the socket. This topic contains
examples of each address families’ address structure. See Socket address structures for a general
definition of structure of socket addresses.
v Socket type determines the desired form of communication for the socket.
v Socket-supported protocols determines the protocol that the socket uses.
These parameters or characteristics define the socket application and how it interoperates with other
socket applications. Depending on the address family of a socket, you may have different choices for the
socket type and protocol. The table below shows the corresponding address family and its associated
socket type and protocols:
| Table 1. Summary of socket characteristics
| Address family Socket type Socket protocol
| AF_UNIX SOCK_STREAM N/A
| SOCK_DGRAM N/A
| AF_INET SOCK_STREAM TCP
| SOCK_DGRAM UDP
| SOCK_RAW IP, ICMP
In addition to these socket characteristics or parameters, constant values are defined in network routines
and header files that are shipped with the QSYSINC library. For descriptions of header files, see the
individual APIs listed in the Socket APIs topic in the Information Center. Each API lists its appropriate
header file in the usage section of the API description.
Socket network routines allow socket applications to obtain information from the DNS, host, protocol,
service, and network files. For a description of these routines, see Socket network routines.
The address family parameter (address_family) on the socket() function specifies the address structure
that is used on the socket functions. The following topics describe each of these address families, their
use, their related protocol, and examples of relevant structure:
v AF_INET address family
| v AF_INET6 address family
v AF_UNIX address family
v AF_UNIX_CCSID address family
v AF_TELEPHONY address family
| For a socket application that uses the Internet Protocol version 4 (IPv4), the AF_INET address family uses
| the sockaddr_in address structure. When you use _XOPEN_SOURCE macro, the AF_INET address
| structure changes to be compatible with BSD 4.4/UNIX 98 specifications. For the sockaddr_in address
| structure, these differences are summarized in the table:
| Table 4. Differences between BSD 4.3 and BSD 4.4/UNIX 98 for sockaddr_in address structure
| BSD 4.3 sockaddr_in address structure BSD 4.4/UNIX 98 sockaddr_in address structure
See Use AF_INET address family for information on using AF_INET and a sample program that uses
AF_INET address family.
| For a socket application that uses TCP, UDP or RAW, the AF_INET6 address family uses the
| sockaddr_in6 address structure. This address structure changes if you use _XOPEN_SOURCE macro to
| implement BSD 4.4/UNIX 98 specifications. For the sockaddr_in6 address structure, these differences are
| summarized in this table:
| Table 6. Differences between BSD 4.3 and BSD 4.4/UNIX 98 for sockaddr_in6 address structure
| BSD 4.3 sockaddr_in6 address structure BSD 4.4/UNIX 98 sockaddr_in6 address structure
| Sockets with the address family AF_UNIX use the sockaddr_un address structure. This address structure
| changes if you use _XOPEN_SOURCE macro to implement BSD 4.4/UNIX 98 specifications. For the
| sockaddr_un address structure, these differences are summarized in the table:
| Table 8. Differences between BSD 4.3 and BSD 4.4/UNIX 98 for sockaddr_un address structure
| BSD 4.3 sockaddr_un address structure BSD 4.4/UNIX 98 sockaddr_un address structure
See Use AF_UNIX address family for information on using AF_UNIX and provides a sample programs that
use this address family.
However, because an AF_UNIX socket may return the path name from an AF_UNIX_CCSID socket in an
AF_UNIX address structure, path size is limited. AF_UNIX only supports 126 characters so
AF_UNIX_CCSID will also be limited to 126 characters.
A user may not exchange AF_UNIX and AF_UNIX_CCSID addresses on a single socket. When
AF_UNIX_CCSID is specified on the socket() call, all addresses must be sockaddr_unc on later API
calls.
struct sockaddr_unc {
short sunc_family;
short sunc_format;
char sunc_zero[12];
Qlg_Path_Name_T sunc_qlg;
union {
char unix[126];
wchar_t wide[126];
char* p_unix;
wchar_t* p_wide;
} sunc_path;
} ;
For more information on AF_UNIX_CCSID sockets, see Use AF_UNIX_CCSID address family, which
provides a sample program.
The system supports AF_TELEPHONY sockets only as a connection-oriented sockets, which have the
socket type, SOCK_STREAM. A connection in a telephony domain socket provides no more reliability than
the underlying telephone connection. If you want guaranteed delivery, you must work with the applications
that provide these services, such as the fax applications that use this address family.
Sockets with AF_TELEPHONY address family use the sockaddr_tel address structure:
struct sockaddr_tel {
short stel_family;
struct tel_addr stel_addr;
char stel_zero[4];
};
The telephony address consists of a 2–byte length followed by a telephone number of up to 40 digits
(0–9).
struct tel_addr {
unsigned short t_len
char t_addr[40];
};
Table 11. AF_TELEPHONY address structure
Address structure field Definition
stel_family This field contains the address family.
stel_addr This field contains the telephony address.
stel_zero This field is a reserved field.
For more information on AF_TELEPHONY address family, see Use AF_TELEPHONY address family,
which provides steps on configuring the environment for using AF_TELEPHONY address family.
Socket type
The second parameter on a socket call determines the type of socket. Socket type provides identification
of the type and characteristics of the connection that will be enabled for transportation of the data from
one machine to another or from one process to another. The following list describes the socket types that
iSeries supports :
Stream (SOCK_STREAM)
This type of socket is connection-oriented. Establish an end-to-end connection by using the bind(),
listen(), accept(), and connect() functions. SOCK_STREAM sends data without errors or duplication, and
receives the data in the sending order. SOCK_STREAM builds flow control to avoid data overruns. It does
not impose record boundaries on the data. SOCK_STREAM considers the data to be a stream of bytes. In
the iSeries implementation, you can use stream sockets over Transmission Control Protocol (TCP),
Systems Network Architecture (SNA), AF_UNIX, AF_UNIX_CCSID, and AF TELEPHONY sockets. You can
also use stream sockets to communicate with systems outside a secure host (firewall).
| Datagram (SOCK_DGRAM)
| In Internet Protocol terminology, the basic unit of data transfer is a datagram. This is basically a header
| followed by some data. The datagram socket is connectionless. It establishes no end-to-end connection
| Raw (SOCK_RAW)
| This type of socket allows direct access to lower-layer protocols, such as Internet Protocol (IPv4 or IPv6)
| and Internet Control Message Protocol (ICMP or ICMP6). SOCK_RAW requires more programming
| expertise because you manage the protocol header information that is used by the transport provider. At
| this level, the transport provider may dictate the format of the data and the semantics that are
| transport-provider specific.
Socket protocols
Protocols provide the network transportation of an application’s data from one machine to another (or from
one process to another within the same machine). The application specifies the transport provider on the
protocol parameter of the socket() function.
For the AF_INET address family, more than one transport provider is allowed. The protocols of SNA,
TCP/IP, or UDP/IP can be active on the same socket at the same time. The ALWANYNET (Allow ANYNET
support) network attribute allows a customer to select whether a transport other than TCP/IP can be used
for AF_INET socket applications. This network attribute can be either *YES or *NO. The default value is
*NO.
For example, if the current status (the default status) is *NO, the use of AF_INET over an SNA transport is
not active. If AF_INET sockets are to be used over a TCP/IP transport only, the ALWANYNET status
should be set to *NO to improve CPU utilization.
Note: The ALWANYNET network attribute also affects APPC over TCP/IP.
For information on APPC configuration options, see Configuring APPC, APPN, and HPR.
AF_INET sockets over TCP/IP can also specify a type of SOCK_RAW, which means that the socket
communicates directly with the network layer known as Internet Protocol (IP). TCP or UDP transport
providers normally communicate with this layer. When you use SOCK_RAW sockets, the application
program specifies any protocol between 0 and 255 (except the TCP and UDP protocols). This protocol
number then flows in the IP headers when machines are communicating on the network. In effect, the
application program is the transport provider, because it must provide for all the transport services that
UDP or TCP transports normally provide.
For the AF_UNIX, AF_UNIX_CCSID, and AF_TELEPHONY address families, a protocol specification is not
really meaningful because there are no protocol standards involved. The communications mechanism
between two processes on the same machine is specific to the machine.
|
| Socket flow of events: Connection-oriented server
| The following sequence of the socket calls provide a description of the graphic. It also describes the
| relationship between the server and client application in a connection-oriented design. Each set of flows
| contain links to usage notes on specific APIs. If you need more details on the use of a particular API, you
| can use these links. The Example: A connection-oriented server uses the following sequence of function
| calls:
| 1. The socket() function returns a socket descriptor representing an endpoint. The statement also
| identifies that the INET (Internet Protocol) address family with the TCP transport (SOCK_STREAM) will
| be used for this socket.
| 2. The setsockopt() function allows the local address to be reused when the server is restarted before
| the required wait time expires.
| 3. After the socket descriptor is created, the bind() function gets a unique name for the socket. In this
| example, the user sets the s_addr to zero, which allows connections to be established from any IPv4
| client that specifies port 3005.
The following figure illustrates the client/server relationship from the socket APIs used in the code
examples for a connectionless socket design.
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/**************************************************************************/
/* Constants used by this program */
/**************************************************************************/
#define SERVER_PORT 3555
#define BUFFER_LENGTH 100
#define FALSE 0
void main()
{
/***********************************************************************/
/* Variable and structure definitions. */
/***********************************************************************/
int sd=-1, rc;
char buffer[BUFFER_LENGTH];
struct sockaddr_in serveraddr;
struct sockaddr_in clientaddr;
int clientaddrlen = sizeof(clientaddr);
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of each of the socket descriptors is only done once at the */
/* very end of the program. */
/***********************************************************************/
do
{
/********************************************************************/
/********************************************************************/
/* After the socket descriptor is created, a bind() function gets a */
/* unique name for the socket. In this example, the user sets the */
/* s_addr to zero, which means that the UDP port of 3555 will be */
/* bound to all IP addresses on the system. */
/********************************************************************/
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVER_PORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
/********************************************************************/
/* The server uses the recvfrom() function to receive that data. */
/* The recvfrom() function waits indefinitely for data to arrive. */
/********************************************************************/
rc = recvfrom(sd, buffer, sizeof(buffer), 0,
(struct sockaddr *)&clientaddr,
&clientaddrlen);
if (rc < 0)
{
perror("recvfrom() failed");
break;
}
/********************************************************************/
/* Echo the data back to the client */
/********************************************************************/
rc = sendto(sd, buffer, sizeof(buffer), 0,
(struct sockaddr *)&clientaddr,
sizeof(clientaddr));
if (rc < 0)
{
perror("sendto() failed");
break;
}
/********************************************************************/
/* Program complete */
/********************************************************************/
} while (FALSE);
/***********************************************************************/
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
/**************************************************************************/
/* Constants used by this program */
/**************************************************************************/
#define SERVER_PORT 3555
#define BUFFER_LENGTH 100
#define FALSE 0
#define SERVER_NAME "ServerHostName"
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of the socket descriptor is only done once at the very end */
/* of the program. */
/***********************************************************************/
do
{
/********************************************************************/
/* The socket() function returns a socket descriptor representing */
/* an endpoint. The statement also identifies that the INET */
/* (Internet Protocol) address family with the UDP transport */
/* (SOCK_STREAM) will be used for this socket. */
/********************************************************************/
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0)
{
perror("socket() failed");
/********************************************************************/
/* If an argument was passed in, use this as the server, otherwise */
/* use the #define that is located at the top of this program. */
/********************************************************************/
if (argc > 1)
strcpy(server, argv[1]);
else
strcpy(server, SERVER_NAME);
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVER_PORT);
serveraddr.sin_addr.s_addr = inet_addr(server);
if (serveraddr.sin_addr.s_addr == (unsigned long)INADDR_NONE)
{
/*****************************************************************/
/* The server string that was passed into the inet_addr() */
/* function was not a dotted decimal IP address. It must */
/* therefore be the hostname of the server. Use the */
/* gethostbyname() function to retrieve the IP address of the */
/* server. */
/*****************************************************************/
hostp = gethostbyname(server);
if (hostp == (struct hostent *)NULL)
{
printf("Host not found --> ");
printf("h_errno = %d\n", h_errno);
break;
}
memcpy(&serveraddr.sin_addr,
hostp->h_addr,
sizeof(serveraddr.sin_addr));
}
/********************************************************************/
/* Initialize the data block that is going to be sent to the server */
/********************************************************************/
memset(buffer, 0, sizeof(buffer));
strcpy(buffer, "A CLIENT REQUEST");
/********************************************************************/
/* Use the sendto() function to send the data to the server. */
/********************************************************************/
rc = sendto(sd, buffer, sizeof(buffer), 0,
(struct sockaddr *)&serveraddr,
sizeof(serveraddr));
if (rc < 0)
{
perror("sendto() failed");
break;
}
/********************************************************************/
/* Use the recvfrom() function to receive the data back from the */
/* server. */
/********************************************************************/
rc = recvfrom(sd, buffer, sizeof(buffer), 0,
(struct sockaddr *)&serveraddr,
& serveraddrlen);
if (rc < 0)
{
perror("recvfrom() failed");
break;
/********************************************************************/
/* Program complete */
/********************************************************************/
} while (FALSE);
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
}
See Prerequisites for socket programming for details on setting up an environment to use the AF_INET
address family.
For sample programs that use AF_INET address family, see Example: A connection-oriented server and
Example: A connection-oriented client.
| Like AF_INET sockets, AF_INET6 sockets can be either connection-oriented (type SOCK_STREAM) or
| they can be connectionless (type SOCK_DGRAM). Connection-oriented AF_INET6 sockets use TCP as
| the transport protocol. Connectionless AF_INET6 sockets use UDP as the transport protocol. When you
| create an AF_INET6 domain socket, you specify AF_INET6 for the address family in the socket program.
| AF_INET6 sockets can also use a type of SOCK_RAW. If this type is set, the application connects directly
| to the IP layer and does not use either the TCP or UDP transports. See Prerequisites for socket
| programming for details on setting up an environment to use the AF_INET6 address family.
| These addresses can be generated automatically by the getaddrinfo() function, when the specified host
| has only IPv4 addresses.
| You can create applications that use AF_INET6 sockets to open TCP connections to IPv4 nodes. To
| accomplish this task, you can encode the destination’s IPv4 address as an IPv4–mapped IPv6 address
| and pass that addresswithin a sockaddr_in6 structure in the connect() or sendto() call. When applications
| use AF_INET6 sockets to accept TCP connections from IPv4 nodes, or receive UDP packets from IPv4
| nodes, the system returns the peer’s address to the application in the accept(), recvfrom(), or
| getpeername() calls using a sockaddr_in6 structure encoded this way.
| While the bind() function allows applications to select the source IP address of UDP packets and TCP
| connections, applications often want the system to select the source address for them. Applications use
| in6addr_any similarly to the way they use the INADDR_ANY macro in IPv4 for this purpose. An additional
| feature of binding in this way is that it allows an AF_INET6 socket to communicate with both IPv4 and
| IPv6 nodes. For example, an application issuing an accept() on a listening socket bound to in6addr_any
| will accept connections from either IPv4 or IPv6 nodes. This behavior can be modified through the use of
| the IPPROTO_IPV6 level socket option IPV6_V6ONLY. Few applications will likely need to know which
| type of node with which they are interoperating. However, for those applications that do need to know, the
| IN6_IS_ADDR_V4MAPPED() macro defined in <netinet/in.h> is provided.
| If you would like a more detailed comparison of IPv4 and IPv6, see Compare IPv4 to IPv6 in the
| Information Center. This information compares the features between these two protocols.
| For examples programs and description of a situation where AF_INET6 socket communicates to both IPv4
| and IPv6 nodes, see Create an application to accept IPv4 and IPv6 clients.
| IPv6 Restrictions
| Currently, OS/400 support of IPv6 is limited in some functionality in V5R2. The following table lists these
| restrictions and their implications to socket programmers.
| Table 12. IPv6 restrictions and implications
| Restriction Implication
| IPv6 does not support fragmentation. AF_INET6 (SOCK_DGRAM) should not attempt to send
| datagrams larger than the MTU of the interface minus the
| size of the headers.
| IPv6 anycast is not supported. You cannot connect to or send to anycast addresses.
| IPv6 multicast is not supported. You cannot send or receive multicast datagrams.
| iSeries host table does not support IPv6 addresses. The getaddrinfo() and getnameinfo() APIs will not be
| able to locate IPv6 addresses in the host table. These
| APIs will find addresses in the DNS only.
| The gethostbyname() and gethostbyaddr() APIs only Use getaddrinfo() and getnameinfo() APIs if IPv6
| support IPv4 address resolution. address resolution is required.
| UNIX domain datagram sockets act differently than UDP datagram sockets. With UDP datagram sockets,
| the client program does not have to call the bind() function because the system assigns an unused port
| number automatically. The server can then send a datagram back to that port number. However, with
| UNIX domain datagram sockets, the system does not automatically assign a path name for the client.
| Thus, all client programs using UNIX domain datagrams must call the bind() function. The exact path
| name specified on the client’s bind() is what is passed to the server. Thus, if the client specifies a relative
| path name (that is, a path name that is not fully qualified by starting with /), the server cannot send the
| client a datagram unless it is running with the same current directory.
| An example path name that an application might use for this address family is /tmp/myserver or
| servers/thatserver. With servers/thatserver, you have a path name that is not fully qualified (no / was
| specified). This means that the location of the entry in the file system hierarchy should be determined
| relative to the current working directory.
| The following figure illustrates the client/server relationship of the AF_UNIX address family. See
| Prerequisites for socket programming for details on setting up an environment to use the AF_UNIX
| address family.
| Socket flow of events: Client application that uses AF_UNIX address family
| The Example: Client application that uses AF_UNIX address family uses the following sequence of
| function calls:
| 1. The socket() function returns a socket descriptor representing an endpoint. The statement also
| identifies that the UNIX address family with the stream transport (SOCK_STREAM) will be used for this
| socket. The function returns a socket descriptor representing an endpoint. You can also use the
| socketpair() function to initialize a UNIX socket.
| AF_UNIX or AF_UNIX_CCSID are the only address families to support the socketpair() function. The
| socketpair() function returns two socket descriptors that are unnamed and connected.
| 2. After the socket descriptor is received, the connect() function is used to establish a connection to the
| server.
| 3. The send() function sends 250 bytes of data specified which is specified in the server application with
| the SO_RCVLOWAT socket option.
| 4. The recv() function loops until all 250 bytes of the data until all 250 bytes have arrived.
| 5. The close() function closes any open socket descriptors.
Before working with AF_UNIX_CCSID socket application, you should be familiar with the
Qlg_Path_Name_T structure to determine the output format. See Path name structures in the API
Reference information in the Information Center for details.
When working with an output address structure, such as one returned from accept(), getsockname(),
getpeername(), recvfrom(), and recvmsg(), the application must examine the socket address structure
(sockaddr_unc) to determine its format. The sunc_format and sunc_qlg fields will determine the output
format of the path name. But sockets will not necessarily use the same values on output as the application
used on input addresses.
Socket flow of events: Server application that uses AF_UNIX_CCSID address family
The Example: Server application that uses AF_UNIX_CCSID address family uses the following sequence
of function calls:
Socket flow of events: Client application that uses AF_UNIX_CCSID address family
The Example: Client application that uses AF_UNIX_CCSID address family uses the following sequence of
function calls:
1. The socket() function returns a socket descriptor representing an endpoint. The statement also
identifies that the UNIX address family with the stream transport (SOCK_STREAM) will be used for this
socket. The function returns a socket descriptor representing an endpoint. You can also use the
socketpair() function to initialize a UNIX socket.
AF_UNIX or AF_UNIX_CCSID are the only address families to support the socketpair() function. The
socketpair() function returns two socket descriptors that are unnamed and connected.
2. After the socket descriptor is received, the connect() function is used to establish a connection to the
server.
3. The send() function sends 250 bytes of data specified which is specified in the server application with
the SO_RCVLOWAT socket option.
4. The recv() function loops until all 250 bytes of the data until all 250 bytes have arrived.
5. The close() function closes any open socket descriptors.
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/unc.h>
void main()
{
/***********************************************************************/
/* Variable and structure definitions. */
/***********************************************************************/
int sd=-1, sd2=-1;
int rc, length;
char buffer[BUFFER_LENGTH];
struct sockaddr_unc serveraddr;
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of each of the socket descriptors is only done once at the */
/* very end of the program. */
/***********************************************************************/
do
{
/********************************************************************/
/* The socket() function returns a socket descriptor representing */
/* an endpoint. The statement also identifies that the UNIX_CCSID */
/* address family with the stream transport (SOCK_STREAM) will be */
/* used for this socket. */
/********************************************************************/
sd = socket(AF_UNIX_CCSID, SOCK_STREAM, 0);
if (sd < 0)
{
perror("socket() failed");
break;
}
/********************************************************************/
/* After the socket descriptor is created, a bind() function gets a */
/* unique name for the socket. */
/********************************************************************/
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sunc_family = AF_UNIX_CCSID;
serveraddr.sunc_format = SO_UNC_USE_QLG;
serveraddr.sunc_qlg.CCSID = 500;
serveraddr.sunc_qlg.Path_Type = QLG_PTR_SINGLE;
serveraddr.sunc_qlg.Path_Length = strlen(SERVER_PATH);
serveraddr.sunc_path.p_unix = SERVER_PATH;
/********************************************************************/
/* The listen() function allows the server to accept incoming */
/* client connections. In this example, the backlog is set to 10. */
/* This means that the system will queue 10 incoming connection */
/* requests before the system starts rejecting the incoming */
/* requests. */
/********************************************************************/
rc = listen(sd, 10);
if (rc< 0)
{
/********************************************************************/
/* The server uses the accept() function to accept an incoming */
/* connection request. The accept() call will block indefinitely */
/* waiting for the incoming connection to arrive. */
/********************************************************************/
sd2 = accept(sd, NULL, NULL);
if (sd2 < 0)
{
perror("accept() failed");
break;
}
/********************************************************************/
/* In this example we know that the client will send 250 bytes of */
/* data over. Knowing this, we can use the SO_RCVLOWAT socket */
/* option and specify that we don’t want our recv() to wake up */
/* until all 250 bytes of data have arrived. */
/********************************************************************/
length = BUFFER_LENGTH;
rc = setsockopt(sd2, SOL_SOCKET, SO_RCVLOWAT,
(char *)&length, sizeof(length));
if (rc < 0)
{
perror("setsockopt(SO_RCVLOWAT) failed");
break;
}
/********************************************************************/
/* Receive that 250 bytes data from the client */
/********************************************************************/
rc = recv(sd2, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("recv() failed");
break;
}
/********************************************************************/
/* Echo the data back to the client */
/********************************************************************/
rc = send(sd2, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("send() failed");
break;
}
/********************************************************************/
/* Program complete */
/********************************************************************/
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
if (sd2 != -1)
close(sd2);
/***********************************************************************/
/* Remove the UNIX path name from the file system */
/***********************************************************************/
unlink(SERVER_PATH);
}
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <string.h>
#include <wcstr.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/unc.h>
/**************************************************************************/
/* Constants used by this program */
/**************************************************************************/
#define SERVER_PATH "/tmp/server"
#define BUFFER_LENGTH 250
#define FALSE 0
/***********************************************************************/
/* A do/while(FALSE) loop is used to make error cleanup easier. The */
/* close() of the socket descriptor is only done once at the very end */
/* of the program. */
/***********************************************************************/
do
{
/********************************************************************/
/* The socket() function returns a socket descriptor representing */
/* an endpoint. The statement also identifies that the UNIX_CCSID */
/********************************************************************/
/* If an argument was passed in, use this as the server, otherwise */
/* use the #define that is located at the top of this program. */
/********************************************************************/
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sunc_family = AF_UNIX_CCSID;
if (argc > 1)
{
/* The argument is a UNICODE path name. Use the default format */
serveraddr.sunc_format = SO_UNC_DEFAULT;
wcscpy(serveraddr.sunc_path.wide, (wchar_t *) argv[1]);
}
else
{
/* The local #define is CCSID 500. Set the Qlg_Path_Name to use */
/* the character format */
serveraddr.sunc_format = SO_UNC_USE_QLG;
serveraddr.sunc_qlg.CCSID = 500;
serveraddr.sunc_qlg.Path_Type = QLG_CHAR_SINGLE;
serveraddr.sunc_qlg.Path_Length = strlen(SERVER_PATH);
strcpy((char *)&serveraddr.sunc_path, SERVER_PATH);
}
/********************************************************************/
/* Use the connect() function to establish a connection to the */
/* server. */
/********************************************************************/
rc = connect(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (rc < 0)
{
perror("connect() failed");
break;
}
/********************************************************************/
/* Send 250 bytes of a’s to the server */
/********************************************************************/
memset(buffer, ’a’, sizeof(buffer));
rc = send(sd, buffer, sizeof(buffer), 0);
if (rc < 0)
{
perror("send() failed");
break;
}
/********************************************************************/
/* In this example we know that the server is going to respond with */
/* the same 250 bytes that we just sent. Since we know that 250 */
/* bytes are going to be sent back to us, we could use the */
/* SO_RCVLOWAT socket option and then issue a single recv() and */
/* retrieve all of the data. */
/* */
/* The use of SO_RCVLOWAT is already illustrated in the server */
/* side of this example, so we will do something different here. */
/* The 250 bytes of the data may arrive in separate packets, */
/* therefore we will issue recv() over and over again until all */
/* 250 bytes have arrived. */
/********************************************************************/
/*****************************************************************/
/* Increment the number of bytes that have been received so far */
/*****************************************************************/
bytesReceived += rc;
}
} while (FALSE);
/***********************************************************************/
/* Close down any open socket descriptors */
/***********************************************************************/
if (sd != -1)
close(sd);
}
AF_TELEPHONY sockets must be associated with a network telephony device (logically, a telephone)
before a connection can be initiated or completed. Use the special ioctl() command, SIOCSTELRSC (set
telephony resources), to make this association. You must configure these devices and make them ready
for use before issuing this command.
Before making an SIOCSTELRSC ioctl() call, the application must resolve the device name. The device
name must be resolved to a system pointer, and you must use this pointer as input for the SIOCSTELRSC
command.
The device will remain associated with the socket until the socket is closed. Finally, you can associate
more than one device with a socket. This multiple association permits an application to listen for and
answer calls on more than one device through a single socket.
| Socket flow of events: Server application that uses AF_TELEPHONY address family
| The Example: Accept an AF_TELEPHONY connection uses the following sequence of function calls:
| 1. The socket() function returns a socket descriptor representing an endpoint.
| 2. You must associate the socket with a device by resolving the device name to a system pointer and
| filling the structure for the request. Issue the ioctl() function to associate the device and the system
| pointer.
| 3. After the socket descriptor is received, the connect() function is used to establish a connection to the
| server.
| 4. The listen() allows the server to accept incoming client connections.
int main() {
/******************************************************************/
/* Miscellaneous declares */
/******************************************************************/
int xSock, xRC, xLength;
/******************************************************************/
/* Resolve device name to system pointer */
/******************************************************************/
_SYSPTR pDev; /* System pointer to device */
_RSLV_Template_T xTemp; /* Template for resolve */
char pName[]="FRED "; /* Device name */
struct TelResource xResource; /* SIOCSTELRSC structure */
/******************************************************************/
/* Socket address structure */
/******************************************************************/
struct sockaddr_tel xAddr;
/******************************************************************/
/* Buffers */
/******************************************************************/
char pSendBuffer[1024];
char pRecvBuffer[1024];
/******************************************************************/
/* Open a socket */
/******************************************************************/
xSock = socket(AF_TELEPHONY,SOCK_STREAM,0);
if (xSock<0) {
perror("socket() failed");
return(-1);
}
/******************************************************************/
/* Associate the socket with a device */
/* ...resolve the device name to a system pointer */
/* ...fill in the structure for this request */
/* ...issue the ioctl to perform the association */
/******************************************************************/
memset(&xTemp,0x00,sizeof(xTemp));
memcpy(xTemp.Obj.Name, pName, 30);
memset(&xResource,0x00,sizeof(xResource));
xResource.trCount=1;
xResource.trResourceList=&pDev;
xRC=ioctl(xSock,SIOCSTELRSC,&xResource);
if (xRC<0) {
perror("ioctl() failed");
close(xSock);
return(-1);
}
/******************************************************************/
/* Connect to a remote resource (dial a call) */
/******************************************************************/
memset(&xAddr,0x00,sizeof(xAddr));
xAddr.stel_family=AF_TELEPHONY;
xAddr.stel_addr.t_len=11;
memcpy(xAddr.stel_addr.t_addr,"18005551212",11);
xRC=connect(xSock,(struct sockaddr*)&xAddr,sizeof(xAddr));
if (xRC<0) {
perror("connect() failed");
close(xSock);
return(-1);
}
/******************************************************************/
/* Send the contents of the send buffer */
/******************************************************************/
xRC=send(xSock,pSendBuffer,1024,0);
if (xRC<0) {
perror("send() failed");
close(xSock);
return(-1);
}
/******************************************************************/
/* Receive a reply */
/******************************************************************/
xRC=recv(xSock,pRecvBuffer,1024,0);
if (xRC<0) {
perror("recv() failed");
close(xSock);
return(-1);
}
/******************************************************************/
/* All done, close and return */
/******************************************************************/
close(xSock);
return(0);
}
int main() {
/*********************************************************************/
/* Micellaneous declares */
/*********************************************************************/
int xSock,xNewSock,xRC,xLength;
/*****************************************************************/
/* Resolve device name to system pointer data areas */
/*****************************************************************/
_SYSPTR pDev; /* System pointer to device */
_RSLV_Template_T xTemp; /* Template for resolve */
char pName[]="GEORGE "; /* Device name */
struct TelResource xResource; /* SIOCSTELRSC structure */
/*****************************************************************/
/* Socket address structure */
/*****************************************************************/
struct sockaddr_tel xAddr;
/*****************************************************************/
/* Buffers */
/*****************************************************************/
char pSendBuffer[1024];
char pRecvBuffer[1024];
/******************************************************************/
/* Open a socket */
/******************************************************************/
xSock = socket(AF_TELEPHONY,SOCK_STREAM,0);
if (xSock<0) {
perror("socket() failed");
return(-1);
}
/*****************************************************************/
/* ...first, resolve the device name to a system pointer */
/* ...next, fill in the structure for this request */
/* ...finally, issue the ioctl to perform the association */
/*****************************************************************/
memset(&xTemp,0x00,sizeof(xTemp));
memcpy(xTemp.Obj.Name, pName, 30);
xTemp.Obj.Type_Subtype = WLI_DEVD;
xTemp.Auth = _AUTH_NONE;
_RSLVSP2(&pDev,&xTemp);
memset(&xResource,0x00,sizeof(xResource));
xResource.trCount=1;
xResource.trResourceList=&pDev;
xRC=ioctl(xSock,SIOCSTELRSC,&xResource);
if (xRC<0) {
perror("ioctl() failed");
close(xSock);
return(-1);
}
/*****************************************************************/
/* Bind to a local number (using TELADDR_ANY means to accept */
/*****************************************************************/
/* Listen for incoming calls */
/*****************************************************************/
xRC=listen(xSock,5);
if (xRC<0) {
perror("listen() failed");
close(xSock);
return(-1);
}
/*****************************************************************/
/* Accept an incoming call */
/*****************************************************************/
memset(&xAddr,0x00,sizeof(xAddr));
xLength = sizeof(xAddr);
xNewSock=accept(xSock,(struct sockaddr*)&xAddr,&xLength);
if (xNewSock<0) {
perror("accept() failed");
close(xSock);
return(-1);
}
/*****************************************************************/
/* Receive some data */
/*****************************************************************/
xRC=recv(xNewSock,pRecvBuffer,1024,0);
if (xRC<0) {
perror("recv() failed");
close(xSock);
close(xNewSock);
return(-1);
}
/*****************************************************************/
/* Send a reply */
/*****************************************************************/
xRC=send(xNewSock,pSendBuffer,1024,0);
if (xRC<0) {
perror("send() failed");
close(xSock);
close(xNewSock);
return(-1);
}
/*****************************************************************/
/* All done, close both sockets and return */
/*****************************************************************/
close(xSock);
close(xNewSock);
return(0);
}
Asynchronous I/O
Asynchronous I/O APIs provide a method for threaded client server models to perform highly concurrent
and memory efficient I/O. In previous threaded client/server models, typically two I/O models have
prevailed. The first model dedicated one thread per client connection. The first model consumes too many
threads and could incur a substantial sleep and wake up cost. The second model minimizes the number of
threads by issuing the select() API on a large set of client connections and delegating a readied client
connection or request to a thread. In the second model, you must select or mark on each subsequent
select, which can cause a substantial amount of redundant work.
Asynchronous I/O and overlapped I/O resolves both these dilemmas by passing data to and from user
buffers after control has been returned to the user application. Asynchronous I/O notifies these worker
threads when data is available to be read or when a connection has become ready to transmit data.
An application will create an I/O completion port using the QsoCreateIOCompletionPort() API. This API
will return a handle which can be used to schedule and wait for completion of asynchronous I/O requests.
The application will start an input or an output function, specifying a I/O completion port handle. When the
I/O completes, status information and an application-defined handle will be posted to the specified I/O
completion port. The post to the I/O completion port will wake up exactly one of possibly many threads
that are waiting. The application receives:
For an example of a simple server model that uses asynchronous I/O, see Example: Use asynchronous
I/O.
Secure sockets
Currently, OS/400 supports two methods of creating secure socket applications on the iSeries. The SSL_
APIs and Global Secure Toolkit (GSKit) APIs provide communications privacy over an open
communications network, which in most cases is the Internet. These APIs allow client/server applications
to communicate in a way that is designed to prevent eavesdropping, tampering, and message forgery.
Both support server and client authentication and both allow an application to use Secure Sockets Layer
(SSL) protocol. However, GSKit APIs are supported across all IBM ERserver platforms, while the SSL_
APIs are native to the OS/400 operating system. To ensure interoperatability across platforms, it is
recommended that you used GSKit APIs when developing applications for secure socket connections.
| The Secure socket API error code messages topic provides a list of common error code messages that
| can occur with secure socket APIs.
Originally developed by Netscape, Secure Sockets Layer (SSL) protocol is a layered protocol that is
intended to be used on top of a reliable transport such as Transmission Control Protocol (TCP) to provide
secure communications for an application. A few of the many applications that require secure
communications are HTTPs, FTPs, SMTP, and TELNETs.
An SSL-enabled application usually needs to use a different port than an application that is not
SSL-enabled. For example, an SSL-enabled browser accesses an SSL-enabled Hypertext Transfer
Protocol (HTTP) server with a Universal Resource Locator (URL) that begins ″HTTPs″ rather than ″HTTP.″
In most cases, a URL of ″HTTPs″ attempts to open a connection to port 443 of the server system instead
of to port 80 that the standard HTTP server uses.
There are multiple versions of the SSL protocol defined. The latest version, Transport Layer Security (TLS)
Version 1.0, provides an evolutionary upgrade from SSL Version 3.0. Both iSeries-native SSL_ APIs and
the GSKit APIs support TLS Version 1.0, TLS Version 1.0 with SSL Version 3.0 compatibility, SSL Version
3.0, SSL Version 2.0, and SSL Version 3.0 with 2.0 compatibility. For more details on TLS Version 1.0, see
Internet Engineering Task Force (IETF) RFC 2246, ″Transport Layer Security″.
| Note: These APIs only support sockets with an address family of AF_INET or AF_INET6 and type
| SOCK_STREAM.
| The following table describes the GSKit APIs:
| Table 14. Global secure toolkit APIs
| Function Description
| gsk_attribute_get_buffer() Obtains specific character string information about a
| secure session or an SSL environment, such as
| certificate store file, certificate store password, application
| ID, and ciphers.
| gsk_attribute_get_cert_info() Obtains specific information about either the server or
| client certificate for a secure session or an SSL
| environment.
| gsk_attribute_get_enum_value() Obtains values for specific enumerated data for a secure
| session or an SSL environment.
| gsk_attribute_get_numeric_value() Obtains specific numeric information about a secure
| session or an SSL environment.
| gsk_attribute_set_buffer() Sets a specified buffer attribute to a value inside the
| specified secure session or an SSL environment.
| gsk_attribute_set_enum() Sets a specified enumerated type attribute to an
| enumerated value in the secure session or SSL
| environment.
| gsk_attribute_set_numeric_value() Sets specific numeric information for a secure session or
| an SSL environment.
| gsk_environment_close() Closes the SSL environment and releases all storage
| associated with the environment.
| gsk_environment_init() Initializes the SSL environment after any required
| attributes are set.
| gsk_environment_open() Returns an SSL environment handle that must be saved
| and used on subsequent gsk calls.
| gsk_secure_soc_close() Closes a secure session and free all the associated
| resources for that secure session.
| gsk_secure_soc_init() Negotiates a secure session, using the attributes set for
| the SSL environment and the secure session.
| gsk_secure_soc_misc() Performs miscellaneous functions for a secure session.
| gsk_secure_soc_open() Obtains storage for a secure session, sets default values
| for attributes, and returns a handle that must be saved
| and used on secure session-related function calls.
| gsk_secure_soc_read() Receives data from a secure session.
| gsk_secure_soc_startInit() Starts an asynchronous negotiation of a secure session,
| using the attributes set for the SSL environment and the
| secure session.
| An application that uses the sockets and GSKit APIs contains the following elements:
| 1. A call to socket() to obtain a socket descriptor.
| 2. A call to gsk_environment_open() to obtain a handle to an SSL environment.
| 3. One or more calls to gsk_attribute_set_xxxxx() to set attributes of the SSL environment. At a
| minimum, either a call to gsk_attribute_set_buffer() to set the GSK_OS400_APPLICATION_ID value
| or to set the GSK_KEYRING_FILE value. Only one of these should be set. It is prefered that you use
| the GSK_OS400_APPLICATION_ID value. Also ensure you set the type of application (client or
| server), GSK_SESSION_TYPE, using gsk_attribute_set_enum().
| 4. A call to gsk_environment_init() to initialize this environment for SSL processing and to establish the
| SSL security information for all SSL sessions that will run using this environment.
| 5. Socket calls to activate a connection. It calls connect() to activate a connection for a client program,
| or it calls bind(), listen(), and accept() to enable a server to accept incoming connection requests.
| 6. A call to gsk_secure_soc_open() to obtain a handle to a secure session.
| 7. One or more calls to gsk_attribute_set_xxxxx() to set attributes of the secure session. At a minimum,
| a call to gsk_attribute_set_numeric_value() to associate a specific socket with this secure session.
| 8. A call to gsk_secure_soc_init() to initiate the SSL handshake negotiation of the cryptographic
| parameters.
| Note: Typically, a server program must provide a certificate for an SSL handshake to succeed. A
| server must also have access to the private key that is associated with the server certificate
| and the key database file where the certificate is stored. In some cases, a client must also
| provide a certificate during the SSL handshake processing. This occurs if the server which the
| client is connecting to has enabled client authentication. The
| gsk_attribute_set_buffer(GSK_OS400_APPLICATION_ID) or
| gsk_attribute_set_buffer(GSK_KEYRING_FILE) API calls identify (though in dissimilar ways)
| the key database file from which the certificate and private key that are used during the
| handshake are obtained.
| 9. Calls to gsk_secure_soc_read() and gsk_secure_soc_write() to receive and send data.
| 10. A call to gsk_secure_soc_close() to end the secure session.
| 11. A call to gsk_environment_close() to close the SSL environment.
| 12. A call to close() to destroy the connected socket.
| See these examples for sample programs that use the GSKit APIs:
| v Example: Establish a secure server with asynchronous data receive
| v Example: Establish a secure server that uses asynchronous handshake
| v Example: Establish a secure client with Global Secure ToolKit (GSKit) APIs
| SSL_ APIs
| The SSL_ APIs provide allow programmers to create secure socket applications on iSeries. Unlike GSKit
| APIs, SSL_ APIs are native to the OS/400 system only. The following table describes nine SSL_ APIs that
| An application that uses the sockets and SSL_ APIs contains the following elements:
| v A call to socket() to obtain a socket descriptor.
| v Either call SSL_Init() or SSL_Init_Application() to initialize the job environment for SSL processing and
| to establish the SSL security information for all SSL sessions that will run in the current job. Only one of
| these APIs should be used. It is prefered that you use the SSL_Init_Application() API.
| v Socket calls to activate a connection. It calls connect() to activate a connection for a client program, or
| it calls bind(), listen(), and accept() to enable a server to accept incoming connection requests.
| v A call to SSL_Create() to enable SSL support for the connected socket.
| v A call to SSL_Handshake() to initiate the SSL handshake negotiation of the cryptographic parameters.
| Note: Typically, a server program must provide a certificate for an SSL handshake to succeed. A server
| must also have access to the private key that is associated with the server certificate and the key
| database file where the certificate is stored. In some cases, a client must also provide a
| certificate during the SSL handshake processing. This occurs if the server which the client is
| connecting to has enabled client authentication. The SSL_Init() or SSL_Init_Application() APIs
| identify (though in dissimilar ways) the key database file from which the certificate and private
| key that are used during the handshake are obtained.
| v Calls to SSL_Read() and SSL_Write() to receive and send data.
| v A call to SSL_Destroy() to disable SSL support for the socket.
| v A call to close() to destroy the connected sockets.
| For sample programs that use these SSL_ APIs, see these sample programs:
| v Example: Establish a secure server with SSL_ APIs
| v Example: Establish a secure client with SSL_ APIs
| where XXXXXXX is message ID for the return code. For example if the return code was 3, you would
| enter
| DSPMSGD RANGE(CPDBCB9)
| 2. Select 1 to display message text.
| Table 16. Secure Socket API Error Code Messages
| Return Code Message ID Constant Name
| 0 CPCBC80 GSK_OK
| 4 CPCBC80 GSK_INSUFFICIENT_STORAGE
| 502 CPE3406 GSK_WOULD_BLOCK
| 1 CPDBCA1 GSK_INVALID_HANDLE
| 2 CPDBCB3 GSK_API_NOT_AVAILABLE
| 3 CPDBCB9 GSK_INTERNAL_ERROR
| 5 CPDBC95 GSK_INVALID_STATE
| 107 CPDBC98 GSK_KEYFILE_CERT_EXPIRED
| 201 CPDBCA4 GSK_NO_KEYFILE_PASSWORD
| 202 CPDBCB5 GSK_KEYRING_OPEN_ERROR
| 301 CPDBCA5 GSK_CLOSE_FAILED
| 402 CPDBC81 GSK_ERROR_NO_CIPHERS
| 403 CPDBC82 GSK_ERROR_NO_CERTIFICATE
| 404 CPDBC84 GSK_ERROR_BAD_CERTIFICATE
| 405 CPDBC86 GSK_ERROR_UNSUPPORTED_CERTIFICATE_TYPE
| 406 CPDBC8A GSK_ERROR_IO
| 407 CPDBCA3 GSK_ERROR_BAD_KEYFILE_LABEL
| 408 CPDBCA7 GSK_ERROR_BAD_KEYFILE_PASSWORD
| 409 CPDBC9A GSK_ERROR_BAD_KEY_LEN_FOR_EXPORT
| 410 CPDBC8B GSK_ERROR_BAD_MESSAGE
| 411 CPDBC8C GSK_ERROR_BAD_MAC
| 412 CPDBC8D GSK_ERROR_UNSUPPORTED
| 414 CPDBC84 GSK_ERROR_BAD_CERT
| 415 CPDBC8B GSK_ERROR_BAD_PEER
| 417 CPDBC92 GSK_ERROR_SELF_SIGNED
| 420 CPDBC96 GSK_ERROR_SOCKET_CLOSED
| 421 CPDBCB7 GSK_ERROR_BAD_V2_CIPHER
| 422 CPDBCB7 GSK_ERROR_BAD_V3_CIPHER
| 428 CPDBC82 GSK_ERROR_NO_PRIVATE_KEY
| 501 CPDBCA8 GSK_INVALID_BUFFER_SIZE
| 601 CPDBCAC GSK_ERROR_NOT_SSLV3
| 602 CPDBCA9 GSK_MISC_INVALID_ID
Applications that run on hosts in a secure internal network must send their requests to firewall proxy
servers to navigate the firewall. The proxy servers can then forward these requests to the real server on
the less secure network and relay the reply back to the applications on the originating host. A common
example of a proxy server is an HTTP proxy server. Proxy servers perform a number of tasks for HTTP
clients:
v They hide your internal network from outside systems.
v They protect the host from direct access by outside systems.
v They can filter data that comes in from outside if they are properly designed and configured.
HTTP proxy servers handle only HTTP clients.
A common alternative to running multiple proxy servers on a firewall is to run a more robust proxy server
known as a SOCKS server. A SOCKS server can act as a proxy for any TCP client connection that is
established using the sockets API. The key advantage to iSeries Client SOCKS support is that it enables
client applications to access a SOCKS server transparently without changing any client code.
The figure below shows a common firewall arrangement with an HTTP proxy, a telnet proxy, and a SOCKS
proxy on the firewall. Notice that the two separate TCP connections used for the secure client that is
accessing a server on the internet. One connection leads from the secure host to the SOCKS server, and
the other leads from the less secure network to the SOCKS server.
Note: The remote host on the less secure network connects directly to the SOCKS server. It does not
have direct access to the secure client.
Up to this point we have addressed ″outbound″ TCP connections that originate from the secure client.
Client SOCKS support also lets you tell the SOCKS server to allow an inbound connection request across
a firewall. An Rbind() call from the secure client system allows this communication. For Rbind() to
operate, the secure client must have previously issued a connect() call and the call must have resulted in
an outbound connection over the SOCKS server. The Rbind() inbound connection must be from the same
IP address that was addressed by the outbound connection that the connect() established.
The following figure shows a detailed overview of how sockets functions interact with a SOCKS server
transparent to the application. In the example, the FTP client calls the Rbind() function instead of a bind()
function1. It makes this call by recompiling the FTP client code with the __Rbind preprocessor #define,
which defines bind() to be Rbind(). Alternatively, an application can explicitly code Rbind() in the pertinent
source code. If an application does not require inbound connections across a SOCKS server, Rbind()
should not be used.
Notes:
1. The FTP client uses Rbind() because the FTP protocol allows the FTP server to establish a data
connection because of a request from the FTP client to send files or data.
2. The SOCKS server establishes a data connection with the FTP client and relays data between the
FTP client and the FTP server. Many SOCKS servers allow a fixed length of time for the server to
connect to the secure client. If a the server does not connect within this time, errno ECONNABORTED
will be encountered on the accept().
3. FTP client initiates an outbound TCP connection to a less secure network through a SOCKS server.
The destination address that the FTP client specifies on the connect is the IP address and port of the
FTP server located on the less secure network. The secure host system has been configured through
the SOCKS page to direct this connection through the SOCKS server. Once configured, the system
automatically directs the connection to the SOCKS server that was specified through the SOCKS page.
4. A socket is opened and Rbind() is called in order to establish an inbound TCP connection. Once
established, this inbound connection is from the same destination-outbound IP address that was
specified above. Outbound and inbound connections over the SOCKS server must be paired for a
particular thread. In other words, all Rbind() inbound connections should immediately follow the
outbound connection over the SOCKS server and no intervening non-SOCKS connections relating to
this thread can be attempted before the Rbind() runs.
5. getsockname() returns the SOCKS server address. The socket is logically bound to the SOCKS
server IP address coupled with a port that is selected through the SOCKS server. In this example, the
address is sent through the ″control connection″ Socket CTLed to the FTP server that is located on the
less secure network. This is the address to which the FTP server connects. The FTP server connects
to the SOCKS server and not directly to the secure host.
6. The SOCKS server establishes a data connection with the FTP client and relays data between the
FTP client and the FTP server. Many SOCKS servers allow a fixed length of time for the server to
connect to the secure client. If a server does not connect within this time, errno ECONNABORTED will
be encountered on the accept().
All network functions with names that end in ″_r″ have similar semantics and are also threadsafe. For a
sample program that uses thread safe Socket APIs, see Example: Use gethostbyaddr_r() for threadsafe
network routines.
The other resolver routines are threadsafe with each other but they use the _res data structure. This data
structure is shared between all threads in a process and can be changed by an application during a
resolver call. For a sample program that uses resolver routines, see Example: Update and query DNS.
Non-blocking I/O
When an application issues one of the socket input functions, and there is no data to read, the function
blocks, and does not return until there is data to read. Similarly, an application can block on a socket
output function when data cannot be sent immediately. Finally, connect() and accept() can block while
waiting for connection establishment with the partner’s programs.
Sockets provides a method that enables application programs to issue functions that block so that the
function returns without delay. This is done by either calling fcntl() to turn on the O_NONBLOCK flag, or
calling ioctl() to turn on the FIONBIO flag. Once running in this non-blocking mode, if a function cannot be
completed without blocking, it returns immediately. A connect() may return with [EINPROGRESS], which
means that the connection initiation has been started. You can then use select() to determine when the
connection has completed. For all other functions that are affected by running in the non-blocking mode,
an error code of [EWOULDBLOCK] indicates that the call was unsuccessful.
For a sample program that uses non-blocking I/O, see Example: Non-blocking I/O and select().
The application should ensure that it is able to handle receiving a signal before it requests the system to
send signals. This is done by setting up signal handlers. One way to set a signal handler is by issuing
the sigaction() call.
An application requests the system to send the SIGURG signal by one of the following methods:
v Issuing a fcntl() call and specifying a process ID or a process group ID with the F_SETOWN command.
v Issuing an ioctl() call and specifying the FIOSETOWN or the SIOCSPGRP command (request).
An application requests the system to send the SIGIO signal in two steps. First it must set the process ID
or the process group ID as described above for the SIGURG signal. This is to inform the system of where
the application wants the signal to be delivered. Second, the application must do either of the following:
v Issue the fcntl() call and specify the F_SETFL command with the FASYNC flag.
v Issue the ioctl() call and specify the FIOASYNC command.
This step requests the system to generate the SIGIO signal. Note that these steps can be done in any
order. Also note that if an application issues these requests on a listening socket, the values set by the
requests are inherited by all sockets that are returned to the application from the accept() function. That
is, newly accepted sockets will also have the same process ID or process group ID as well as the same
information with regard to sending the SIGIO signal.
A socket can also generate synchronous signals on error conditions. Whenever an application receives
[EPIPE] an errno on a socket function, a SIGPIPE signal is delivered to the process that issued the
operation receiving the errno value. On a BSD implementation, by default the SIGPIPE signal ends the
process that received the errno value. To remain compatible with previous releases of the OS/400
implementation, the OS/400 implementation uses a default behavior of ignore for the SIGPIPE signal. This
ensures that existing applications will not be negatively affected by the addition of the signals function.
When a signal is delivered to a process that is blocked on a sockets function, the function returns from the
wait with the [EINTR] errno value, allowing the application’s signal handler to run. The functions for which
this will occur are:
v accept()
v connect()
v read()
v readv()
v recv()
v recvfrom()
v recvmsg()
v select()
v send()
v sendto()
v sendmsg()
It is important to note that signals do not provide the application program with a socket descriptor that
identifies where the condition being signalled actually exists. Thus, if the application program is using
multiple socket descriptors, it will have to either poll the descriptors or use the select() call to determine
why the signal was received.
For a sample program that uses signals, see Example: Use signals with blocking socket APIs.
IP multicasting
IP multicasting allows an application to send a single IP datagram that a group of hosts in a network can
receive. The hosts that are in the group may reside on a single subnet or may be on different subnets that
multicast capable routers connect. Hosts may join and leave groups at any time. There are no restrictions
on the location or number of members in a host group. A class D Internet address in the range 224.0.0.1
to 239.255.255.255 identifies a host group.
You can currently use IP multicasting with AF_INET address family only.
An application program can send or receive multicast datagrams using the Sockets API and
connectionless, SOCK_DGRAM type sockets. Multicasting is a one-to-many transmission method.
Connection-oriented sockets of type SOCK_STREAM cannot be used for multicasting. When a socket of
type SOCK_DGRAM is created, an application can use the setsockopt() function to control the multicast
characteristics associated with that socket. The setsockopt() function accepts the following IPPROTO_IP
level flags:
v IP_ADD_MEMBERSHIP: Joins the multicast group specified
v IP_DROP_MEMBERSHIP: Leaves the multicast group specified
v IP_MULTICAST_IF: Sets the interface over which outgoing multicast datagrams should be sent
v IP_MULTICAST_TTL: Sets the Time To Live (TTL) in the IP header for outgoing multicast datagrams
v IP_MULTICAST_LOOP: Specifies whether or not a copy of an outgoing multicast datagram should be
delivered to the sending host as long as it is a member of the multicast group
For examples of IP multicasting, see the following examplesExample: Use multicasting.
The send_file() enables the sending of file data directly from a file system over a connected socket with a
single API call.
The accept_and_recv() is a combination of three socket functions: accept(), getsockname(), and recv().
For sample programs for the send_file() and accept_and_recv() APIs, see Example: Transfer file data
using send_file() and accept_and_recv().
OOB data is sent by specifying the MSG_OOB flag on the send(), sendto(), and sendmsg() functions.
The transmission of OOB data is the same as the transmission of regular data. It is sent after any data
that is buffered. In other words, OOB data does not take precedence over any data that may be buffered;
data is transmitted in the order that it was sent.
Note: The value that indicates which byte the OOB marker points to is set on a system basis (all
applications use that value). This value must be consistent between the local and remote ends of
a TCP connection. Socket applications that use this value must use it consistently between the
client and server applications. To change which byte the OOB marker points to, see Change TCP
Attributes (CHGTCPA) command in the Information Center.
The SIOCATMARK ioctl() request determines if the read pointer is pointing to the last OOB byte.
Note: If multiple occurrences of OOB data are sent, the OOB marker points to the last OOB byte of the
final OOB data occurrence.
v Independent of whether OOB data is received in-line or not, an input operation processes data up to the
OOB marker, if OOB data was sent.
v A recv(), recvmsg(), or recvfrom() function (with the MSG_OOB flag set) is used to receive OOB data.
An error of [EINVAL] is returned if one of the receive functions has completed and one of the following
occurs:
– The socket option SO_OOBINLINE is not set and there is no OOB data to receive.
– The socket option SO_OOBINLINE is set.
If the socket option SO_OOBINLINE is not set, and the sending program sent OOB data with size
greater than one byte, all the bytes but the last are considered normal data. (Normal data means that
the receiving program can receive data without specifying the MSG_OOB flag.) The last byte of the
OOB data that was sent is not stored in the normal data stream. This byte can only be retrieved by
issuing a recv(), recvmsg(), or recvfrom() function with the MSG_OOB flag set. If a receive is issued
with the MSG_OOB flag not set, and normal data is received, the OOB byte is deleted. Also, if multiple
occurrences of OOB data are sent, the OOB data from the preceding occurrence is lost, and the
position of the OOB data of the final OOB data occurrence is remembered.
If the socket option SO_OOBINLINE is set, then all of the OOB data that was sent is stored in the
normal data stream. Data can be retrieved by issuing one of the three receive functions without
specifying the MSG_OOB flag (if it is specified, an error of [EINVAL] is returned). OOB data is not lost if
multiple occurrences of OOB data are sent.
v OOB data is not discarded if SO_OOBINLINE is not set, OOB data has been received, and the user
then sets SO_OOBINLINE on. The initial OOB byte is considered normal data.
I/O multiplexing—select()
Because Asynchronous I/O provides a more efficient way to maximize your application resources, we
recommend using the Asynchronous I/O APIs rather than the select() API. However, your specific
application design may allow for select() to be used. Like Asynchronous I/O, select() creates a common
point to wait on multiple conditions at the same time. However, select() allows an application to specify
sets of descriptors to do the following:
v To see if there is data to be read.
v To see if data can be written.
v To see if an exception condition is present.
The descriptors that can be specified in each set can be socket descriptors, file descriptors, or any other
object that is represented by a descriptor.
The select() function also allows the application to specify if it wants to wait for data to become available.
The application can specify how long to wait. See Example: Non-blocking I/O and select() for a sample
program.
Included in the network routines is a group of routines called resolver routines. These routines make,
send, and interpret packets for name servers in the Internet domain and are also used to do name
resolution. The resolver routines normally get called by gethostbyname(), gethostbyaddr(),
getnameinfo(), and getaddrinfo() but can be called directly. For examples that use these resolver
routines, see Example: Use gethostbyaddr_r() for threadsafe network routines. Primarily resolver routines
are used for accessing domain name system (DNS) through socket application. See Domain name service
support for details on how sockets can be used with DNS.
The resolvers provided in the OS/400 implementation are socket functions that provide communication
with a name server. These routines are used to make, send, update, and interpret packets, and perform
name caching for performance. They also provide function for ASCII to EBCDIC and EBCDIC to ASCII
conversion. Optionally, the resolver will use transaction signatures (TSIG) to securely communicate with
the DNS. For a brief summary of the individual resolver routines, see Sockets Network Functions
(Routines) in the API reference topic in the Information Center. This link also provides information on the
_res structure. The _res structure contains global information that are used by these routines.
For more information on domain names, see the following RFC, which you can locate from the RFC
search page.
v RFC 1034, ″Domain names - concepts and facilities″
v RFC 1035, ″Domain names - implementation and specification″
v RFC 1886, ″DNS Extensions to support IP version 6″
v RFC 2136, ″Dynamic Updates in the Domain Name System (DNS UPDATE)″
v RFC 2181, ″Clarifications to the DNS Specification″
v RFC 2845, ″Secret Key Transaction Authentication for DNS (TSIG)″
v RFC 3152, ″DNS Delegation of IP6.ARPA″
| For more information on other ways to use DNS with socket applications, see the following topics:
| v Environment variables
| This topic describes the environment variables that can be used for name resolution.
| v Data caching
| This topic provides details on using sockets to cache responses to DNS queries to lessen the amount of
| traffic on a network. Example: Update and query DNS provides a sample program that shows how DNS
| records can be queried and updated with socket APIs.
| Environment variables
| You can use enviroment variables to override default initialization of resolver functions. Environment
| variables are only checked after a successful call to res_init() or res_ninit(). So if the structure has been
| manually initialized, environment variables are ignored. Also note that the structure is only initialized once
| so later changes to the environment variables will be ignored.
| Note: The name of the environment variable must be uppercased. The string value may be mixed case.
| Japanese systems using CCSID 290 should use uppercase characters and numbers only in both
| environment variables names and values. The list contains the descriptions of environment variables that
| can be used with the res_init() and res_ninit() APIs.
| LOCALDOMAIN
| Set this environment variable to a space-separated list of up to six search domains with a total of
| 256 characters (including spaces). This will override the configured search list (struct state.defdname
| and struct state.dnsrch). If a search list is specified, the default local domain is not used on queries.
| RES_OPTIONS
| Allows certain internal resolver variables to be modified. The environment variable can be set to one
| or more of the following space-separated options:
| v NDOTS:n
| Sets a threshold for the number of dots which must appear in a name given to res_query() before
| QIBM_BIND_RESOLVER_FLAGS
| Set this environment variable to a space separated list of resolver option flags. This will override the
| RES_DEFAULT options (struct state.options) and system configured values (Change TCP/IP Domain
| - CHGTCPDMN). The state.options structure will be initialized normally, using RES_DEFAULT,
| OPTIONS environment values and CHGTCPDMN configured values. Then this environment variable
| will be used to override those defaults. The flags named in this environment variable may be
| prepended with a ’+’, ’-’ or ’NOT_’ to set (’+’) or reset (’-’,’NOT_’) the value.
| For example, to turn on RES_NOCHECKNAME and turn off RES_ROTATE, use the following
| command from a character-based interface:
| ADDENVVAR ENVVAR(QIBM_BIND_RESOLVER_FLAGS) VALUE(’RES_NOCHECKNAME NOT_RES_ROTATE’)
| or
| ADDENVVAR ENVVAR(QIBM_BIND_RESOLVER_FLAGS) VALUE(’+RES_NOCHECKNAME -RES_ROTATE’)
| QIBM_BIND_RESOLVER_SORTLIST
| Set this environment variable to a space-separated list of up to ten IP addresses/mask pairs in dotted
| decimal format (9.5.9.0/255.255.255.0) to create a sort list (struct state.sort_list).
| Data caching
| Caching of responses to DNS queries is done by OS/400 sockets in an effort to lessen the amount of
| network traffic. The cache is added to and updated as needed.
| If RES_AAONLY (authoritative answers only) is set in _res.options, the query is always sent on the
| network. In this case, the cache is never checked for the answer. If RES_AAONLY is not set, the cache is
| checked for an answer to the query before any attempt to send it on the network is performed. If the
| answer is found and the time to live has not expired, the answer is returned to the user as the answer to
| the query. If the time to live has expired, the entry is removed, and the query is sent on the network. Also,
| if the answer is not found in the cache, the query is sent on the network.
| Answers from the network are cached if the responses are authoritative. Non-authoritative answers are not
| cached. Also, responses received as a result of an inverse query are not cached. You can clear this cache
| by updating the DNS configuration with either the CHGTCPDMN, CFGTCP option 12, or through iSeries
| Navigator.
| For an example program that uses data caching, see Example: Update and update DNS.
The following list summarizes the differences between the OS/400 implementation and the BSD
implementation.
/etc/hosts, /etc/services, /etc/networks, and /etc/protocols
For these files, the OS/400 implementation supplies the following database files which serve the
same functions, respectively.
/etc/resolv.conf
The OS/400 implementation requires that this information be configured using the TCP/IP
properties page in iSeries Navigator. To access the TCP/IP properties page, complete the following
steps:
1. In iSeries Navigator, expand your iSeries server --> Network --> TCP/IP Configuration.
2. Right-click TCP/IP Configuration.
3. Click Properties.
bind() On a BSD system, a client can create an AF_UNIX socket using socket(), connect to a server
using connect(), and then bind a name to its socket using bind(). The OS/400 implementation does
not support this scenario (the bind() fails).
close()
The OS/400 implementation supports the linger timer for close(), except for AF_INET sockets over
SNA. Some BSD implementations do not support the linger timer for close().
connect()
On a BSD system, if a connect() is issued against a socket that was previously connected to an
address and is using a connectionless transport service, and an invalid address or an invalid
address length is used, the socket is no longer connected. The OS/400 implementation does not
support this scenario (the connect() fails and the socket is still connected).
A connectionless transport socket for which a connect() has been issued can be disconnected by
setting the address_length parameter to zero and issuing another connect().
accept(), getsockname(), getpeername(), recvfrom(), and recvmsg()
| When using AF_UNIX or AF_UNIX_CCSID address family and the socket has not been bound, the
| default OS/400 implementation may return an address length of zero and an unspecified address
| structure. The OS/400 BSD 4.4/UNIX 98 and other implementations may return a small address
| structure with just the address family specified.
ioctl()
v On a BSD system, on a socket of type SOCK_DGRAM, the FIONREAD request returns the
length of the data plus the length of the address. On the OS/400 implementation, FIONREAD
only returns the length of data.
| UNIX 98 compatibility
| Created by Open Group, a consortium of developers and venders, UNIX 98 improved the inoperability of
| the UNIX operating system while incorporating much of the Internet-related function for which UNIX had
| become known. New for V5R2, OS/400 sockets provides programmers the ability to write socket
| applications that are compatible with UNIX 98 operating environment. Currently, IBM supports two versions
| of most sockets APIs. The base OS/400 API uses BSD 4.3 structures and syntax. The other uses syntax
| and structures compatible with BSD 4.4 and the UNIX 98 programming interface specifications. You can
| select the UNIX 98 compatible interface by defining the _XOPEN_SOURCE macro to a value of 520 or
| greater.
| When you specify the _XOPEN_OPEN macro, you can write UNIX 98 compatible applications with the
| same address families that are used in default OS/400 implementations; however, there are differences in
| the sockaddr address structure. The table compares the BSD 4.3 sockaddr address structure with the
| UNIX 98 compatible address structure:
| Table 17. Comparison of BSD 4.3 and UNIX 98/BSD 4.4 socket address structure
| BSD 4.3 structure BSD 4.4/UNIX 98 compatible structure
| sockaddr address structure
| API differences
| When you develop in ILE-based languages and an application is compiled with the _XOPEN_SOURCE
| macro, some sockets APIs are mapped to internal names. These internal names provide the same
| function as the original API. The table lists these affected APIs. If you are writing socket applications in
| some other C-based language, you can write directly to the internal name of these APIs. Use the link to
| the original API to see usage notes and details for both versions of these APIs.
| Table 18. API and UNIX 98 equivelant name
| API name Internal name
| accept() qso_accept98()
| accept_and_recv() qso_accept_and_recv98()
| bind() qso_bind98()
| connect() qso_connect98()
| endhostent() qso_endhostent98()
| endnetent() qso_endnetent98()
| endprotoent() qso_endprotoent98()
| endservent() qso_endservent98()
| getaddrinfo() qso_getaddrinfo98()
| gethostbyaddr() qso_gethostbyaddr98()
| gethostbyaddr_r() qso_gethostbyaddr_r98()
| gethostname() qso_gethostname98()
| gethostname_r() qso_gethostname_r98()
| gethostbyname() qso_gethostbyname98()
| gethostent() qso_gethostent98()
| getnameinfo() qso_getnameinfo98()
| getnetbyaddr() qso_getnetbyaddr98()
| getnetbyname() qso_getnetbyname98()
| getnetent() qso_getnetent98()
| getpeername() qso_getpeername98()
| getprotobyname() qso_getprotobyname98()
| getprotobynumber() qso_getprotobynumber98()
| getprotoent() qso_getprotoent98()
| getsockname() qso_getsockname98()
| getsockopt() qso_getsockopt98()
| getservbyname() qso_getservbyname98()
| getservbyport() qso_getservbyport98()
Sockets provides three sets of APIs that can pass descriptors between server jobs:
v spawn()
Note: spawn() is not a socket API. It is supplied as part of the OS/400 Process-Related APIs.
v givedescriptor() and takedescriptor()
v sendmsg() and recvmsg()
The spawn() API starts a new server job (often called a ″child job″) and gives certain descriptors to that
child job. If the child job is already active, then the givedescriptor() and takedescriptor() or the
sendmsg() and recvmsg() APIs need to be used.
However, the sendmsg() and recvmsg() APIs offer many advantages over spawn() and givedescriptor()
and takedescriptor():
The sendmsg() and recvmsg() APIs allow you to transfer data, which may be control information,
along with the descriptor; the givedescriptor() and takedescriptor() APIs do not.
Performance
Applications that use the sendmsg() and recvmsg() APIs tend to perform slightly better than
those that use the givedescriptor() and takedescriptor() APIs in three areas:
v Elapsed time
v CPU utilization
v Scalability
The amount of performance improvement of an application depends on the extent that the
application passes descriptors.
Pool of worker jobs
You may want to set up a pool of worker jobs so that a server can pass a descriptor and only one
of the jobs in the pool will become active and receive it. The sendmsg() and recvmsg() APIs can
be used to accomplish this by having all of the worker jobs wait on a shared descriptor. When the
server calls sendmsg(), only one of the worker jobs will receive the descriptor.
Unknown worker job ID
The givedescriptor() API requires the server job to know the job identifier of the worker job.
Typically the worker job obtains the job identifier and transfers it over to the server job with a data
queue. The sendmsg() and recvmsg() do not require the extra overhead to create and manage
this data queue.
Adaptive server design
When a server is designed using the givedescriptor() and takedescriptor(), a data queue is
typically used to transfer the job identifiers from worker jobs over to the server. The server then
does a socket(), bind(), listen(), and an accept(). When the accept() API completes, the server
pulls off the next available job ID from the data queue. It then passes the inbound connection to
that worker job. Problems arise when many incoming connection requests occur at once and there
are not enough worker jobs available. If the data queue that contains the worker job identifiers is
empty, the server blocks waiting for a worker job to become available, or the server creates
additional worker jobs. In many environments, neither of these alternatives are desirable because
additional incoming requests may fill the listen backlog.
Servers that use sendmsg() and recvmsg() APIs to pass descriptors remain unhindered during
heavy activity because they do not need to know which worker job is going to handle each
incoming connection. When a server calls sendmsg(), the descriptor for the incoming connection
and any control data are put into an internal queue for the AF_UNIX socket. When a worker job
becomes available, it will call recvmsg() and receive the first descriptor and the control data that
was in the queue.
Inactive worker job
The givedescriptor() API requires the worker job to be active while the sendmsg() API does not.
The job that calls sendmsg() does not require any information about the worker job. The
sendmsg() API requires only that an AF_UNIX socket connection has been set up.
For a sample program that uses the sendmsg() and recvmsg() APIs, see Example: Pass descriptors
between processes.
| Suppose you are a socket programmer that works for an application development company that
| specializes in socket applications for iSeries. To keep ahead of their competitors, you have decided to
| develop a suite of applications that will use the AF_INET6 address family, which accept connections from
| IPv4 and IPv6 . You want to create an application that will process requests from both IPv4 and IPv6
| nodes. You know that the OS/400 supports the AF_INET6 address family sockets which provides
| interoperability with AF_INET address family sockets. You also know this can be accomplished by using an
| IPv4-mapped IPv6 address format. See IPv6 applications compatibility with IPv4 applications for more
| details on working with interoperability between IPv6 and IPv4 applications.
| Scenario objectives
| Prerequisite steps
| Before developing your application that meets these objectives, you complete the following tasks:
| 1. Install QSYSINC library. This library provides necessary header files that are needed when compiling
| socket applications.
| 2. Install the C Compiler licensed program (5722–CX2).
| 3. Install and configure the 2838 Ethernet card. For information on Ethernet options, see Ethernet topic in
| the Information Center.
| 4.
| Set up TCP/IP and IPv6 network.
| Scenario details
| The following graphic describes the IPv6 network for which you will be creating applications to handle
| requests from IPv6 and IPv4 clients. The iSeries contains the program that will listen and process requests
| from these clients. The network is comprised of two separate domains, one that contains IPv4 clients
| exclusively and the other remote network containing only IPv6 clients. The domain name of the iSeries is
| myserver.myco.com. The server application will use the AF_INET6 address family to process these
| incoming requests with the in6addr_any specified on the bind() function call.
| See the following example programs that are used within this scenario:
| v Example: Accept connections from both IPv6 and IPv4 clients
| v Example: IPv4 or IPv6 client
|
| Example: Accept connections from both IPv6 and IPv4 clients
| Use this sample program to create a server/client model that accepts requests from both IPv4 (those
| socket applications that use AF_INET address family) and IPv6 (those applications that use the AF_INET6
| address family). Currently your socket application may only use AF_INET address family, which allows for
| TCP and UDP protocol; however, this may change with the increase in the use of IPv6 addresses. You
| can use this sample program to create your own applications that accomodate both address families.
| Socket flow of events: Server application that accepts requests from both IPv4 and IPv6 clients
| This flow describes each of the function calls and what they do within the socket application that accepts
| requests both IPv4 and IPv6 clients.
| 1. The socket() API specifies a socket descriptor that creates an endpoint. It also specifies the AF_INET6
| address family, which supports IPv6, and the TCP transport (SOCK_STREAM) will be used for this
| socket.
| 2. The setsockopt() function allows application to reuse the local addresswhen the server is restarted
| before the required wait time expires.
| 3. A bind() function supplies a unique name for the socket. In this example, the programmer set the
| address to in6addr_any, which (by default) allows connections to be established from any IPv4 or IPv6
| client that specifies port 3005. (i.e. the bind is done to both the IPv4 and IPv6 port spaces).
| Note: If the server only needs to handle IPv6 clients, then IPV6_ONLY socket option can be used.
| 4. The listen() function allows the server to accept incoming client connections. In this example, the
| programmer set the backlog to 10, which allows the system to queue 10 connection requests before
| the system starts rejecting incoming requests.
| 5. The server uses the accept() function to accept an incoming connection request. The accept() call will
| block indefinitely waiting for the incoming connection to arrive from an IPv4 or IPv6 client.
| 6. The getpeername() function returns the client’s address to the application. If the client is an IPv4
| client, the address will be shown as an IPv4–mapped IPv6 address.
Chapter 8. Socket scenario: Create an application to accept IPv4 and IPv6 clients 83
| 7. The recv() function receives that 250 bytes data from the client. In this example, the programmer
| knows that the client will send 250 bytes of data over. Knowing this, the programmer uses the
| SO_RCVLOWAT socket option and specify that she don’t want the recv() to wake up until all 250
| bytes of data have arrived.
| 8. The send() function echoes the data back to the client.
| 9. The close() function closes any open socket descriptors.
| Note: This client example can be used with other server application designs that wish to accept request
| for either IPv4 or IPv6 nodes. Other server designs, like those described in Examples:
| Connection-oriented designs, can be used with this client example.
| 1. The inet_pton() call converts the text form of the address to the binary form. In this example, two of
| these calls are issued. The first determines if the server is a valid AF_INET address. The second
| inet_pton() call determines whether the server has an AF_INET6 address. If it is numeric then we
| want to prevent getaddrinfo() from doing any name resolution. Otherwise a host name was provided
| that needs to be resolved whe the getaddrinfo() call is issued.
| 2. The getaddrinfo() call retrieves the address information needed for the subsequent socket() and
| connect() calls.
| 3. The socket() function returns a socket descriptor representing an endpoint. The statement also
| identifies the address family, socket type, and protocol using the information returned from
| getaddrinfo().
| 4. The connect() function establishes a connection to the server regardless of whether the server is IPv4
| or IPv6..
| 5. The send() function sends the data request to the server.
| 6. The recv() function receives data from the server application.
| 7. The close() function closes any open socket descriptors.
| The following sample code, shows the server application for this scenario. For the client application, see
| Example: IPv4 or IPv6 client. For information on the use of this code, see code disclaimer information.
| /**************************************************************************/
| /* Header files needed for this sample program */
| /**************************************************************************/
| #include <stdio.h>
| #include <sys/types.h>
| #include <sys/socket.h>
| #include <netinet/in.h>
| #include <arpa/inet.h>
|
| /**************************************************************************/
| /* Constants used by this program */
| /**************************************************************************/
| #define SERVER_PORT 3005
| #define BUFFER_LENGTH 250
| #define FALSE 0
|
| void main()
| {
| /***********************************************************************/
| /* Variable and structure definitions. */
| /***********************************************************************/
| int sd=-1, sdconn=-1;
| int rc, on=1, rcdsize=BUFFER_LENGTH;
| char buffer[BUFFER_LENGTH];
| struct sockaddr_in6 serveraddr, clientaddr;
| int addrlen=sizeof(clientaddr);
| char str[INET6_ADDRSTRLEN];
|
Chapter 8. Socket scenario: Create an application to accept IPv4 and IPv6 clients 85
| /* requests before the system starts rejecting the incoming */
| /* requests. */
| /********************************************************************/
| if (listen(sd, 10) < 0)
| {
| perror("listen() failed");
| break;
| }
|
| printf("Ready for client connect().\n");
|
| /********************************************************************/
| /* The server uses the accept() function to accept an incoming */
| /* connection request. The accept() call will block indefinitely */
| /* waiting for the incoming connection to arrive from an IPv4 or */
| /* IPv6 client. */
| /********************************************************************/
| if ((sdconn = accept(sd, NULL, NULL)) < 0)
| {
| perror("accept() failed");
| break;
| }
| else
| {
| /*****************************************************************/
| /* Display the client address. Note that if the client is */
| /* an IPv4 client, the address will be shown as an IPv4 Mapped */
| /* IPv6 address. */
| /*****************************************************************/
| getpeername(sdconn, (struct sockaddr *)&clientaddr, &addrlen);
| if(inet_ntop(AF_INET6, &clientaddr.sin6_addr, str, sizeof(str))) {
| printf("Client address is %s\n", str);
| printf("Client port is %d\n", ntohs(clientaddr.sin6_port));
| }
| }
|
| /********************************************************************/
| /* In this example we know that the client will send 250 bytes of */
| /* data over. Knowing this, we can use the SO_RCVLOWAT socket */
| /* option and specify that we don’t want our recv() to wake up */
| /* until all 250 bytes of data have arrived. */
| /********************************************************************/
| if (setsockopt(sdconn, SOL_SOCKET, SO_RCVLOWAT,
| (char *)&rcdsize,sizeof(rcdsize)) < 0)
| {
| perror("setsockopt(SO_RCVLOWAT) failed");
| break;
| }
|
| /********************************************************************/
| /* Receive that 250 bytes of data from the client */
| /********************************************************************/
| rc = recv(sdconn, buffer, sizeof(buffer), 0);
| if (rc < 0)
| {
| perror("recv() failed");
| break;
| }
|
| printf("%d bytes of data were received\n", rc);
| if (rc == 0 ||
| rc < sizeof(buffer))
| {
| printf("The client closed the connection before all of the\n");
| printf("data was sent\n");
| break;
| }
Chapter 8. Socket scenario: Create an application to accept IPv4 and IPv6 clients 87
| struct addrinfo hints, *res=NULL;
|
| /***********************************************************************/
| /* A do/while(FALSE) loop is used to make error cleanup easier. The */
| /* close() of the socket descriptor is only done once at the very end */
| /* of the program along with the free of the list of addresses. */
| /***********************************************************************/
| do
| {
| /********************************************************************/
| /* If an argument was passed in, use this as the server, otherwise */
| /* use the #define that is located at the top of this program. */
| /********************************************************************/
| if (argc > 1)
| strcpy(server, argv[1]);
| else
| strcpy(server, SERVER_NAME);
|
| memset(&hints, 0x00, sizeof(hints));
| hints.ai_flags = AI_NUMERICSERV;
| hints.ai_family = AF_UNSPEC;
| hints.ai_socktype = SOCK_STREAM;
| /********************************************************************/
| /* Check if we were provided the address of the server using */
| /* inet_pton() to convert the text form of the address to binary */
| /* form. If it is numeric then we want to prevent getaddrinfo() */
| /* from doing any name resolution. */
| /********************************************************************/
| rc = inet_pton(AF_INET, server, &serveraddr);
| if (rc == 1) /* valid IPv4 text address? */
| {
| hints.ai_family = AF_INET;
| hints.ai_flags |= AI_NUMERICHOST;
| }
| else
| {
| rc = inet_pton(AF_INET6, server, &serveraddr);
| if (rc == 1) /* valid IPv6 text address? */
| {
|
| hints.ai_family = AF_INET6;
| hints.ai_flags |= AI_NUMERICHOST;
| }
| }
| /********************************************************************/
| /* Get the address information for the server using getaddrinfo(). */
| /********************************************************************/
| rc = getaddrinfo(server, servport, &hints, &res);
| if (rc != 0)
| {
| printf("Host not found --> %s\n", gai_strerror(rc));
| if (rc == EAI_SYSTEM)
| perror("getaddrinfo() failed");
| break;
| }
|
| /********************************************************************/
| /* The socket() function returns a socket descriptor representing */
| /* an endpoint. The statement also identifies the address family, */
| /* socket type, and protocol using the information returned from */
| /* getaddrinfo(). */
| /********************************************************************/
| sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
| if (sd < 0)
| {
| perror("socket() failed");
| break;
Chapter 8. Socket scenario: Create an application to accept IPv4 and IPv6 clients 89
| if (sd != -1)
| close(sd);
| /***********************************************************************/
| /* Free any results returned from getaddrinfo */
| /***********************************************************************/
| if (res != NULL)
| freeaddrinfo(res);
| }
| All of the remaining examples are concurrent server designs. In these designs, the system uses multiple
| jobs and threads to handle the incoming connection requests. With a concurrent server there are usually
| multiple clients that connect to the server at the same time.
| For multiple concurrent clients in a network, it is recommend that you use the Asynchronous I/O socket
| APIs. These APIs provide the best performance in networks that have multiple concurrent clients.
| Asynchronous I/O describes what these APIs do and how they work. For an example program that uses
| these APIs, see Example: Use asynchronous I/O.
| spawn() server and spawn() worker
| The spawn() server and spawn() worker example uses the spawn() API to create a new job to
| handle each incoming request. After spawn() completes, the server can then wait on the accept()
| API for the next incoming connection to be received.
| The only problem with this server design is the performance overhead of creating a new job each
| time a connection is received. You can avoid the performance overhead of the spawn() server
| example by using prestarted jobs. Instead of creating a new job each time a connection is
| received, the incoming connection is given to a job that is already active. All of the remaining
| examples in this topic use prestarted jobs.
| sendmsg() server and recvmsg() worker
| The sendmsg() server and recvmsg() worker example pass the incoming connection to the worker
| (client) job. The server prestarted all of the worker jobs when the server job first started.
| All of these examples use a basic design for the client connection. See Example: Generic client for details.
|
| Flow of socket events: Server that uses spawn() to accept and process requests
| The following sequence of the socket calls provide a description of the graphic. It also describes the
| relationship between the server and worker examples. Each set of flows contain links to usage notes on
| specific APIs. If you need more details on the use of a particular API, you can use these links. The
| Example: Create a server that uses spawn() uses the following socket calls to create a child process with
| the spawn() function call:
| 1. The socket() function returns a socket descriptor representing an endpoint. The statement also
| identifies that the INET (Internet Protocol) address family with the TCP transport (SOCK_STREAM) will
| be used for this socket.
| 2. After the socket descriptor is created, the bind() function gets a unique name for the socket.
| 3. The listen() allows the server to accept incoming client connections.
| 4. The server uses the accept() function to accept an incoming connection request. The accept() call will
| block indefinitely waiting for the incoming connection to arrive.
| 5. The spawn() function initialize the parameters for a work job to handle incoming requests. In this
| example, the socket descriptor for the new connection is mapped over to descriptor 0 in the child
| program.
| See Example: Enable the worker job to receive a data buffer for a sample program that uses the socket
| descriptor to complete processes.
| The following figure illustrates how the server, worker, and client jobs interact when the system uses the
| sendmsg() and recvmsg() server design.
| Flow of socket events: Server that uses sendmsg() and recvmsg() functions
| The following sequence of the socket calls provide a description of the graphic. It also describes the
| relationship between the server and worker examples. Each set of flows contain links to usage notes on
| specific APIs. If you need more details on the use of a particular API, you can use these links. Example:
| Server program used for sendmsg() and recvmsg() uses the following socket calls to create a child
| process with the sendmsg() and recvmsg() function calls:
| 1. The socket() function returns a socket descriptor representing an endpoint. The statement also
| identifies that the INET (Internet Protocol) address family with the TCP transport (SOCK_STREAM) will
| be used for this socket.
| 2. After the socket descriptor is created, the bind() function gets a unique name for the socket.
| 3. The listen() allows the server to accept incoming client connections.
| 4. The socketpair() function creates a pair of UNIX datagram sockets. A server can use the
| socketpair() API to create a pair of AF_UNIX sockets.
| The following figure illustrates how the server, worker, and client jobs interact when the system uses the
| multiple accept() server design.
| Note: For a description of the client portion of this diagram, see Example: Generic client.
| Flow of socket events: Server that creates a pool of multiple accept() worker jobs
| The following sequence of the socket calls provide a description of the graphic. It also describes the
| relationship between the server and worker examples. Each set of flows contain links to usage notes on
| specific APIs. If you need more details on the use of a particular API, you can use these links. Example:
| Server program to create a pool of multiple accept() worker jobs uses the following socket calls to create a
| child process:
| 1. The socket() function returns a socket descriptor representing an endpoint. The statement also
| identifies that the INET (Internet Protocol) address family with the TCP transport (SOCK_STREAM) will
| be used for this socket.
| 2. After the socket descriptor is created, the bind() function gets a unique name for the socket.
| 3. The listen() allows the server to accept incoming client connections.
| 4. The spawn() function creates each of the worker jobs.
| 5. In this example, the first close() function closes the listening socket.
| This client job will work with each of these common connection-oriented server designs:
| v An iterative server. See Example: Write an iterative server program for a sample program.
| v A spawn server and worker. See Example: Use the spawn() API to create child processes for a sample
| program.
| v A sendmsg() server and rcvmsg() worker. See Example: Server program used for sendmsg() and
| recvmsg() for a sample program.
| v A multiple accept() design. See Example: Server program to create a pool of multiple accept() worker
| jobs for a sample program.
| v A non-blocking I/O and select() design. See Example: Non-blocking I/O and select() for a sample
| program.
| v A server that accepts connections from either an IPv4 or IPv6 client.. See Example: Accept connections
| from both IPv6 and IPv4 clients for a sample program.
| For information on the use of code examples, see the code disclaimer.
| /**************************************************************************/
| /* Generic client example is used with connection-oriented server designs */
| /**************************************************************************/
| #include <stdio.h>
| #include <stdlib.h>
| #include <sys/socket.h>
| #include <netinet/in.h>
|
| #define SERVER_PORT 12345
|
| main (int argc, char *argv[])
| {
| int len, rc;
| int sockfd;
| char send_buf[80];
| char recv_buf[80];
| struct sockaddr_in addr;
|
| /*************************************************/
Note: You can also use accept asynchronously by using the QsoStartAccept().
5. At some point, a client request arrives asynchronous to the server process . The sockets operating
system loads the supplied user buffer and sends the completed QsoStartRecv() request to the
specified I/O completion port. One worker thread is awoken and proceeds to process this request.
6. The worker thread extracts the client socket descriptor from the application-defined handle and
proceeds to echo the received data back to the client by performing a QsoStartSend() operation.
7. If the data can be immediately sent then QsoStartSend() returns indication of the fact, otherwise the
sockets operating system will send the data as soon as possible and post indication of the fact to the
specified I/O completion port. The worker thread gets indication of data sent and can wait on the I/O
completion port for another request or terminate if instructed to do so. QsoPostIOCompletion() could
be used by the master thread to post a worker thread termination event.
8. Master thread waits for worker thread to finish and then destroys the I/O completion port by calling
QsoDestroyIOCompletionPort().
Note: This server example works with the common client code describe in Example: Generic client.
This examples shows how a server program can use the asynchronous APIs. For information on the use
of code examples, see the code disclaimer.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#define _MULTI_THREADED
#include "pthread.h"
#include "qsoasync.h"
#define BufferLength 80
#define Failure 0
#define Success 1
#define SERVPORT 12345
/********************************************************************/
/* */
/* Function Name: main */
/* */
/* Descriptive Name: Master thread will establish a client */
/* connection and hand processing responsibility */
/* to a worker thread. */
/* Note: Due to the thread attribute of this program, spawn() must */
int main()
{
int listen_sd, client_sd, rc;
int on = 1, ioCompPort;
pthread_t thr;
void *status;
char buffer[BufferLength];
struct sockaddr_in serveraddr;
Qso_OverlappedIO_t ioStruct;
/*********************************************/
/* Create an I/O completion port for this */
/* process. */
/*********************************************/
if ((ioCompPort = QsoCreateIOCompletionPort()) < 0)
{
perror("QsoCreateIOCompletionPort() failed");
exit(-1);
}
/*********************************************/
/* Create a worker thread to */
/* to process all client requests. The */
/* worker thread will wait for client */
/* requests to arrive on the I/O completion */
/* port just created. */
/*********************************************/
rc = pthread_create(&thr, NULL, workerThread,
&ioCompPort);
if (rc < 0)
{
perror("pthread_create() failed");
QsoDestroyIOCompletionPort(ioCompPort);
close(listen_sd);
exit(-1);
}
/*********************************************/
/* Create an AF_INET stream socket to receive*/
/* incoming connections on */
/*********************************************/
if ((listen_sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket() failed");
QsoDestroyIOCompletionPort(ioCompPort);
exit(-1);
}
/*********************************************/
/* Allow socket descriptor to be reuseable */
/*********************************************/
if ((rc = setsockopt(listen_sd, SOL_SOCKET,
SO_REUSEADDR,
(char *)&on,
sizeof(on))) < 0)
{
perror("setsockopt() failed");
QsoDestroyIOCompletionPort(ioCompPort);
close(listen_sd);
exit(-1);
}
/*********************************************/
if ((rc = bind(listen_sd,
(struct sockaddr *)&serveraddr,
sizeof(serveraddr))) < 0)
{
perror("bind() failed");
QsoDestroyIOCompletionPort(ioCompPort);
close(listen_sd);
exit(-1);
}
/*********************************************/
/* Set listen backlog */
/*********************************************/
if ((rc = listen(listen_sd, 10)) < 0)
{
perror("listen() failed");
QsoDestroyIOCompletionPort(ioCompPort);
close(listen_sd);
exit(-1);
}
/*********************************************/
/* accept an incoming client connection. */
/*********************************************/
if ((client_sd = accept(listen_sd, (struct sockaddr *)NULL,
NULL)) < 0)
{
perror("accept() failed");
QsoDestroyIOCompletionPort(ioCompPort);
close(listen_sd);
exit(-1);
}
/*********************************************/
/* Issue QsoStartRecv() to receive client */
/* request. */
/* Note: */
/* postFlag == on denoting request should */
/* posted to the I/O */
/* completion port, even if */
/* if request is immediately */
/* available. Worker thread */
/* will process client */
/* request. */
/*********************************************/
/*********************************************/
/* initialize Qso_OverlappedIO_t structure - */
/* reserved fields must be hex 00’s. */
/*********************************************/
memset(&ioStruct, ’\0’, sizeof(ioStruct));
ioStruct.buffer = buffer;
ioStruct.bufferLength = sizeof(buffer);
/*********************************************/
/* Store the client descriptor in the */
/*********************************************/
/* Wait for worker thread to finish */
/* processing client connection. */
/*********************************************/
rc = pthread_join(thr, &status);
QsoDestroyIOCompletionPort(ioCompPort);
if ( rc == 0 && (rc = __INT(status)) == Success)
{
printf("Success.\n");
exit(0);
}
else
{
perror("pthread_join() reported failure");
exit(-1);
}
}
/* end workerThread */
/********************************************************************/
/* */
/* Function Name: workerThread */
/* */
/* Descriptive Name: Process client connection. */
/********************************************************************/
void *workerThread(void *arg)
{
struct timeval waitTime;
int ioCompPort, clientfd;
Qso_OverlappedIO_t ioStruct;
int rc, tID;
pthread_t thr;
pthread_id_np_t t_id;
t_id = pthread_getthreadid_np();
tID = t_id.intId.lo;
/*********************************************/
/* I/O completion port is passed to this */
/*********************************************/
/* Wait on the supplied I/O completion port */
/* for a client request. */
/*********************************************/
waitTime.tv_sec = 500;
waitTime.tv_usec = 0;
rc = QsoWaitForIOCompletion(ioCompPort, &ioStruct, &waitTime);
if (rc == 1 && ioStruct.returnValue != -1)
/*********************************************/
/* Client request has been received. */
/*********************************************/
;
else
{
printf("QsoWaitForIOCompletion() or QsoStartRecv() failed.\n");
perror("QsoWaitForIOCompletion() or QsoStartRecv() failed");
return __VOID(Failure);
}
/*********************************************/
/* Obtain the socket descriptor associated */
/* with the client connection. */
/*********************************************/
clientfd = *((int *) &ioStruct.descriptorHandle);
/*********************************************/
/* Echo the data back to the client. */
/* Note: postFlag == 0. If write completes */
/* immediate then indication will be */
/* returned, otherwise once the */
/* write is performed the I/O Completion */
/* port will be posted. */
/*********************************************/
ioStruct.postFlag = 0;
ioStruct.bufferLength = ioStruct.returnValue;
rc = QsoStartSend(clientfd, ioCompPort, &ioStruct);
if (rc == 0)
/*********************************************/
/* Operation complete - data has been sent. */
/*********************************************/
;
else
{
/*********************************************/
/* Two possibilities */
/* rc == -1 */
/* Error on function call */
/* rc == 1 */
/* Write could not be immediately */
/* performed. Once complete, the I/O */
/* completion port will be posted. */
/*********************************************/
if (rc == -1)
{
printf("QsoStartSend() failed.\n");
perror("QsoStartSend() failed");
close(clientfd);
return __VOID(Failure);
}
/*********************************************/
/* Wait for operation to complete. */
The following examples explain how to establish a secure server and client using each of these methods.
v Example: GSKit secure server with asynchronous data receive
v Example: GSKit secure server with asynchronous handshake
v Example: Establish a secure client with GSKit APIs
v Example: Establish secure server with SSL APIs
v Example: Establish secure client with SSL APIs
| Note: The following example programs use AF_INET address family, but they can be modified to also use
| the AF_INET6 address family.
| To view the client portion of this graphic, see Secure GSKit client graphic.
| The following sequence of the socket calls provide a description of the graphic. It also describes the
| relationship between the server and client examples. Each set of flows contain links to usage notes on
| specific APIs. If you need more details on the use of a particular API, you can use these links. This flow
| describes the socket calls in the following sample application. .
| 1. The QsoCreateIOCompletionPort() function creates an I/O completion port.
| 2. The pthread_create function creates a worker thread to receive data and to echo it back to the
| client. The worker thread will wait for client requests to arrive on the I/O completion port just created.
| 3. A call to gsk_environment_open() to obtain a handle to an SSL environment.
| 4. One or more calls to gsk_attribute_set_xxxxx() to set attributes of the SSL environment. At a
| minimum, either a call to gsk_attribute_set_buffer() to set the GSK_OS400_APPLICATION_ID value
| or to set the GSK_KEYRING_FILE value. Only one of these should be set. It is prefered that you use
| the GSK_OS400_APPLICATION_ID value. Also ensure you set the type of application (client or
| server), GSK_SESSION_TYPE, using gsk_attribute_set_enum().
| 5. A call to gsk_environment_init() to initialize this environment for SSL processing and to establish the
| SSL security information for all SSL sessions that will run using this environment.
| 6. The socket function creates a socket descriptor. The server then issues the standard set of socket
| calls: bind(), listen(), and accept() to enable a server to accept incoming connection requests.
| 7. The gsk_secure_soc_open() function obtains storage for a secure session, sets default values for
| attributes, and returns a handle that must be saved and used on secure session-related function
| calls.
| 8. One or more calls to gsk_attribute_set_xxxxx() to set attributes of the secure session. At a minimum,
| a call to gsk_attribute_set_numeric_value() to associate a specific socket with this secure session.
| 9. A call to gsk_secure_soc_init() to initiate the SSL handshake negotiation of the cryptographic
| parameters.
| Note: Typically, a server program must provide a certificate for an SSL handshake to succeed. A
| server must also have access to the private key that is associated with the server certificate
| and the key database file where the certificate is stored. In some cases, a client must also
| provide a certificate during the SSL handshake processing. This occurs if the server which the
| client is connecting to has enabled client authentication. The
| gsk_attribute_set_buffer(GSK_OS400_APPLICATION_ID) or
| gsk_attribute_set_buffer(GSK_KEYRING_FILE) API calls identify (though in dissimilar ways)
| the key database file from which the certificate and private key that are used during the
| handshake are obtained.
| 10. The gsk_secure_soc_startRecv() function initiates an asynchronous receive operation on a secure
| session.
| 11. The pthread_join synchronizes the server and worker programs. This function waits for the thread to
| terminate, detaches the thread, then returns the threads exit status to the server.
| 12. The gsk_secure_soc_close() function ends the secure session.
| 13. The gsk_environment_close() function closes the SSL environment.
| 14. The close() function ends the listening socket.
| 15. The close() ends the accepted (client connection) socket.
| 16. The QsoDestroyIOCompletionPort() function destroys the completion port.
| For information on the use of code examples, see the code disclaimer.
| /* GSK Asyncronous Server Program using Application Id*/
|
| /* "IBM grants you a nonexclusive copyright license */
| /* to use all programming code examples from which */
| /* you can generate similar function tailored to your */
| /* own specific needs. */
| /* */
| /* All sample code is provided by IBM for illustrative*/
| /* purposes only. These examples have not been */
| /* thoroughly tested under all conditions. IBM, */
| /* therefore, cannot guarantee or imply reliability, */
| /* serviceability, or function of these programs. */
| /* */
| /* All programs contained herein are provided to you */
| /* "AS IS" without any warranties of any kind. The */
| /* implied warranties of non-infringement, */
| /* merchantability and fitness for a particular */
| /* purpose are expressly disclaimed. " */
|
| /* Assummes that application id is already registered */
| /* and a certificate has been associated with the */
| /* application id. */
| /* No parameters, some comments and many hardcoded */
| /* values to keep it short and simple */
|
| /* use following command to create bound program: */
| /* CRTBNDC PGM(PROG/GSKSERVa) */
| /* SRCFILE(PROG/CSRC) */
| /* SRCMBR(GSKSERVa) */
|
| #include <stdio.h>
| #include <stdlib.h>
| #include <sys/types.h>
| #include <sys/socket.h>
| #include <gskssl.h>
| #include <netinet/in.h>
| #include <arpa/inet.h>
| #include <errno.h>
| #define _MULTI_THREADED
| #include "pthread.h"
| #include "qsoasync.h"
| #define Failure 0
| #define Success 1
| #define TRUE 1
| #define FALSE 0
|
| void *workerThread(void *arg);
| /********************************************************************/
| /* Descriptive Name: Master thread will establish a client */
| /* connection and hand processing responsibility */
| /* to a worker thread. */
| /* Note: Due to the thread attribute of this program, spawn() must */
| /* be used to invoke. */
| /********************************************************************/
| int main(void)
| Socket flow of events: GSKit secure server that uses asynchronous handshake
| This flow describes the socket calls in the following sample application..
| 1. The QsoCreateIOCompletionPort() function creates an I/O completion port.
| 2. The pthread_create function creates a worker thread to process all client requests. the worker thread
| will wait for client requests to arrive on the I/O completion port just created.
| 3. A call to gsk_environment_open() to obtain a handle to an SSL environment.
| 4. One or more calls to gsk_attribute_set_xxxxx() to set attributes of the SSL environment. At a
| minimum, either a call to gsk_attribute_set_buffer() to set the GSK_OS400_APPLICATION_ID value
| or to set the GSK_KEYRING_FILE value. Only one of these should be set. It is prefered that you use
| the GSK_OS400_APPLICATION_ID value. Also ensure you set the type of application (client or
| server), GSK_SESSION_TYPE, using gsk_attribute_set_enum().
| 5. A call to gsk_environment_init() to initialize this environment for SSL processing and to establish the
| SSL security information for all SSL sessions that will run using this environment.
| 6. The socket function creates a socket descriptor. The server then issues the standard set of socket
| calls: bind(), listen(), and accept() to enable a server to accept incoming connection requests.
| 7. The gsk_secure_soc_open() function obtains storage for a secure session, sets default values for
| attributes, and returns a handle that must be saved and used on secure session-related function
| calls.
| 8. One or more calls to gsk_attribute_set_xxxxx() to set attributes of the secure session. At a minimum,
| a call to gsk_attribute_set_numeric_value() to associate a specific socket with this secure session.
| 9. The gsk_secure_soc_startInit() function starts an asynchronous negotiation of a secure session,
| using the attributes set for the SSL environment and the secure session. Control will return to the
| program here. When the handshake processing completes, the completion port will be posted with
| the results. The thread could continue on with other processing; however, for simplicity we have
| chosen to wait here for worker thread to complete.
| Note: Typically, a server program must provide a certificate for an SSL handshake to succeed. A
| server must also have access to the private key that is associated with the server certificate
| and the key database file where the certificate is stored. In some cases, a client must also
| provide a certificate during the SSL handshake processing. This occurs if the server which the
| client is connecting to has enabled client authentication. The
| gsk_attribute_set_buffer(GSK_OS400_APPLICATION_ID) or
| gsk_attribute_set_buffer(GSK_KEYRING_FILE) API calls identify (though in dissimilar ways)
| the key database file from which the certificate and private key that are used during the
| handshake are obtained.
| 10. The pthread_join synchronizes the server and worker programs. This function waits for the thread to
| terminate, detaches the thread, then returns the threads exit status to the server.
| 11. The gsk_secure_soc_close() function ends the secure session.
| 12. The gsk_environment_close() function closes the SSL environment.
| 13. The close() function ends the listening socket.
| 14. The close() ends the accepted (client connection) socket.
| 15. The QsoDestroyIOCompletionPort() function destroys the completion port.
| Socket flow of events: Worker thread that process secure asynchronous requests
| For information on the use of code examples, see the code disclaimer.
| /* GSK Asyncronous Server Program using Application Id*/
| /* and gsk_secure_soc_startInit() */
|
| /* Assummes that application id is already registered */
| /* and a certificate has been associated with the */
| /* application id. */
| /* No parameters, some comments and many hardcoded */
| /* values to keep it short and simple */
|
| /* use following command to create bound program: */
| /* CRTBNDC PGM(MYLIB/GSKSERVSI) */
| /* SRCFILE(MYLIB/CSRC) */
| /* SRCMBR(GSKSERVSI) */
|
| #include <stdio.h>
| #include <stdlib.h>
| #include <sys/types.h>
| #include <sys/socket.h>
| #include <gskssl.h>
| #include <netinet/in.h>
| #include <arpa/inet.h>
| #include <errno.h>
| #define _MULTI_THREADED
| #include "pthread.h"
| #include "qsoasync.h"
| #define Failure 0
| #define Success 1
| #define TRUE 1
| #define FALSE 0
|
| void *workerThread(void *arg);
| /********************************************************************/
| /* Descriptive Name: Master thread will establish a client */
| /* connection and hand processing responsibility */
| /* to a worker thread. */
| /* Note: Due to the thread attribute of this program, spawn() must */
| /* be used to invoke. */
| /********************************************************************/
| int main(void)
| {
| gsk_handle my_env_handle=NULL; /* secure environment handle */
| gsk_handle my_session_handle=NULL; /* secure session handle */
|
| struct sockaddr_in address;
| int buf_len, on = 1, rc = 0;
| int sd = -1, lsd = -1, al, ioCompPort = -1;
| int successFlag = FALSE;
| pthread_t thr;
| void *status;
| Qso_OverlappedIO_t ioStruct;
|
| /*********************************************/
| /* Issue all of the command in a do/while */
| /* loop so that clean up can happen at end */
| The following graphic shows the function calls on a secure client using the GSKit APIs:
| This flow describes the socket calls in the following sample application. Use this client example with the
| the GSKit server example and the Example: GSKit secure server with asynchronous handshake .
| 1. The gsk_environment_open() function obtains a handle to an SSL environment.
| 2. One or more calls to gsk_attribute_set_xxxxx() to set attributes of the SSL environment. At a
| minimum, either a call to gsk_attribute_set_buffer() to set the GSK_OS400_APPLICATION_ID value
| or to set the GSK_KEYRING_FILE value. Only one of these should be set. It is prefered that you use
| the GSK_OS400_APPLICATION_ID value. Also ensure you set the type of application (client or
| server), GSK_SESSION_TYPE, using gsk_attribute_set_enum().
| 3. A call to gsk_environment_init() to initialize this environment for SSL processing and to establish the
| SSL security information for all SSL sessions that will run using this environment.
| 4. The socket function creates a socket descriptor. The client then issues the connect() to connect to
| the server application.
| 5. The gsk_secure_soc_open() function obtains storage for a secure session, sets default values for
| attributes, and returns a handle that must be saved and used on secure session-related function
| calls.
| 6. The gsk_attribute_set_numeric_value() function associates a specific socket with this secure
| session.
| 7. The gsk_secure_soc_init() function starts an asynchronous negotiation of a secure session, using
| the attributes set for the SSL environment and the secure session.
| 8. The gsk_secure_soc_write() function writes data on a secure session to the worker thread.
| Note: For the GSKit server example, this function writes data to the worker thread where the
| gsk_secure_soc_startRecv() function completes. In the asynchronous example, it writes to
| the completed gsk_secure_soc_startInit() .
| 9. The gsk_secure_soc_read() function receives a message from the worker thread using the secure
| session.
| 10. The gsk_secure_soc_close() function ends the secure session.
| 11. The gsk_environment_close() function closes the SSL environment.
| 12. The close() function ends the connection.
| /* GSK Client Program using Application Id */
|
| /* This program assumes that the application id is */
| /* already registered and a certificate has been */
| /* associated with the appliciation id */
| /* */
| /* No parameters, some comments and many hardcoded */
| /* values to keep it short and simple */
|
| /* use following command to create bound program: */
| /* CRTBNDC PGM(MYLIB/GSKCLIENT) */
| /* SRCFILE(MYLIB/CSRC) */
| /* SRCMBR(GSKCLIENT) */
|
| #include <stdio.h>
| #include <sys/types.h>
| #include <sys/socket.h>
| #include <gskssl.h>
| #include <netinet/in.h>
| #include <arpa/inet.h>
| #include <errno.h>
| #define TRUE 1
| #define FALSE 0
|
| void main(void)
| {
The following description shows the relationship between the APIs that enable an SSL server to perform
and communicate with an SSL client:
1. Either call SSL_Init() or SSL_Init_Application() to initialize the job environment for SSL processing
and to establish the SSL security information for all SSL sessions that will run in the current job. Only
one of these APIs should be used. It is preferred that you use the SSL_Init_Application() API.
Note: The sample uses AF_INET address family; however, it can be modified to use the AF_INET6
address family.
For information on the use of code examples, see the code disclaimer.
/* SSL Server Program using SSL_Init_Application */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ssl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
void main(void)
{
SSLHandle *sslh;
SSLInitApp sslinit;
return;
}
For information on the use of code examples, see the code disclaimer.
/* SSL Client Program using SSL_Init_Application */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/socket.h>
#include <ssl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
/* hardcoded IP address */
char addr[12] = "16.35.146.84";
/* initialize a socket */
sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0)
{
perror("socket() failed");
return;
}
/* receive the message from the server using the secure session */
rc = SSL_Read(sslh, buff2, buf_len);
if (rc < 0)
{
printf("SSL_Read() failed with rc = %d.\n",rc);
SSL_Destroy(sslh);
close(sd);
return;
}
For information on the use of code examples, see the code disclaimer.
/********************************************************/
/* Header files */
/********************************************************/
#include </netdb.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define HEX00 ’\x00’
#define NUMPARMS 2
/********************************************************/
/* Pass one parameter that is the IP address in */
/* dotted decimal notation. The host name will be */
/* displayed if found; otherwise, a message states */
/* host not found. */
/********************************************************/
int main(int argc, char *argv[])
{
int rc;
struct in_addr internet_address;
struct hostent hst_ent;
struct hostent_data hst_ent_data;
char dotted_decimal_address [16];
char host_name[MAXHOSTNAMELEN];
/**********************************************************/
/* Verify correct number of arguments have been passed */
/**********************************************************/
if (argc != NUMPARMS)
{
printf("Wrong number of parms passed\n");
exit(-1);
}
/**********************************************************/
/* Obtain addressability to parameters passed */
/**********************************************************/
strcpy(dotted_decimal_address, argv[1]);
/**********************************************************/
/* Initialize the structure-field */
/* hostent_data.host_control_blk with hexadecimal zeros */
/* before its initial use. If you require compatibility */
/* with other platforms, then you must initialize the */
/* entire hostent_data structure with hexadecimal zeros. */
/**********************************************************/
/* Initialize to hex 00 hostent_data structure */
/**********************************************************/
memset(&hst_ent_data,HEX00,sizeof(struct hostent_data));
/**********************************************************/
/* Obtain host name */
/**********************************************************/
/**********************************************************/
/* NOTE: The gethostbyaddr_r() returns an integer. */
/* The following are possible values: */
/* -1 (unsuccessful call) */
/* 0 (successful call) */
/**********************************************************/
rc=gethostbyaddr_r((char *) &internet_address,
sizeof(struct in_addr), AF_INET,
&hst_ent, &hst_ent_data);
if (rc== -1)
{
printf("Host name not found\n");
exit(-1);
}
else
{
/*****************************************************/
/* Copy the host name to an output buffer */
/*****************************************************/
(void) memcpy((void *) host_name,
/****************************************************/
/* You must address all the results through the */
/* hostent structure hst_ent. */
/* NOTE: Hostent_data structure hst_ent_data is just */
/* a data repository that is used to support the */
/* hostent structure. Applications should consider */
/* hostent_data a storage area to put host level data */
/* that the application does not need to access. */
/****************************************************/
(void *) hst_ent.h_name,
MAXHOSTNAMELEN);
/*****************************************************/
/* Print the host name */
/*****************************************************/
printf("The host name is %s\n", host_name);
}
exit(0);
}
For information on the use of code examples, see the code disclaimer.
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <errno.h>
#define TRUE 1
#define FALSE 0
/*************************************************************/
/* Create an AF_INET stream socket to receive incoming */
/* connections on */
/*************************************************************/
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}
/*************************************************************/
/* Allow socket descriptor to be reuseable */
/*************************************************************/
rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof(on));
if (rc < 0)
{
perror("setsockopt() failed");
close(listen_sd);
exit(-1);
}
/*************************************************************/
/* Bind the socket */
/*************************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(SERVER_PORT);
rc = bind(listen_sd,
(struct sockaddr *)&addr, sizeof(addr));
if (rc < 0)
{
perror("bind() failed");
close(listen_sd);
exit(-1);
}
/*************************************************************/
/* Set the listen back log */
/*************************************************************/
rc = listen(listen_sd, 32);
if (rc < 0)
{
perror("listen() failed");
close(listen_sd);
exit(-1);
}
/*************************************************************/
/* Initialize the master fd_set */
/*************************************************************/
FD_ZERO(&master_set);
max_sd = listen_sd;
FD_SET(listen_sd, &master_set);
/*************************************************************/
/* Initialize the timeval struct to 3 minutes. If no */
/* activity after 3 minutes this program will end. */
/*************************************************************/
timeout.tv_sec = 3 * 60;
timeout.tv_usec = 0;
/*************************************************************/
/* Loop waiting for incoming connects or for incoming data */
/* on any of the connected sockets. */
/*************************************************************/
do
{
/**********************************************************/
/* Copy the master fd_set over to the working fd_set. */
/**********************************************************/
memcpy(&working_set, &master_set, sizeof(master_set));
/**********************************************************/
/**********************************************************/
/* Check to see if the select call failed. */
/**********************************************************/
if (rc < 0)
{
perror(" select() failed");
break;
}
/**********************************************************/
/* Check to see if the 5 minute time out expired. */
/**********************************************************/
if (rc == 0)
{
printf(" select() timed out. End program.\n");
break;
}
/**********************************************************/
/* One or more descriptors are readable. Need to */
/* determine which ones they are. */
/**********************************************************/
desc_ready = rc;
for (i=0; i <= max_sd && desc_ready > 0; ++i)
{
/*******************************************************/
/* Check to see if this descriptor is ready */
/*******************************************************/
if (FD_ISSET(i, &working_set))
{
/****************************************************/
/* A descriptor was found that was readable - one */
/* less has to be looked for. This is being done */
/* so that we can stop looking at the working set */
/* once we have found all of the descriptors that */
/* were ready. */
/****************************************************/
desc_ready -= 1;
/****************************************************/
/* Check to see if this is the listening socket */
/****************************************************/
if (i == listen_sd)
{
printf(" Listening socket is readable\n");
/*************************************************/
/* Accept all incoming connections that are */
/* queued up on the listening socket before we */
/* loop back and call select again. */
/*************************************************/
do
{
/**********************************************/
/* Accept each incoming connection. If */
/* accept fails with EWOULDBLOCK, then we */
/* have accepted all of them. Any other */
/* failure on accept will cause us to end the */
/* server. */
/**********************************************/
new_sd = accept(listen_sd, NULL, NULL);
if (new_sd < 0)
{
/**********************************************/
/* Add the new incoming connection to the */
/* master read set */
/**********************************************/
printf(" New incoming connection - %d\n", new_sd);
FD_SET(new_sd, &master_set);
if (new_sd > max_sd)
max_sd = new_sd;
/**********************************************/
/* Loop back up and accept another incoming */
/* connection */
/**********************************************/
} while (new_sd != -1);
}
/****************************************************/
/* This is not the listening socket, therefore an */
/* existing connection must be readable */
/****************************************************/
else
{
printf(" Descriptor %d is readable\n", i);
close_conn = FALSE;
/*************************************************/
/* Receive all incoming data on this socket */
/* before we loop back and call select again. */
/*************************************************/
do
{
/**********************************************/
/* Receive data on this connection until the */
/* recv fails with EWOULDBLOCK. If any other */
/* failure occurs, we will close the */
/* connection. */
/**********************************************/
rc = recv(i, buffer, sizeof(buffer), 0);
if (rc < 0)
{
if (errno != EWOULDBLOCK)
{
perror(" recv() failed");
close_conn = TRUE;
}
break;
}
/**********************************************/
/* Check to see if the connection has been */
/* closed by the client */
/**********************************************/
if (rc == 0)
{
printf(" Connection closed\n");
close_conn = TRUE;
break;
}
/**********************************************/
/**********************************************/
/* Echo the data back to the client */
/**********************************************/
rc = send(i, buffer, len, 0);
if (rc < 0)
{
perror(" send() failed");
close_conn = TRUE;
break;
}
} while (TRUE);
/*************************************************/
/* If the close_conn flag was turned on, we need */
/* to clean up this active connection. This */
/* clean up process includes removing the */
/* descriptor from the master set and */
/* determining the new maximum descriptor value */
/* based on the bits that are still turned on in */
/* the master set. */
/*************************************************/
if (close_conn)
{
close(i);
FD_CLR(i, &master_set);
if (i == max_sd)
{
while (FD_ISSET(max_sd, &master_set) == FALSE)
max_sd -= 1;
}
}
} /* End of existing connection is readable */
} /* End of if (FD_ISSET(i, &working_set)) */
} /* End of loop through selectable descriptors */
/*************************************************************/
/* Cleanup all of the sockets that are open */
/*************************************************************/
for (i=0; i <= max_sd; ++i)
{
if (FD_ISSET(i, &master_set))
close(i);
}
}
| Note: Asynchronous I/O used in a threaded server model is preferable over the more conventional model.
| For more information on advantages of using Asynchronous I/O, see Asynchronous I/O. For a
| sample program that uses the asynchronous I/O APIs, see Example: Using asynchronous I/O.
For information on the use of code examples, see the code disclaimer.
/******************************************************************/
/* Example shows how to set alarms for blocking socket APIs */
/******************************************************************/
/******************************************************************/
/* Include files */
/******************************************************************/
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
/******************************************************************/
/******************************************************************/
/* Main program */
/******************************************************************/
int main(int argc, char *argv[])
{
struct sigaction sact;
struct sockaddr_in addr;
time_t t;
int sd, rc;
/******************************************************************/
/* Create an AF_INET, SOCK_STREAM socket */
/******************************************************************/
printf("Create a TCP socket\n");
sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd == -1)
{
perror(" socket failed");
return(-1);
}
/******************************************************************/
/* Bind the socket. A port number was not specified because */
/* we are not going to ever connect to this socket. */
/******************************************************************/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
printf("Bind the socket\n");
rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
if (rc != 0)
{
perror(" bind failed");
close(sd);
return(-2);
}
/******************************************************************/
/* Perform a listen on the socket. */
/******************************************************************/
printf("Set the listen backlog\n");
rc = listen(sd, 5);
if (rc != 0)
{
perror(" listen failed");
close(sd);
return(-3);
}
/******************************************************************/
/* Set up an alarm that will go off in 5 seconds. */
/******************************************************************/
printf("\nSet an alarm to go off in 5 seconds. This alarm will cause the\n");
printf("blocked accept() to return a -1 and an errno value of EINTR.\n\n");
sigemptyset(&sact.sa_mask);
sact.sa_flags = 0;
sact.sa_handler = catcher;
sigaction(SIGALRM, &sact, NULL);
alarm(5);
/******************************************************************/
/* Call accept. This call will normally block indefinitely, */
/* but because we have an alarm set, it will only block for */
/* 5 seconds. When the alarm goes off, the accept call will */
/* complete with -1 and an errno value of EINTR. */
/******************************************************************/
errno = 0;
printf(" Wait for an incoming connection to arrive\n");
rc = accept(sd, NULL, NULL);
printf(" accept() completed. rc = %d, errno = %d\n", rc, errno);
if (rc >= 0)
{
printf(" Incoming connection was recevied\n");
close(rc);
}
else
{
perror(" errno string");
}
/******************************************************************/
/* Show what time it was when the alarm went off */
/******************************************************************/
time(&t);
printf("After accept(), time is %s\n", ctime(&t));
close(sd);
return(0);
}
An application program can send or receive multicast datagrams by using the socket() API and
connectionless SOCK_DGRAM type sockets. Multicasting is a one-to-many transmission method. You
cannot use connection-oriented sockets of type SOCK_STREAM for multicasting. When a socket of type
SOCK_DGRAM is created, an application can use the setsockopt() function to control the multicast
characteristics associated with that socket. The setsockopt() function accepts the following IPPROTO_IP
level flags:
v IP_ADD_MEMBERSHIP: Joins the multicast group specified.
v IP_DROP_MEMBERSHIP: Leaves the multicast group specified.
v IP_MULTICAST_IF: Sets the interface over which outgoing multicast datagrams are sent.
v IP_MULTICAST_TTL: Sets the Time To Live (TTL) in the IP header for outgoing multicast datagrams.
v IP_MULTICAST_LOOP: Specifies whether or not a copy of an outgoing multicast datagram is delivered
to the sending host as long as it is a member of the multicast group.
Note: IP_ADD_MEMBERSHIP option must be called for each local interface over which the multicast
datagrams are to be received.
5. The read() function reads multicast datagrams that are being sent..
6. The close() function closes any open socket descriptors.
For information on the use of code examples, see the code disclaimer.
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
/* ------------------------------------------------------------*/
/* */
/* Send Multicast Datagram code example. */
/* */
/* ------------------------------------------------------------*/
/*
* Create a datagram socket on which to send.
*/
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("opening datagram socket");
exit(1);
}
/*
* Initialize the group sockaddr structure with a
* group address of 225.1.1.1 and port 5555.
*/
/*
* Disable loopback so you do not receive your own datagrams.
*/
{
char loopch=0;
/*
* Set local interface for outbound multicast datagrams.
* The IP address specified must be associated with a local,
* multicast-capable interface.
*/
localInterface.s_addr = inet_addr("9.5.1.1");
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
(char *)&localInterface,
sizeof(localInterface)) < 0) {
perror("setting local interface");
exit(1);
}
/*
* Send a message to the multicast group specified by the
* groupSock sockaddr structure.
*/
datalen = 10;
if (sendto(sd, databuf, datalen, 0,
(struct sockaddr*)&groupSock,
sizeof(groupSock)) < 0)
{
perror("sending datagram message");
}
}
For information on the use of code examples, see the code disclaimer.
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
/* ------------------------------------------------------------*/
/* */
/* Receive Multicast Datagram code example. */
/* */
/* ------------------------------------------------------------*/
/*
* Create a datagram socket on which to receive.
*/
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
perror("opening datagram socket");
exit(1);
}
/*
* Enable SO_REUSEADDR to allow multiple instances of this
* application to receive copies of the multicast datagrams.
*/
{
int reuse=1;
/*
* Bind to the proper port number with the IP address
* specified as INADDR_ANY.
*/
memset((char *) &localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(5555);;
localSock.sin_addr.s_addr = INADDR_ANY;
/*
* Join the multicast group 225.1.1.1 on the local 9.5.1.1
* interface. Note that this IP_ADD_MEMBERSHIP option must be
* called for each local interface over which the multicast
* datagrams are to be received.
*/
group.imr_multiaddr.s_addr = inet_addr("225.1.1.1");
group.imr_interface.s_addr = inet_addr("9.5.1.1");
if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&group, sizeof(group)) < 0) {
perror("adding multicast group");
close(sd);
exit(1);
}
/*
For information on the use of code examples, see the code disclaimer.
/**************************************************************************/
/* This program updates a DNS using a transaction signature (TSIG) to */
/* sign the update packet. It then queries the DNS to verify success. */
/**************************************************************************/
/**************************************************************************/
/* Header files needed for this sample program */
/**************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <netdb.h>
/**************************************************************************/
/* Declare update records - a zone record, a pre-requisite record, and */
/* 2 update records */
/**************************************************************************/
ns_updrec update_records[] =
{
{
{NULL,&update_records[1]},
{NULL,&update_records[1]},
ns_s_zn, /* a zone record */
"mydomain.ibm.com.",
ns_c_in,
ns_t_soa,
0,
NULL,
0,
0,
NULL,
NULL,
0
},
{
{&update_records[0],&update_records[2]},
{&update_records[0],&update_records[2]},
ns_s_pr, /* pre-req record */
"mypc.mydomain.ibm.com.",
ns_c_in,
ns_t_a,
0,
NULL,
0,
ns_r_nxdomain, /* record must not exist */
NULL,
NULL,
0
/**************************************************************************/
/* These two structures define a key and secret that must match the one */
/* configured on the DNS : */
/* allow-update { */
/* key my-long-key.; */
/* } */
/* */
/* This must be the binary equivalent of the base64 secret for */
/* the key */
/**************************************************************************/
unsigned char secret[18] =
{
0x6E,0x86,0xDC,0x7A,0xB9,0xE8,0x86,0x8B,0xAA,
0x96,0x89,0xE1,0x91,0xEC,0xB3,0xD7,0x6D,0xF8
};
ns_tsig_key my_key = {
"my-long-key", /* This key must exist on the DNS */
NS_TSIG_ALG_HMAC_MD5,
secret,
sizeof(secret)
};
void main()
{
/***********************************************************************/
/* Variable and structure definitions. */
/***********************************************************************/
struct state res;
int result, update_size;
unsigned char update_buffer[2048];
unsigned char answer_buffer[2048];
int buffer_length = sizeof(update_buffer);
result = res_ninit(&res);
{
char zone_name[NS_MAXDNAME];
size_t zone_name_size = sizeof zone_name;
struct sockaddr_in s_address;
struct in_addr addresses[1];
int number_addresses = 1;
/* Check if the DNS server found is one of our regular DNS addresses */
s_address.sin_addr = addresses[0];
s_address.sin_family = res.nsaddr_list[0].sin_family;
s_address.sin_port = res.nsaddr_list[0].sin_port;
memset(s_address.sin_zero, 0x00, 8);
/* Set the DNS address found with res_findzonecut into the res */
/* structure. We will send the (TSIG signed) update to that DNS. */
res.nscount = 1;
res.nsaddr_list[0] = s_address;
/***********************************************************************/
/* The res_findzonecut(), res_nmkupdate(), and res_nsendsigned() */
/* could be replaced with one call to res_nupdate() using */
/* update_records[1] to skip the zone record: */
/* */
/* result = res_nupdate(&res, &update_records[1], &my_key); */
/* */
/***********************************************************************/
/***********************************************************************/
/* Now verify that our update actually worked! */
/* We choose to use TCP and not UDP, so set the appropriate option now */
/* that the res variable has been initialized. We also want to ignore */
/* the local cache and always send the query to the DNS server. */
/***********************************************************************/
| For information on the use of code examples, see the code disclaimer.
| /*************************************************/
| /* Server example send file data to client */
| /*************************************************/
|
| #include <stdio.h>
| #include <stdlib.h>
| #include <errno.h>
| #include <fcntl.h>
| #include <sys/socket.h>
| #include <netinet/in.h>
|
| #define SERVER_PORT 12345
|
| main (int argc, char *argv[])
| {
| int i, num, rc, flag = 1;
| int fd, listen_sd, accept_sd = -1;
|
| size_t local_addr_length;
| size_t remote_addr_length;
| size_t total_sent;
|
| struct sockaddr_in addr;
| struct sockaddr_in local_addr;
| struct sockaddr_in remote_addr;
| struct sf_parms parms;
|
| char buffer[255];
|
| For information on the use of code examples, see the code disclaimer.
| /*************************************************/
| /* Client example requests file data from server */
| /*************************************************/
| #include <ctype.h>
| #include <stdio.h>
| #include <stdlib.h>
| #include <netdb.h>
| #include <sys/socket.h>
| #include <netinet/in.h>
| #include <arpa/inet.h>
|
| #define SERVER_PORT 12345
|
| main (int argc, char *argv[])
| {
| int rc, sockfd;
|
| char filename[256];
| char buffer[32 * 1024];
|
| struct sockaddr_in addr;
| struct hostent *host_ent;
|
| /*************************************************/
| /* Initialize the socket address structure */
| /*************************************************/
| memset(&addr, 0, sizeof(addr));
| addr.sin_family = AF_INET;
| Use the following topics to install and use the Xsocket tool:
| Configure Xsockets
| This topic explains how to create the Xsockets tool, which helps you design and compile your socket
| programs.
| Use Xsockets
| This topic explains how to use the Xsockets tool.
| Customize Xsockets
| This topic explains how to customize the Xsockets tool.
|
| Configure Xsockets
| There are two versions of the tool that can be created. The first version is native iSeries client. The native
| version is completely created by the first set of instructions. The second version uses a web browser as
| the client. If you want to use the web browser client, you must complete native setup instructions for the
| native version first.
| on a command line.
| on a command line.
| 3. Create a library in which to create the Xsocket program files by entering
| CRTLIB <library-name>
| Note: Do not add Xsocket tool objects to the QUSRTOOL library. This could interfere with the use of
| other tools within that directory.
| 4. To add this library to the library list, enter
| ADDLIBLE <library-name>
| on the command line. The<library-name> is the the library that you created in Step 3. For example,
| we used MYXSOCKET as the library name, then we would enter:
| ADDLIBLE MYXSOCKET
| 5. Create the install program TSOCRT that will automatically install the Xsockets tool by entering:
| CRTCLPGM <library-name>/TSOCRT QUSRTOOL/QATTCL
| on the command line. The<library-name> is the the library that you created in Step 3. For example,
| we used MYXSOCKET as the library name, then we would enter:
| CRTCLPGM MYXSOCKET/TSOCRT QUSRTOOL/QATTCL
| 6. To call the install program, enter:
| CALL TSOCRT library-name
| on the command line. In the place of library-name, use the library you created in Step 3. For example,
| to create the tool in the MYXSOCKET library, enter:
| CALL TSOCRT MYXSOCKET
| If you do not have job control (*JOBCTL) special authority when you call TSOCRT to create the sockets
| tool the givedescriptor() socket function will return errors when an attempt is made to pass a descriptor to
| a job which is not the one you are running.
| TSOCRT will create a CL program, an ILE C/400 program (2 modules are created), 2 ILE C/400 service
| programs (2 modules are created), and three display files.Whenever you want to use the tool, you should
| add the library to your library list. All objects created by the tool will have a name that is prefixed by TSO.
| What to do next:
| Note: The native version does not support GSKit secure socket APIs. If you want to write socket
| programs that use these APIs, you should use the browser-based version of the tool.
| where <system_name>is the machine name of the iSeries. For example: http://myiSeries:2001/.
| 3. On the iSeries Tasks page, select IBM HTTP Server for iSeries.
| 4. From the top menu, select the Setup tab.
| 5. Click Create New HTTP Server.
| 6. Select HTTP server (powered by Apache), and click Next.
| 7. Enter the name for the server instance. For example, since this instance will serve the Xsocket tool in
| a browser, you could use the name xsocket. Click Next.
| 8. Select No. This will create a new server instance that is not based on an existing server. Click Next.
| 9. Click Next to accept the default for the server root directory.
| 10. Click Next to accept the default for the document root directory.
| 11. Select the IP address and an available port that you would like to use. Use a port number greater
| than 1024. Click Next.
| Configure Tomcat
| Configure Tomcat
| After you have configured HTTP server (powered by Apache) server instance, you must configure Tomcat
| to launch the Xsockets tool in a web browser.
| 1. Under the Dynamic content heading, select ASF Tomcat Setup Server Task .
| 2. Select Enable servlets for this HTTP Server. This will fill in a Workers definition file for you. Click
| Next.
| 3. Accept the defaults on the Workers Definition page by clicking Next.
| 4. On the URL to Worker Mapping page, click Add.
| 5. In the URL(Mount Point) column, enter /xsock. Click Continue.
| 6. Click Add.
| 7. In the URL(Mount Point) column, enter /xsock/*. Click Continue.
| 8. Click Next.
| 9. On the In-Process Application Context Definition page, click Add.
| 10. In the URL Path column, enter /xsock.
| 11. In the Application Base Directory column, enter webapps/xsock.
| 12. Click Continue. You will see a warning message that you need to configure more information.
| 13. Click Configure under Configure Application.
| 14. In the new browser window that opens, select 3 days in the Session Object timeout field.
| Note: This is the recommended value; however, you can specify another value for the Session
| Object timeout.
| 15. Click Add to add Servlet definition and complete the following steps:
| What to do next:
| where <server_name> is the name of the server instance you created during Apache configuration.
| For example, if you chose xsocks for the server name, you would enter:
| wrklnk ’/www/xsocks/webapps/xsock/WEB-INF/web.xml’
| b. Press 2 to edit the file.
| c. Find the </servlet-class> line in the web.xml file.
| d. Insert the following code after this line:
| <init-param>
| <param-name>library</param-name>
| <param-value>XXXX</param-value>
| </init-param>
| In place of the XXXX, insert the library name that you created during Xsockets configuration.
| e. Save the file and exit the edit session.
| 2. Move JAR file
| a. From a command line, enter this command:
| CPY OBJ(’/QSYS.LIB/XXXX.LIB/QATTIFS2.FILE/TSOXSOCK.MBR’)
| TOOBJ(’/www/<server_name>/webapps/xsock/WEB-INF/lib/tsoxsock.jar’)
| FROMCCSID(*OBJ) TOCCSID(819) OWNER(*NEW)
| where XXXX is the library name that you created during Xsockets configuration and
| <server_name> is the name of the server instance you created during HTTP server (powered by
| Apache) configuration.
| 3. Add authority check to httpd.conf file (This step is optional.)
| This will force Apache to authenticate users trying to access the Xsockets web application.
| Note: It is also necessary for getting write access to create UNIX sockets.
| where <server_name> is the name of the server instance you created during Apache configuration.
| For example, if you chose xsocks for the server name, you would enter:
| wrklnk ’/www/xsocks/conf/httpd.conf’’
| b. Press 2 to edit the file.
| c. Insert the following lines at the end of the file.
| <Location /xsock>
| AuthName "X Socket"
| AuthType Basic
| PasswdFile %%SYSTEM%%
| UserId %%CLIENT%%
| Require valid-user
| order allow,deny
| allow from all
| </Location>
| d. Save the file and exit the edit session.
| What to do next
| where <server_name> is the name of the server instance you created during Apache configuration.
| where <system_name> and <port> are the server instance name and port number that you chose
| during Apache configuration.
| 4. When prompted, enter your username and password for the server. The web client for Xsocket should
| appear.
|
| Use Xsockets
| There are now two ways to work with the Xsockets tool. You can work with the tool from the native client
| or work with the tool in a web browser. To work with a native version of Xsocket, you must configure the
| Xsockets tool. In addition to configuring the Xsockets tool for a native client, you must also complete the
| steps in Configure Xsockets to use a web browser if you prefer to work with the tool in a browser
| environment. Many of the concepts are similar between the two versions of the tools. Both tools allow you
| to issue socket calls interactively and both tools provide errnos for issued socket calls; however, the
| interfaces do have some differences. The following sets of instructions show you how to work with the
| Xsocket tool in both environments.
| The following sets of instruction describes how to work with each of these tools:
| v Use native Xsockets
| v Use browser-based Xsockets
| where the <library-name> is the name of the library you created during native Xsockets configuration.
| For example, if the name of the library is MYXSOCKET, then enter:
| ADDLIBLE MYXSOCKET
| 2. On a command line interface, type
| CALL TSOXSOCK
| 3. In the Xsocket window that displays allows you to access all socket routines through its menu bar and
| selection field. This window always displays after you choose a socket function. You can use this
| interface to select socket programs that already exists. To work with a new socket, complete the
| following:
| a. In the list of socket functions, select socket and press Enter.
| b. In the socket() prompt window that displays, select the appropriate Address Family, Socket Type,
| and Protocol for the socket, and press Enter.
| c. Select Descriptor and select Select descriptor.
| Note: If other socket descriptors already exist, this will display a list of active socket descriptors.
| d. From the list that displays, select the socket descriptor that you created
| Note: If other socket descriptors exist, the tool will automatically apply a socket function to the
| latest socket descriptor.
| 4. From the list of socket functions, select a socket function with which you would like to work. Whatever
| socket descriptor you chose in Step 3c will be used on that socket function. Once you select a socket
| function, a series of windows display that you can provide specific information on the socket function.
| For example, if you select connect(), you will need to provide the address length, address family, and
| address data in the resulting windows. The socket function chosen is then called with this information
| that you provided. Any errors that occur on a socket function will be displayed back to the user as an
| errno.
| Notes:
| 1. The Xsockets tool uses the graphical look support for DDS.Thus, how data is entered and how
| selections are made from the windows/panels you see will be dependent on whether you are using a
| graphical display station or a nongraphical display station. For example, on a graphical display station,
| you will see the selection field for the socket functions as a check box; otherwise, you might see a
| single field.
| 2. Be aware that there are ioctl() requests that are available on a socket which have not been
| implemented in the tool.
| where server-name is the name of the iSeries that contains the server instance.
| 2. Select Administration.
| 3. From the left navigation, select Manage HTTP Servers.
| 4. Select your instance name, and click Start. You can also start the server instance from a command
| line by entering:
| STRTCPSVR SERVER(*HTTP) HTTPSVR(<instance_name>)
| where <instance_name> is the name of your HTTP server created in the Apache configuration. For
| example, you could use the server instance name xsocks.
| 5. To access the Xsockets web application, enter this URL in a browser:
| http://<system_name>:<port>/xsock/index
| where <system_name> is the machine name of the iSeries and <port> is the port specified when you
| created the HTTP instance. For example, if the system name is myiSeries and the HTTP server
| instance listens on port 1025, you would enter:
| http://myiSeries:1025/xsock/index
| 6. Once the Xsocket tool loads in the web browser, you can work with existing socket descriptor or create
| new onces. Many of the concepts are similar between the two versions of the tools Both tools allow
| you to issue socket calls interactively and both tools provide errnos for issued socket calls, however
| the interfaces do have some differences. To create a new socket descriptor, you can do the following:
| a. From the Xsocket Menu, select socket.
| b. In the Xsocket Query window that displays, select the appropriate Address Family, Socket Type,
| and Protocol for this socket discriptor. Click Submit.
| c. Once the page reloads, the new socket descriptor will be displayed in the Socket pull-down menu.
| d. From the Xsocket Menu, select function calls that you would like applied to this socket descriptor.
| As with the native version of the Xsockets tool, the tool will automatically apply function calls to the
| latest socket descriptor if you do not select a socket descriptor.
|
| Delete objects created by the Xsocket tool
| You may need to delete objects that are created by the Xsockets tool. The program named TSODLT is
| created by the install program to remove the objects created by the tool (except the library and the
| program TSODLT ) and/or remove the source members used by the Xsocket tool. The following set of
| commands allow you to delete these objects:
| To delete ONLY the source members used by the tool, enter the following command :
| CALL TSODLT (*YES *NONE)
| To delete ONLY objects that the tool creates, enter the following command:.
| CALL TSODLT (*NO library-name)
| To delete BOTH source members and objects created by the tool, enter the following command:
| CALL TSODLT (*YES library-name)
The following table gives an overview for the each of these service tools.
Table 21. Serviceability tools for Socket and Secure sockets
Serviceability tool Description
| LIC Trace Filtering (TRCINT and TRCCNN) Provides selective trace on sockets. You can now restrict
sockets trace on address family, socket type, protocol, IP
address, and port information. You can also limit traces to
only certain categories of socket APIs and also to only
those sockets that have the SO_DEBUG socket option
set. New for V5R2, a LIC trace can now be filtered by
thread, task, user profile, job name, or server name.
| Trace job with STRTRC SSNID(*GEN) New for V5R2, STRTRC command provides additional
| JOBTRCTYPE(*TRCTYPE) TRCTYPE((*SOCKETS parameters which generates output that is separated from
| *ERROR)) all other non-socket related trace points. This output
contains return code and errno information when an error
is encountered during a socket operation. See the
STRTRC (Start Trace) Command Description in the
Information Center for details.
Flight recorder tracing Sockets LIC component traces will now include a dump of
the flight recorder entries for each socket operation
performed.
Associated job information Allows service personnel and programmers to find all jobs
that are associated to a connected or listening socket.
This information can be viewed using NETSTAT for those
socket applications using an address family of AF_INET
or AF_INET6.
| NETSTAT Connection Status(option ’3’) to enable Provides enhanced low-level debug information when the
| SO_DEBUG SO_DEBUG socket option is set on a socket application.
| Secure socket return code and message processing Presents standardized secure socket return code
messages through two SSL_ APIs. These APIs are
SSL_Strerror() and SSL_Perror(). In addition, the
gsk_strerror() provides similar function for GSKit APIs.
There is also the hstrerror() API that provides return
code information from resolver routines.
Performance Data Collection (PDC) tracepoints Provides a trace for the data flow from an application
through Sockets and the TCP/IP stack.
IBM Redbooks
v Who Knew You Could Do That with RPG IV? A Sorcerer’s Guide to System Access and More
– RFC 2136: ″Dynamic Updates in the Domain Name System (DNS UPDATE)″
IBM grants you a nonexclusive copyright license to use all programming code examples from which you
can generate similar function tailored to your own specific needs.
All sample code is provided by IBM for illustrative purposes only. These examples have not been
thoroughly tested under all conditions. IBM, therefore, cannot guarantee or imply reliability, serviceability,
or function of these programs.
All programs contained herein are provided to you ″AS IS″ without any warranties of any kind. The implied
warranties of non-infringement, merchantability and fitness for a particular purpose are expressly
disclaimed.
Printed in U.S.A.