═══ 1. About This Information ═══ This IBM TCP/IP Version 3.0 for OS/2 Warp Programmer's Reference describes the routines for application programming in the TCP/IP Version 3.0 for OS/2 Warp environment on a workstation. This document contains reference information about the following topics:  Installation and General Programming Information Provides information about installing the Programmer's Toolkit, as well as fundamental, technical information about APIs provided with TCP/IP for OS/2.  Sockets General Programming Information Describes the TCP/IP socket interface and how to use the socket routines in a user-written application.  Sockets in the Internet Domain Describes TCP/IP network protocols, getting started with sockets in the Internet domain, Internet address formats, and TCP/IP-specific network utility routines.  Sockets over Local IPC Describes how programmers can communicate on the same machine using the sockets API, and the local IPC address format.  Sockets over NetBIOS Describes how programmers can communicate with NetBIOS using the sockets API, and the NetBIOS address format.  Protocol-Independent C Sockets API Describes the protocol-independent socket calls supported by Network Services.  TCP/IP Network Utility Routines Describes the sockets utility function calls supported by Network Services.  Remote Procedure Calls (RPCs) Describes the remote procedure calls and how to use them in a user-written application.  File Transfer Protocol Application Programming Interface Describes the file transfer protocol routines and how to use them in a user-written application.  SNMP Agent Distributed Protocol Interface (DPI) Describes the agent DPI, which is used to manipulate variables in a local Management Information Base (MIB).  NETWORKS File Structure Provides examples of network names contained in the TCPIP\ETC\NETWORKS file.  Socket Error Constants Provides the socket error codes and descriptions.  Well-Known Port Assignments Provides a list of the well-known ports supported by TCP/IP. This information also includes index and search functions, which are available using the push buttons at the bottom of the information window. First-time readers should review Installation and General Programming Information before proceeding to whatever section is most relevant to them. Who Should Use This Information This information is intended for application and system programmers with experience in writing application programs on a workstation. You should also be familiar with the OS/2 operating system and know multitasking operating system concepts. It is important that you also know the C programming language. If you are not familiar with TCP/IP concepts, see Internetworking With TCP/IP Volume I: Principles, Protocols, and Architecture, and Internetworking With TCP/IP Volume II: Implementation and Internals. How the Term "Internet" Is Used An internet is a logical collection of networks supported by gateways, routers, bridges, hosts, and various layers of protocols that permit the network to function as a large, virtual network. The term internet is used as a generic term for a TCP/IP network and should not be confused with the Internet (note capital I), which consists of large national backbone networks (such as MILNET, NSFNet, and CREN) and a myriad of regional and local campus networks all over the world. ═══ 2. Installation and General Programming Information ═══ This section contains information about installing the Toolkit, as well as technical information you need to know before working with the application program interfaces (APIs) provided with TCP/IP Version 3.0 for OS/2 Warp. Before you can work with the APIs, the TCP/IP Version 3.0 for OS/2 Warp product and APIs must be installed in the MPTN directory (or the directory specified by the ETC environment variable). For example, all files in the ETC directory are in the MPTN\ETC directory. Topics Installing the Toolkit Header Files Library Files Porting Considerations Compiling and Linking an API Application Accessing a TCP/IP API DLL from an Intermediate DLL ═══ 2.1. Installing the Toolkit ═══ This section describes how to install the Toolkit. Topics Software Requirements Installing the Toolkit from the Product Diskette ═══ 2.1.1. Software Requirements ═══ You must have one of the following items installed on your system:  IBM OS/2 Warp Version 3.0 or higher with the Internet Connection for OS/2 (from the BonusPak).  IBM OS/2 Warp Connect Version 3.0 or higher with TCP/IP Version 3.0 for OS/2 Warp (TCP/IP Version 3.0 for OS/2 Warp is a part of Warp Connect). In addition, you must have the following installed on your system:  Any IBM 32-bit compiler for OS/2, including: - VisualAge C++ - C Set++ ═══ 2.1.2. Installing the Toolkit from the Product Diskette ═══ To install the TCP/IP Version 3.0 for OS/2 Warp Toolkit, perform the following steps: 1. Insert the Toolkit diskette. 2. At an OS/2 command prompt, type A:\INSTALL and press Enter. 3. Select the Install push button. 4. Follow the installation instructions. After you have installed the Toolkit, you may want to set your environment variables to find:  Header files  Link libraries  Executable programs You can set your environment variables interactively or you can include them in your CONFIG.SYS file. For information on setting the environment variables in your CONFIG.SYS file, see Compiling and Linking an API Application. ═══ 2.2. Header Files ═══ This section lists the header files for each API. These files are in the TCPIP\INCLUDE directory. Topics Sockets Remote Procedure Calls (RPCs) File Transfer Protocol Application Programming Interface (FTP API) Simple Network Management Protocol (SNMP) Agent Distributed Program Interface (DPI) ═══ 2.2.1. Sockets ═══ The socket application header files are: ARPA\NAMESER.H NERRNO.H NET\IF.H NET\IF_ARP.H NET\ROUTE.H NETDB.H NETDB\NB.H NETINET\IF_ETHER.H NETINET\IN.H NETINET\IN_SYSTM.H NETINET\IP.H NETINET\IP_ICMP.H NETINET\TCP.H RESOLV.H SYS\IOCTL.H SYS\SELECT.H SYS\SOCKET.H SYS\TIME.H SYS\UN.H TYPES.H UTILS.H ═══ 2.2.2. Remote Procedure Calls (RPCs) ═══ The RPC application header files are: RPC\AUTH.H RPC\AUTH_UNI.H RPC\CLNT.H RPC\PMAP_CLN.H RPC\PMAP_PRO.H RPC\RPC.H RPC\RPC_MSG.H RPC\RPCNETDB.H RPC\RPCTYPES.H RPC\SVC.H RPC\SVC_AUTH.H RPC\XDR.H ═══ 2.2.3. File Transfer Protocol Application Programming Interface (FTP API) ═══ The only FTP API application header file is: FTPAPI.H ═══ 2.2.4. Simple Network Management Protocol (SNMP) Agent Distributed Program Interface (DPI) ═══ The only SNMP DPI application header file is: DPI\SNMP_DPI.H ═══ 2.3. Library Files ═══ The tables in this section list library files to which an application must link. As shipped from IBM, the files listed in the first table are in either the TCPIP\LIB directory or MPTN\LIB directory, and the files listed in the second table are in either the TCPIP\DLL or MPTN\DLL directory. Note: Before you run the program, verify that DLL files are in a directory listed in the LIBPATH system environment variable. ┌──────────────────────────────────────────────────────────────────────────────┐ │ Table 1. Library Files and Their Applications │ ├─────────────────────────┬────────────────────────────────────────────────────┤ │ LIBRARY FILE │ APPLICATION │ ├─────────────────────────┼────────────────────────────────────────────────────┤ │ DPI20DLL.LIB │ Dynamic SNMP DPI calls │ ├─────────────────────────┼────────────────────────────────────────────────────┤ │ FTPAPI.LIB │ Dynamic FTP API calls │ ├─────────────────────────┼────────────────────────────────────────────────────┤ │ RPC32DLL.LIB │ Dynamic Sun remote procedure calls │ ├─────────────────────────┼────────────────────────────────────────────────────┤ │ SO32DLL.LIB │ Dynamic socket calls │ ├─────────────────────────┼────────────────────────────────────────────────────┤ │ TCP32DLL.LIB │ Dynamic TCP/IP network library calls │ └─────────────────────────┴────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────────────────────┐ │ Table 2. DLL Files and Their Applications │ ├─────────────────────────┬────────────────────────────────────────────────────┤ │ DLL FILE │ APPLICATION │ ├─────────────────────────┼────────────────────────────────────────────────────┤ │ DPI20DLL.DLL │ Executable SNMP DPI calls │ ├─────────────────────────┼────────────────────────────────────────────────────┤ │ FTPAPI.DLL │ Executable FTP API calls │ ├─────────────────────────┼────────────────────────────────────────────────────┤ │ RPC32DLL.DLL │ Executable remote procedure calls │ ├─────────────────────────┼────────────────────────────────────────────────────┤ │ SO32DLL.DLL │ Executable socket calls │ ├─────────────────────────┼────────────────────────────────────────────────────┤ │ TCP32DLL.DLL │ Executable TCP/IP network library calls │ └─────────────────────────┴────────────────────────────────────────────────────┘ ═══ 2.4. Porting Considerations ═══ Be aware of the following when you port your applications:  To access system return values, use only the errno.h include statement supplied with the compiler.  To access network return values, add the following include statement: #include  Error codes set by the OS/2 TCP/IP sockets implementation are not made available via the global errno variable. Instead, error codes are accessed by using the sock_errno() API described in sock_errno(). Use psock_errno(), instead of perror(), to produce a short error message on the standard error device describing the last error encountered during a call to a socket library function. An application cannot assign new values to error codes. This is intended to obtain per-thread error codes in a multithreaded application environment and to avoid conflict with standard IBM compiler error constants. For compatibility with BSD (Berkeley Software Distribution), an application can choose to define: #define errno sock_errno() #define perror psock_errno If a source file includes code that checks errno for both OS/2 socket and OS/2 nonsocket functions, this mechanism cannot be used. For more information about how to port a specific application, see the section for that application. ═══ 2.5. Compiling and Linking an API Application ═══ Follow these steps to compile and link an API application using an IBM 32-bit compiler for OS/2: 1. Set your environment variables to find the following:  Executable programs  Link libraries  Header files You can set the environment variables in your CONFIG.SYS file. An example of the entries you might have in your CONFIG.SYS file follows: LIBPATH=E:\IBMC\DLL;C:\MPTN\DLL;C:\TCPIP\DLL; SET PATH=E:\IBMC\BIN; SET DPATH=E:\IBMC\LOCALE;E:\IBMC\HELP; SET LIB=E:\IBMC\LIB;C:\MPTN\LIB;C:\TCPIP\LIB; SET INCLUDE=E:\IBMC\INCLUDE;C:\TCPIP\INCLUDE; SET HELP=E:\IBMC\HELP; SET BOOKSHELF=E:\IBMC\HELP; SET TMP=E:\IBMC\TMP SET TZ=EST5EDT,0,0,0,0,0,0,0,0,0 2. To compile your program, enter: icc /Ti /DOS2 /Sm /Ss /Q /Gm /Gt /C myprog.c 3. To create an executable program, you can enter: For VisualAge C++ ilink /NOFREEFORMAT /De myprog,myprog.exe,NULL,so32dll.lib tcp32dll.lib For C Set++ link386 /De myprog,myprog.exe,NULL,so32dll.lib tcp32dll.lib Note: 1. Before you run a program, verify that the DLL files are in a directory listed in the LIBPATH system environment variable. 2. For more information about the compile and link options, multithreaded libraries, and dynamic link libraries, see the User's Guide provided with your compiler. ═══ 2.6. Accessing a TCP/IP API DLL from an Intermediate DLL ═══ A TCP/IP API DLL can be accessed both directly from an application and through an intermediate DLL. An example of an intermediate DLL would be a virtual network API layer that supports generalized network functionality for applications and that uses the TCP/IP API. The IBM TCP/IP Version 3.0 for OS/2 Warp Programmer's Toolkit contains the sample program to build a DLL. You can find the program in the TCPIP\SAMPLES\SAMPDLL directory. For more information about DLLs, refer to the OS/2 Warp Technical Library, Control Programming Guide, G25H-7101. ═══ 3. Sockets General Programming Information ═══ This section contains technical information for planning, designing, and writing application programs that use the Network Services sockets application programming interface (API) in a TCP/IP Version 3.0 for OS/2 Warp environment. Topics Introduction to Network Services Programming with Sockets Multithreading Considerations Typical Socket Session Diagrams Using Socket Calls Porting a Sockets API Application Compiling and Linking a Sockets API Application ═══ 3.1. Introduction to Network Services ═══ IBM's Network Services for TCP/IP Version 3.0 for OS/2 Warp provides a solution to interconnect applications across networks. Network Services provides a 32-bit sockets API for the Internet (TCP/IP), local interprocess communication (Local IPC), and NetBIOS communication domains. Network Services sockets is based on the Berkeley Software Distribution (BSD) Version 4.3 sockets implementation. The sockets API allows you to write distributed or client/server applications in supported communication domains to allow applications to communicate across networks. In addition, the interface allows interprocess communication within the same workstation. Applications can have full network access by just using the sockets API. You can run an existing sockets application in another communications domain by modifying the communications domain selection and the networking addressing parameters used by the application. You must then recompile and relink the application. The following figure describes the internal structure of TCP/IP Version 3.0 for OS/2 Warp. Internal Structure of TCP/IP The major components of the TCP/IP stack are:  Control program: CNTRL.EXE is a very important component of our stack. CNTRL.EXE provides threads for proper running of our stack. It provides a thread for each of the following: - IP input processing - TCP slow timeout processing - Debug thread for IP - ARP timeout processing - Ifndis debug thread - Watchdog thread for the adapter status - Loopback IP packets processing CNTRL.EXE is normally started from CONFIG.SYS with a RUN=statement. It should be the first program to begin executing when TCP/IP is started. CNTRL.EXE for Version 3.0 is different from that in Version 2.0 stack.  DLLs: Both the TCP32DLL.DLL and SO32DLL.DLL are 32-bit DLLs. The TCPIPDLL.DLL is still shipped for backwards compatibility with TCP/IP Version 1.2.1, but no service is done on it. TCP32DLL.DLL contains the getXbyY() code in it and is thread re-entrant. This DLL is also SLIP and LAN aware; that is, hosts on local LANs and the Internet (by way of SLIP) can be accessed simultaneously. SO32DLL.DLL contains all sockets APIs and is thread re-entrant.  Device drivers: SOCKETS.SYS provides the sockets layer for the stack. All the protocol device drivers are below the sockets layer. Applications written to sockets API first pass through SOCKETS.SYS. The socket address families that are supported are AF_OS2 (or AF_UNIX), AF_INET, and AF_NB. AFOS2.SYS is the Local Interprocess Communication (LIPC) device driver. This driver supports AF_OS2 (or AF_UNIX) type sockets. The AF_OS2 socket types can be used by applications within one OS/2 machine to communicate with each other (using sockets APIs). AFNB.SYS device driver provides support for sockets over NetBIOS. This driver supports applications written using AF_NB type sockets (and sockets APIs). But the communication takes place using NetBIOS. AFINET.SYS is the transport protocol device driver for AF_INET sockets. AFINET.SYS is essentially TCP/IP code. SLIP, PPP, X25, and SNAlink use a special interface in this driver to their respective hardware. For example, SLIP.EXE creates a special interface inside the stack for the ComPort. IP packets for this SLIP interface are then passed on to the ComPort rather than to IFNDIS.SYS. IFNDIS.SYS is the NDIS bottom half of AFINET.SYS. IFNDIS.SYS is compliant with NDIS specs Version 2.0.1. Any MAC driver written to these specifications should work with the Version 3.0 stack. This driver takes IP packets from AFINET.SYS, adds a MAC header to it, and transmits it to the MAC driver (for example, IBMTOK.OS2). The MAC header from the incoming packets is stripped off and the remaining IP packet is passed onto AFINET.SYS. ═══ 3.2. Programming with Sockets ═══ This section contains introductory information about sockets and the sockets API. The sockets API allows an application to communicate across networks with other applications. You can, for example, make use of the sockets interface when you write a client application that may communicate with a server application running on the same or another workstation. To use sockets, you must know the C programming language. The Network Services sockets API provides a standard interface to all supported communication domains: Internet, Local IPC, and NetBIOS. Network Services supports four socket types: datagram, raw, sequenced packet, and stream. Each communication domain supports certain socket types. Topics What Is a Socket? Socket Protocol Families and Supported Communication Domains Socket Address Families Connection Modes Socket Types Socket Addresses and Data Structure Format ═══ 3.2.1. What Is a Socket? ═══ A socket is an endpoint for communication. From an application program perspective, it is a resource allocated by Network Services. It is represented by an integer called a socket descriptor. A pair of sockets, one in each of two tasks, is used for interprocess communication (IPC) between them, on a single workstation or different workstations. Sockets are full-duplex, which means that data can be transmitted and received simultaneously. Each socket is created for a particular communication domain and socket type. Communication can occur only between applications using the same socket type within the same communications domain. The sockets API is designed to provide applications with access to the network that hides the details of the physical network. You choose a socket type (refer to Socket Types) that reflects the communication characteristics that you desire. For example, stream sockets offer a reliable method of data transmission without message boundaries. ═══ 3.2.2. Socket Protocol Families and Supported Communication Domains ═══ Communication domains are specified in Network Services as protocol families. Specifying the protocol family indicates to the system the underlying network protocol used during communications. All servers in the same protocol family understand and use the same scheme of addressing socket endpoints. The protocol families are defined in the header file and are listed in the following table: ┌─────────────────────────────────────────────────────────────────────────────┐ │ Table 3. Communication Domains Supported │ ├────────────────┬────────────────────┬────────────────────┬──────────────────┤ │ │ PROTOCOL FAMILY │ │ │ │ COMMUNICATION │ #DEFINE IN │ SUPPORTED PROTO- │ SUPPORTED SOCKET │ │ DOMAIN │ │ COLS │ TYPES │ ├────────────────┼────────────────────┼────────────────────┼──────────────────┤ │ Internet │ PF_INET │ ICMP, IP, TCP, UDP │ Datagram, Raw, │ │ │ │ │ Stream │ ├────────────────┼────────────────────┼────────────────────┼──────────────────┤ │ Local IPC │ PF_OS2 or PF_UNIX │ Local IPC │ Datagram, Stream │ ├────────────────┼────────────────────┼────────────────────┼──────────────────┤ │ NetBIOS │ PF_NETBIOS or │ NetBIOS │ Datagram, │ │ │ PF_NB │ │ Sequenced Packet │ └────────────────┴────────────────────┴────────────────────┴──────────────────┘ As the table indicates, some socket types can be used in more than one communications domain. Socket Types provides more information on socket types. The table below lists the default protocols for each communication domain-socket type that is supported: ┌─────────────────────────────────────────────────────────────────────────────┐ │ Table 4. Default Protocols │ ├────────────┬────────────────────┬──────────────────────┬────────────────────┤ │ COMMUNI- │ │ DEFAULT PROTOCOL │ │ │ CATION │ │ #DEFINE IN │ OTHER PROTOCOLS │ │ DOMAIN │ SOCKET TYPE │ │ SUPPORTED │ ├────────────┼────────────────────┼──────────────────────┼────────────────────┤ │ Internet │ Datagram │ UDP (IPPROTO_UDP) │ │ ├────────────┼────────────────────┼──────────────────────┼────────────────────┤ │ │ Raw │ Raw IP (IPPROTO_RAW) │ ICMP │ │ │ │ │ (IPPROTO_ICMP) │ ├────────────┼────────────────────┼──────────────────────┼────────────────────┤ │ │ Stream │ TCP (IPPROTO_TCP) │ │ ├────────────┼────────────────────┼──────────────────────┼────────────────────┤ │ Local IPC │ Datagram │ none (0) │ │ ├────────────┼────────────────────┼──────────────────────┼────────────────────┤ │ │ Stream │ none (0) │ │ ├────────────┼────────────────────┼──────────────────────┼────────────────────┤ │ NetBIOS │ Datagram │ NetBIOS (NBPROTO_NB) │ │ ├────────────┼────────────────────┼──────────────────────┼────────────────────┤ │ │ Sequenced Packet │ NetBIOS (NBPROTO_NB) │ │ └────────────┴────────────────────┴──────────────────────┴────────────────────┘ ═══ 3.2.3. Socket Address Families ═══ An address family is a specific address format that conforms to the rules of the communication domain that the address family is used in. The address family indicates to the system how to interpret supplied addresses. The address families that are supported by TCP/IP Version 3.0 for OS/2 Warp Network Services are defined in the header file, as follows: ┌────────────────────────────────────────────────────────────────────────────────┐ │ Table 5. Socket Address Families │ ├────────────┬────────────────────────────────────────────┬──────────────────────┤ │ │ │ ADDRESS FAMILY │ │ ADDRESS │ │ #DEFINE IN │ │ FAMILY │ DESCRIPTION │ │ ├────────────┼────────────────────────────────────────────┼──────────────────────┤ │ Internet │ Defines addresses in the Internet Protocol │ AF_INET │ │ │ version 4 address format. │ │ ├────────────┼────────────────────────────────────────────┼──────────────────────┤ │ Local IPC │ Defines addresses in the Local IPC address │ AF_OS2 or AF_UNIX │ │ │ format. │ │ ├────────────┼────────────────────────────────────────────┼──────────────────────┤ │ NetBIOS │ Defines addresses in the NetBIOS address │ AF_NETBIOS or AF_NB │ │ │ format. │ │ └────────────┴────────────────────────────────────────────┴──────────────────────┘ ═══ 3.2.4. Connection Modes ═══ A connection mode refers to whether there is an established logical channel for the transmission of data between two application programs. The Network Services sockets API supports two connection modes:  Connection-oriented  Connectionless The connection-oriented mode requires a logical connection to be established between two applications before data transfer or communication can occur. Applications encounter some overhead during the connection establishment phase as the applications negotiate the connection request. This mode is useful for applications that use long datastream transmissions or require reliable transmissions of data. The connectionless mode does not require a logical connection to allow communication between applications. Rather, individual messages are transmitted independently from one application to another application. Each message must contain the data and all information required for delivery of the message to its destination. Normally, datagram and raw socket types use the connectionless mode. The term connected refers to two endpoints that have an established logical connection between them. Stream and sequenced packet socket types use the connection-oriented mode. For information on how datagram and raw socket types can be connected, see "Datagram or raw sockets" in connect(). ═══ 3.2.5. Socket Types ═══ This section discusses the following topics:  Stream Sockets  Sequenced Packet Sockets  Datagram Sockets  Raw Sockets  Guidelines for Using Socket Types Each socket has an associated type, which describes the semantics of communication for the socket. The socket type determines the socket communication properties such as reliability, ordering, and prevention of duplication of messages. The Network Services sockets API supports communications only between applications using the same socket type. The socket type is passed as a parameter to the socket() call. For more detailed information, see socket(). Socket Types Summary Table summarizes the attributes and features of each socket type. ═══ 3.2.5.1. Stream Sockets ═══ Stream sockets model full-duplex byte streams and define a reliable connection-oriented service. Data is sent without errors or duplication and is received in the same order as it was sent. Flow control is built in to avoid data overruns. Note that there is no guarantee for a one-to-one correspondence of send and receive calls. It is possible for data sent by one or more send calls to be received by one or more different receive calls. Stream sockets are connected sockets and are either active or passive. Active sockets are used by clients who initiate connection requests with connect(). Passive sockets are used by servers to accept connection requests with the listen() and accept() calls. A passive socket that has indicated its willingness to accept connections with the listen() call cannot be used to initiate connection requests. After a connection has been established between stream sockets, any of the data transfer calls can be used: send() and recv() sendto() and recvfrom() sendmsg() and recvmsg() writev() and readv() Usually, a send()-recv() pair is used for sending data on stream sockets. ═══ 3.2.5.2. Sequenced Packet Sockets ═══ Sequenced packet sockets define a reliable connection-oriented service. Data is sent without error or duplication and is received in the same order as it was sent. Flow control is built in to avoid data overruns. Every sequenced packet is sent and received as a complete record. After a connection has been established between sequenced packet sockets, any of the data transfer calls can be used: send() and recv() sendto() and recvfrom() sendmsg() and recvmsg() writev() and readv() Usually, a send()-recv() pair is used for sending data on sequenced packet sockets. ═══ 3.2.5.3. Datagram Sockets ═══ Datagram sockets provide connectionless message exchange with no guarantees of delivery. Data can be lost or duplicated, and datagrams can arrive out of order. Datagrams are sent and received as complete records. Datagram sockets are connectionless sockets by default. A datagram socket operating in connectionless mode can exchange datagrams using the sendto() and recvfrom() calls or the sendmsg() and recvmsg() calls. If an application program is using datagram sockets and calls connect() fully specifying the destination address, the socket will be considered connected. The application program can then use the other data transfer calls send() and recv() or writev() and readv(). The connected or unconnected method of data transfer stays in effect until connect() is called again with a different destination address. Refer to "Datagram or raw sockets" in connect() for additional information. Datagram sockets may be used to send broadcast messages in the Internet and NetBIOS communications domain. For the Internet domain, the constant INADDR_BROADCAST, defined in , can be used to send a broadcast datagram. For the NetBIOS domain, the address format has a type field that specifies whether the address is unique, multicast, or broadcast. ═══ 3.2.5.4. Raw Sockets ═══ Raw sockets provide connectionless message exchange with the same data transfer semantics as previously described for datagram sockets. You can use raw sockets to directly access the protocol specified in the socket() call, such as the Internet Protocol (IP), or the Internet Control Message Protocol (ICMP). Raw sockets are often used for testing new protocol implementations or gaining access to some of the more advanced facilities of an existing protocol. Raw sockets are connectionless mode sockets by default. Raw sockets can be connected if connect() is called to specify the destination address. Refer to "Datagram or raw sockets" in connect() for additional information. ═══ 3.2.5.5. Socket Types Summary Table ═══ The following table summarizes many of the attributes and features of supported socket types: ┌────────────────────────────────────────────────────────────────────────────────┐ │ Table 6. Socket Types │ ├───────────┬────────────────────┬─────────────────┬───────────┬─────────────────┤ │ │ │ │ CON- │ │ │ SOCKET │ #DEFINE IN │ │ NECTION │ PRIMARY │ │ TYPE │ │ DOMAINS │ ORIENTED? │ SOCKET CALLS │ ├───────────┼────────────────────┼─────────────────┼───────────┼─────────────────┤ │ Stream │ SOCK_STREAM │ INET, Local IPC │ yes │ send - recv │ ├───────────┼────────────────────┼─────────────────┼───────────┼─────────────────┤ │ Sequenced │ SOCK_SEQPACKET │ NetBIOS │ yes │ send - recv │ │ Packet │ │ │ │ │ ├───────────┼────────────────────┼─────────────────┼───────────┼─────────────────┤ │ Datagram │ SOCK_DGRAM │ INET, Local │ no (*) │ sendto - │ │ │ │ IPC, NetBIOS │ │ recvfrom(*) │ ├───────────┼────────────────────┼─────────────────┼───────────┼─────────────────┤ │ Raw │ SOCK_RAW │ INET │ no (*) │ sendto - │ │ │ │ │ │ recvfrom(*) │ ├───────────┴────────────────────┴─────────────────┴───────────┴─────────────────┤ │ NOTE: (*) Datagram sockets and raw sockets are connectionless, unless the │ │ application has called connect() for the socket. In this case, the socket is │ │ CONNECTED. Refer to Connection Modes for additional information. │ └────────────────────────────────────────────────────────────────────────────────┘ ═══ 3.2.5.6. Guidelines for Using Socket Types ═══ If you are communicating with an existing application, you must use the same socket type and the same communication domain as the existing application. Raw sockets have a special purpose of interfacing directly to the underlying protocol layer. If you are writing a new protocol on top of Internet Protocol (IP) or wish to use the Internet Control Message Protocol (ICMP), then you must use raw sockets. You should consider the following factors in choosing a socket type for new applications:  Reliability: Stream and sequenced packet sockets provide the most reliable connection. Connectionless datagram and raw sockets are unreliable because packets can be discarded, duplicated, or received out of order. This may be acceptable if the application does not require reliability, or if the application implements the reliability on top of the sockets API. The trade-off is the increased performance available over stream and sequenced packet sockets.  Performance: The overhead associated with reliability, flow control, packet reassembly, and connection maintenance degrades the performance of stream and sequenced packet sockets so that these socket types do not perform as well as datagram sockets acting in a connectionless mode.  Amount of data to be transferred: Datagram and sequenced packet sockets impose a limit on the amount of data transferred. As the amount of data in a single transaction increases, it is preferable to use stream or sequenced packet sockets. ═══ 3.2.6. Socket Addresses and Data Structure Format ═══ A socket can be bound to a unique local address with the bind() call so that other sockets can connect to it. The socket address format conforms to the rules governing network addresses for each communication domain. For example, in the Internet communication domain, a socket address consists of a 32-bit internet host address and a 16-bit local port number. Each communication domain has different rules for valid socket names and interpretation of names. A socket address is defined by the following sockaddr structure, which is found in the header file: struct sockaddr { u_short sa_family; /* address family */ char sa_data[14]; /* address value */ }; The sa_family field contains the address family. The sa_data field is different for each address family. Each address family defines its own structure, which is overlaid on the sockaddr structure. Socket address structures for the Internet, Local IPC, and NetBIOS communication domains (sockaddr_in, sockaddr_un, sockaddr_nb) are described in "Addressing within an Internet Domain", Local IPC Address Format, and NetBIOS Address Format. ═══ 3.3. Multithreading Considerations ═══ The sockets and network utility routines are completely re-entrant. Multiple threads of an application can call any socket call. ═══ 3.4. Typical Socket Session Diagrams ═══ The following figures show a graphical representation for the general sequence of socket calls needed to provide communication between applications for supported socket types. For stream or sequenced packet socket types, and for datagram socket types, see the following figures. This basic sequence is the same for each supported communication domain for all supported socket types. This means that a programmer can modify the communications domain selection and the networking addressing parameters of an existing sockets program, recompile and relink, and the program can be run in another domain. This also allows programs that use sockets in multiple domains to be easily constructed. A Typical Stream or Sequenced Packet Socket Session There is an alternative to the serialized sequence as shown in the next figure. The server can call accept() to create a new thread to handle this new socket. Using the original thread, the server can then call accept() again and begin waiting for more requests. A Typical Datagram Socket Session ═══ 3.5. Using Socket Calls ═══ A detailed description of how to use the basic socket calls follows. These examples show communication between applications in the Internet communication domain using stream sockets. Sample programs can be found in the TCPIP\SAMPLES\SOCKET directory. Additional information for other supported domains can be found in Sockets over Local IPC and Sockets over NetBIOS. 1. An application must get a socket descriptor using the socket() call: An Application Uses the socket() Call int socket(int domain, int type, int protocol); /* extracted from sys\socket.h */ int s; . . . s = socket(PF_INET, SOCK_STREAM, 0); In this example, the socket() call allocates a socket descriptor s in the Internet communication domain. The communication domain is specified in the domain parameter. The domain is specified with one of the supported protocol families. This example specifies the protocol family PF_INET. The type parameter is a constant that specifies the type of socket. For the Internet communication domain, this can be SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW. The protocol parameter is a constant that specifies which protocol to use. Passing 0 chooses the default protocol for the specified socket type. Socket Protocol Families and Supported Communication Domains includes information on default protocols. If successful, socket() returns a non-negative integer socket descriptor. 2. After an application has a socket descriptor, it can explicitly bind() a unique name to the socket: An Application Uses the bind() Call int rc; int s; struct sockaddr_in myname; int bind(int s, struct sockaddr *name, int namelen); /* extracted from sys\socket.h */ /* clear the structure */ memset(&myname, 0, sizeof(myname)); myname.sin_family = AF_INET; myname.sin_addr.s_addr = inet_addr("129.5.24.1"); /* specific interface */ myname.sin_port = htons(1024); . . . rc = bind(s, (struct sockaddr *) &myname, sizeof(myname)); For a server in the Internet domain to be able to listen() for connections on a stream socket or issue recvfrom() on a datagram socket, the server must first bind() the socket to a specific address family and port. This example binds myname to socket s. Note that the sockaddr_in structure should be cleared before calling bind(). For a more detailed description, see bind(). For information on the sockaddr_in structure, see Internet Address Formats. The unique name myname specifies that the application uses an Internet address family (AF_INET) at Internet address 129.5.24.1, and is bound to port 1024. The preceding example shows two useful network utility routines.  inet_addr() takes an Internet address in dotted-decimal form and returns it in network-byte order. For a more detailed description, see inet_addr().  htons() takes a port number in host-byte order and returns the port in network-byte order. For a more detailed description, see htons(). The next figure shows how the bind() call on the server side uses the network utility routine getservbyname() to find a well-known port number for a specified service from the ETC\SERVICES file (for more information on well-known ports, see Ports). The figure also shows the use of the internet address wildcard value INADDR_ANY. This is the value generally used on a socket bind() call. It binds the socket to all internet addresses available on the local machine, without requiring the program to know the local internet address. (The code fragment in the preceding figure will run successfully only on the machine with internet address 192.5.24.1.) If a host has more than one internet address (that is, if it is multihomed host), messages sent to any of the addresses will be deliverable to a socket bound to INADDR_ANY. A bind() Call Uses the getservbyname() Call int rc; int s; struct sockaddr_in myname; int bind(int s, struct sockaddr *name, int namelen); /* extracted from sys\socket.h */ struct servent *sp; . . . sp = getservbyname("login","tcp"); /* get application specific */ /* well-known port */ . . . /* clear the structure */ memset(&myname,0,sizeof(myname)); myname.sin_family = AF_INET; myname.sin_addr.s_addr = INADDR_ANY; /* any interface */ myname.sin_port = sp->s_port; . . . rc = bind(s,(struct sockaddr *)&myname,sizeof(myname)); 3. After binding a name to a socket, a server using stream sockets must indicate its readiness to accept connections from clients. The server does this with the listen() call: An Application Uses the listen() Call int s; int backlog; int rc; int listen(int s, int backlog); /* extracted from sys\socket.h */ . . . rc = listen(s, 5); The listen() call is used to indicate the server is ready to begin accepting connections. In this example, a maximum of five connection requests can be queued for the server. Additional requests are ignored. For a more detailed description, see listen(). 4. Clients using stream sockets call connect() to request a connection: An Application Uses the connect() Call int s; struct sockaddr_in servername; int rc; int connect(int s, struct sockaddr *name, int namelen); /* extracted from sys\socket.h */ . . . memset(&servername, 0,sizeof(servername)); servername.sin_family = AF_INET; servername.sin_addr.s_addr = inet_addr("129.5.24.1"); servername.sin_port = htons(1024); . . . rc = connect(s, (struct sockaddr *) &servername, sizeof(servername)); The connect() call attempts to connect socket s to the server with name supplied in servername. This could be the server that was used in the previous bind() example. With TCP stream sockets the caller blocks until the connection is accepted by the server. On successful return from connect(), the socket s is associated with the connection to the server. Refer to ioctl() for additional information about determining blocking and nonblocking behavior. Note that the sockaddr_in structure should be cleared before calling connect(). For a more detailed description, see connect(). An Application Uses the gethostbyname() Call int s; struct sockaddr_in servername; char *hostname = "serverhost"; int rc; int connect(int s, struct sockaddr *name, int namelen); /* extracted from sys\socket.h */ struct hostent *hp; . . . hp = gethostbyname(hostname); /* clear the structure */ memset(&servername,0,sizeof(servername)); servername.sin_family = AF_INET; servername.sin_addr.s_addr = *((u_long *)hp->h_addr); servername.sin_port = htons(1024); . . . rc = connect(s,(struct sockaddr *)&servername,sizeof(servername)); The above figure shows an example of using the gethostbyname() network utility routine to find out the Internet address of serverhost from the name server or the ETC\HOSTS file. 5. Servers using stream sockets accept a connection request with the accept() call: An Application Uses the accept() Call int clientsocket; int s; struct sockaddr clientaddress; int addrlen; int accept(int s, struct sockaddr *addr, int *addrlen); /* extracted from sys\socket.h */ . . . addrlen = sizeof(clientaddress); . . . clientsocket = accept(s, &clientaddress, &addrlen); If connection requests are not pending on socket s, the accept() call will block the server unless ioctl() has been called. Refer to ioctl() for additional information about determining blocking and nonblocking behavior. When a connection request is accepted on socket s, the name of the client and length of the client name are returned, along with a new socket descriptor. The new socket descriptor is associated with the client that initiated the connection and socket s is again available to accept new connections. For a more detailed description, see accept(). 6. There are several sockets API calls available to clients and servers for data transfer. The readv() and writev(), and send() and recv() calls can be used on connected sockets. The sendto() and recvfrom() calls can be used on both connected and unconnected sockets. An Application Uses the send() and recv() Calls int bytes_sent; int bytes_received; char data_sent[256] = "data to be sent on connected socket"; char data_received[256]; int send(int socket, char *buf, int buflen, int flags); /* extracted from sys\socket.h */ int recv(int socket, char *buf, int buflen, int flags); /* extracted from sys\socket.h */ int s; . . . bytes_sent = send(s, data_sent, sizeof(data_sent), 0); . . . bytes_received = recv(s, data_received, sizeof(data_received), 0); The example above shows an application sending data on a connected socket and receiving data in response. The flags field can be used to specify additional options to send() or recv(), such as sending out-of-band data. For additional information, see send() and recv(). 7. If the socket is not connected, additional addressing information must be passed using sendto() and may be optionally returned using recvfrom(). The following figure shows an example of using the sendto() and recvfrom() calls: An Application Uses the sendto() and recvfrom() Call int bytes_sent; int bytes_received; char data_sent[256] = "data to be sent using sendto()"; char data_received[256]; struct sockaddr_in to; struct sockaddr from; int addrlen; int sendto(int socket, char *buf, int buflen, int flags, struct sockaddr *addr, int addrlen); /* extracted from sys\socket.h */ int recvfrom(int socket, char *buf, int buflen, int flags, struct sockaddr *addr, int *addrlen); /* extracted from sys\socket.h */ int s; . . . memset(&to, 0, sizeof(to)); to.sin_family = AF_INET; to.sin_addr.s_addr = inet_addr("129.5.24.1"); to.sin_port = htons(1024); . . . bytes_sent = sendto(s, data_sent, sizeof(data_sent), 0, (struct sockaddr *) &to, sizeof(to)); . . . addrlen = sizeof(from); /* must be initialized */ bytes_received = recvfrom(s, data_received, sizeof(data_received), 0, &from, &addrlen); The sendto() and recvfrom() calls take additional parameters that allow the caller to specify the recipient of the data or to be notified of the sender of the data. See recvfrom(), and sendto(), for more information about these additional parameters. 8. The writev() and readv() calls provide the additional features of scatter gather read/write of data. Scattered data can be located in multiple data buffers. The writev() call gathers the scattered data and sends it. The readv() call receives data and scatters it into multiple buffers. 9. Applications can handle multiple sockets. In such situations, you can use the select() call to determine the sockets that have data to be read, those that are ready for data to be written, and the sockets that have pending exception conditions. If the timeout parameter is positive, select() waits up to this amount of time for at least one socket to become ready on the indicated conditions. This is useful for applications servicing multiple connections that cannot afford to block, waiting for data on one connection. There are two versions of the select() call: a TCP/IP Version 3.0 for OS/2 Warp version and a version modeled after the BSD select() call. An example of how the TCP/IP Version 3.0 for OS/2 Warp select() call is used is shown in the next figure. For information on using the BSD version of the select() call, refer to BSD Version. An Application Uses the select() Call . . . int socks[3]; /* array of sockets */ long timeout = MAX_TIMEOUT; int s1, s2, s3; int number_ready; . . . /* put sockets to check in socks[] */ socks[0] = s1; /* read socket number */ socks[1] = s2; /* write socket number */ socks[2] = s3; /* second write socket number */ /* check for READ on s1, WRITE on s2 and s3 */ number_ready = select(socks, 1, 2, 0, timeout); In this example, the application indicates the sockets to be checked for readability or readiness for writing. 10. In addition to select(), applications can use the ioctl() call to help perform asynchronous (nonblocking) socket operations. An example of the use of the ioctl() call follows: An Application Uses the ioctl() Call int s; int bytes_received; int dontblock; char buf[256]; int rc; int ioctl(int s, int command, char *command_data, int datasize); /* extracted from sys\socket.h */ . . . dontblock = 1; . . . rc = ioctl(s, FIONBIO, (char *) &dontblock, sizeof(dontblock)); . . . bytes_received = recv(s, buf, sizeof(buf), 0); if (bytes_received == -1) { if (sock_errno() == SOCEWOULDBLOCK) /* data is not present */ else /* error occurred */ } else /* bytes_ received indicates amount of data received in buf */ This example causes the socket s to be placed in nonblocking mode. When this socket is passed as a parameter to calls that would block, such as recv() when data is not present, it causes the call to return with an error code, and sets the error value to SOCEWOULDBLOCK. Setting the mode of the socket to be nonblocking allows an application to continue processing without becoming blocked. For a more detailed description, seeioctl(). 11. The socket descriptor s is deallocated with the soclose() call. For a more detailed description, see soclose(). Here is an example of the soclose() call: An Application Uses the soclose() Call int soclose(int s); /* extracted from sys\socket.h */ . . . /* close the socket */ soclose(s); . . . ═══ 3.6. Porting a Sockets API Application ═══ Network Services sockets is based on the Berkeley Software Distribution version 4.3 sockets implementation. The IBM OS/2 socket implementation differs from the Berkeley socket implementation as follows:  Sockets are not OS/2 files or devices. Socket numbers have no relationship to OS/2 file handles. Therefore, read(), write(), and close() do not work for sockets. Using read(), write(), or close() gives incorrect results. Use the recv(), send(), and soclose() functions instead.  Socket calls require that you call the sock_init() routine before you call them. Therefore, always call sock_init() at the beginning of programs using the socket interface.  Error codes set by the OS/2 TCP/IP sockets implementation are not made available via the global errno variable. Instead, error codes are accessed by using the sock_errno() API described in sock_errno(). Use psock_errno(), instead of perror(), to write a short error message to the standard error device describing the last error encountered during a call to a socket library function. It is not possible for an application to assign new values to error codes. This is intended to obtain per-thread error codes in a multithreaded application environment and to avoid conflict with standard IBM C Set++ error constants. For compatibility with BSD, an application can choose to define: #define errno sock_errno() #define perror psock_errno() If a source file includes code that checks errno for both OS/2 socket and OS/2 nonsocket functions, this mechanism cannot be used. BSD-style error checking is as follows: - rt = recv(s, buf, sizeof(buf), 0); if (rt == -1 && errno == EWOULDBLOCK) {...} - if (recv(s, buf, sizeof(buf), 0) < 0) { perror("Recv()"); exit(1); } The preferred OS/2-style error checking is as follows: - rt = recv(s, buf, sizeof(buf), 0); if (rt == -1 && sock_errno() == SOCEWOULDBLOCK) {...} - if (recv(s, buf, sizeof(buf), 0) < 0) { psock_errno("Recv()"); exit(1); } Error constants consistent with BSD sockets are provided for compatibility purposes; your application can use the error constant EWOULDBLOCK, instead of SOCEWOULDBLOCK. Refer to Socket Error Constants, or the file for definitions of error constants.  The select() call has a different interface. Unlike the Berkeley select() call, you cannot use the OS/2 select() call to wait for activity on devices other than sockets. See select() for more information.  ioctl() implementation might differ from the current Berkeley ioctl() implementation. For example, IBM has added a lendata parameter, which the current Berkeley ioctl() implementation does not support. Other functions of the IBM ioctl() call might also differ from the current Berkeley ioctl() implementation. In addition, the getsockopt() and setsockopt() might provide different support. See ioctl(), getsockopt(), and setsockopt() for more information. You must define the constant OS2 by doing one of the following:  Place #define OS2 at the top of each file that includes TCP/IP header files.  Use the /DOS2 option when compiling the source for your application. ═══ 3.7. Compiling and Linking a Sockets API Application ═══ Follow these steps to compile and link a sockets API application using an IBM 32-bit OS/2 compiler: 1. Set your environment variables to find the following:  Executable programs  Link libraries  Header files You can set the environment variables in your CONFIG.SYS file. An example of the entries you might have in your CONFIG.SYS file follows: LIBPATH=E:\IBMC\DLL;C:\MPTN\DLL;C:\TCPIP\DLL; SET PATH=E:\IBMC\BIN; SET DPATH=E:\IBMC\LOCALE;E:\IBMC\HELP; SET LIB=E:\IBMC\LIB;C:\MPTN\LIB;C:\TCPIP\LIB; SET INCLUDE=E:\IBMC\INCLUDE;C:\TCPIP\INCLUDE; SET HELP=E:\IBMC\HELP; SET BOOKSHELF=E:\IBMC\HELP; SET TMP=E:\IBMC\TMP SET TZ=EST5EDT,0,0,0,0,0,0,0,0,0 2. To compile your program, enter: icc /Ti /DOS2 /Sm /Ss /Q /Gm /Gt /C myprog.c 3. To create an executable program, you can enter: For VisualAge C++ ilink /NOFREEFORMAT /De myprog,myprog.exe,NULL,so32dll.lib tcp32dll.lib For C Set++ link386 /De myprog,myprog.exe,NULL,so32dll.lib tcp32dll.lib Note: 1. Before you run a sockets application, verify that the DLL files (so32dll.dll and tcp32dll.dll) are in a directory listed in the LIBPATH system environment variable. 2. For more information about the compile and link options, multithreaded libraries, and dynamic link libraries, see the User's Guide provided with your compiler. ═══ 4. Sockets in the Internet Domain ═══ This section describes the use of sockets in the Internet domain. Topics Protocols Used in the Internet Domain Getting Started with Sockets in the Internet Domain Network-Byte Order Internet Address Formats TCP/IP Specific Network Utility Routines ═══ 4.1. Protocols Used in the Internet Domain ═══ This section describes the network protocols in TCP/IP. The Internet domain is supported only by the TCP/IP protocol suite. Networking protocols like TCP/IP are layered as shown in the following figure. For more information on the Internet domain and the TCP/IP protocol suite, refer to TCP/IP Illustrated, Volume 1: The Protocols, Douglas E. Comer, Addison-Wesley Publishing Co., 1993. The Internet Layered Architecture Topics Transmission Control Protocol (TCP) User Datagram Protocol (UDP) Internet Protocol (IP) Internet Control Message Protocol (ICMP) Address Resolution Protocol (ARP) ═══ 4.1.1. Transmission Control Protocol (TCP) ═══ TCP is a transport protocol that provides a reliable mechanism for delivering packets between hosts on an Internet. TCP takes a stream of data, breaks it into datagrams, sends each one individually using Internet Protocol (IP), and reassembles the datagrams at the destination node. If any datagrams are lost or damaged during transmission, TCP detects this and resends the missing datagrams. The received data stream is a reliable copy of the transmitted data stream. Note: The PUSH flag is a notification from the sender to the receiver for the receiver to pass all the data that it has to the receiving process. This data consists of whatever is in the segment with the PUSH flag, along with any other data the receiving TCP has collected for the receiving process. Our TCP implementation automatically sets the PUSH flag if the data in the segment being sent empties the send buffer. In addition, our implementation ignores a received PUSH flag because we don't delay the delivery of the received data to the application. You can use TCP sockets for both passive (server) and active (client) applications. While some calls are necessary for both types, some are role-specific. TCP is the default protocol for stream sockets in the Internet domain. See the Toolkit for sample C socket communication client and server programs. TCP is a connection-oriented protocol. It is used to communicate between pairs of applications. After you make a connection, it exists until you close the socket. During the connection, data is either delivered or an error code is returned by Network Services. ═══ 4.1.2. User Datagram Protocol (UDP) ═══ UDP is a transport-layer datagram protocol that sends and receives whole packets across the network. UDP is used for application-to-application programs between TCP/IP hosts. UDP does not offer a guarantee of datagram delivery or duplication protection. UDP does provide checksums for both the header and data portions of a datagram. However, applications that require reliable delivery of streams of data should use TCP. UDP is the default protocol for datagram sockets in the Internet domain. Unlike applications using TCP, UDP applications are usually connectionless. A UDP socket application can become connected by calling the connect() API. An unconnected socket can be used to communicate with many hosts; but a connected socket, because it has a dedicated destination, can send data to, and receive data from, only one host at a time. UDP is considered an unreliable protocol because it sends its data over the network without verification. Consequently, after a packet has been accepted by the UDP interface, the arrival of the packet and the arrival order of the packet at the destination cannot be guaranteed. ═══ 4.1.3. Internet Protocol (IP) ═══ The IP network layer provides the interface from the transport layer (host-to-host) protocols to the link-level protocols. IP is the basic transport mechanism for routing IP packets to the next gateway, router, or destination host. IP provides the means to transmit packets of data from sources to destinations. Sources and destinations are hosts identified by 32-bit IP addresses, which are assigned independent of the underlying physical network. Outgoing packets automatically have an IP header prefixed to them, and incoming packets have their IP header removed before being sent to the higher-level protocols. This protocol ensures the unique addressing of hosts in an Internet network. IP does not ensure a reliable communication, because it does not require acknowledgments from the sending host, the receiving host, or intermediate hosts. IP does not provide error control for data; it provides only a header checksum. IP treats each packet as an independent entity unrelated to any other packet. IP does not perform retransmissions or flow control. A higher-level protocol, TCP/IP, for example, that uses IP must implement its own reliability procedures. Applications do not typically access IP directly, but rather use TCP or UDP which, in turn, use IP. Raw sockets can use IP. ═══ 4.1.4. Internet Control Message Protocol (ICMP) ═══ ICMP is used to pass control information between hosts. For example, the information can be sent in any of the following situations:  When a host checks to see if another host is available (PING)  When a packet cannot reach its destination  When a gateway or router can direct a host to send traffic on a shorter route  When a gateway or router does not have the buffering capacity to forward a packet ICMP provides feedback about problems in the communication environment; it does not make IP reliable. The use of ICMP does not guarantee that an IP packet will be delivered reliably or that an ICMP message will be returned to the source host when an IP packet is not delivered or is incorrectly delivered. Raw sockets can use ICMP and, like IP, ICMP is not typically used by application programs directly. ═══ 4.1.5. Address Resolution Protocol (ARP) ═══ ARP maps IP addresses to hardware addresses. TCP/IP uses ARP to collect and distribute the information for mapping tables. ARP is not directly available to users or applications. When an application sends an Internet packet, IP requests the appropriate address mapping. If the mapping is not in the mapping table, an ARP broadcast packet is sent to all the hosts on the local network requesting the physical hardware address for the host. ═══ 4.2. Getting Started with Sockets in the Internet Domain ═══ This section provides some basic information for getting started with sockets in the Internet domain:  Use the socket() call to create a socket in the Internet domain specifying PF_INET for the domain parameter.  Use AF_INET for the address family.  The following socket types are supported for the Internet domain: - Datagram (SOCK_DGRAM) - Raw (SOCK_RAW) - Stream (SOCK_STREAM) The socket type is passed as a parameter to the socket() call. For additional information, refer to Socket Types and general socket programming concepts in Sockets General Programming Information.  Datagram sockets use the UDP protocol, stream sockets use the TCP protocol, and raw sockets can use the raw, ICMP, or IGMP protocols.  Use the network utility routines to get addresses with a given name (refer to TCP/IP Specific Network Utility Routines for additional information). ═══ 4.3. Network-Byte Order ═══ Ports and addresses are specified to sockets API calls by using the network-byte ordering convention. Network-byte order is also known as big endian byte ordering, which has the high-order byte at the starting address. By contrast, little endian has the low-order byte at the starting address. Using network-byte ordering for data exchanged between hosts allows hosts using different underlying byte ordering conventions to exchange address information. There is a set of network utility API calls for translating addresses from host-byte to network-byte order and from network-byte to host-byte order. For more information about network-byte order and address translation, see:  bind()  htonl()  htons()  ntohl()  ntohs() Note: The socket interface does not handle application data byte ordering differences. Application writers must handle data buffer byte order differences themselves. ═══ 4.4. Internet Address Formats ═══ This section describes the address formats used in the Internet domain. Internet addresses (IP) are 32-bit values that represent a network interface. Every Internet address within an administered Internet (AF_INET) communication domain must be unique. A host can have as many Internet addresses as it has network interfaces. For more information about Internet address formats, see Internetworking with TCP/IP Volume I: Principles, Protocols, and Architectures, and Volume II: Implementation and Internals, Douglas E. Comer, Prentice Hall, 1991. Each Internet host is assigned at least one unique Internet address. This address is used by IP and other higher-level protocols. When a host is a gateway, it has more than one IP address. Gateway hosts connect two or more physical networks and have one IP address per connected physical network. Addresses within an Internet consist of a network number and a local address. All physical host IP addresses share the same network number and are logically part of the same network even if that network is connected with various physical media. Hosts on disjoint physical networks might also have the same network number, but are not part of the same Internet network. Hosts that are part of the same Internet network can exchange packets directly without going through intermediate routers. An Internet network can be subdivided logically using subnet mask. All host interfaces to the same physical network are given the same subnetwork number. An Internet domain can provide standards for assigning addresses to networks, broadcasts, and subnetworks. Dotted-Decimal Notation: A commonly used notation for Internet host addresses is the dotted-decimal format, which divides the 32-bit address into four 8-bit fields. The value of each field is specified as a decimal number, and the fields are separated by periods (for example, 10.2.0.52). Address examples in this document use dotted-decimal notation in the following forms:  nnn.lll.lll.lll  nnn.nnn.lll.lll  nnn.nnn.nnn.lll where: nnn represents part or all of a network number. lll represents part or all of a local address. Note: Additional details about Internet network address format class A, B, C, and D addresses, subnetwork address format, and broadcast address formats can be found in TCP/IP User's Guide. Addressing within an Internet Domain: A socket address in an Internet communication domain is composed of the five fields in the following sockaddr_in structure: length, address family, port, an Internet address, and a reserved field. The sockaddr_in structure should be cleared before use. The structure is located in the header file: struct in_addr { u_long s_addr; }; struct sockaddr_in { u_short sin_family; /* AF_INET */ u_short sin_port; /* port id */ struct in_addr sin_addr; /* address */ char sin_zero[8]; /* not used */ }; The sin_family field is set to AF_INET. The sin_port field is set to the port number in network-byte order. If you are specifying your workstation address in sin_addr and you set sin_portto 0 using the bind() call, the system assigns an available port. If you specify a different workstation address in sin_addr, you must specify the port. For more information on ports, see Ports. The sin_addr field is set to the Internet address represented in network-byte order. When specified as a parameter to bind(), sin_addr is usually set to the constant INADDR_ANY, as defined in in . This binds the socket to any and all local internet addresses. By using INADDR_ANY, an application can bind a socket without specifying the local internet address. The constant INADDR_ANY also allows an application running on a host with multiple interfaces (called a multihomed host) to receive UDP datagrams and TCP connection requests arriving at any interface on a single socket. (The application is not required to have one socket per interface, with each interface bound to a specific internet address). To specify your workstation address, you can leave sin_addr unspecified. If you are specifying a different workstation address, you must specify a valid internet address for that workstation. The sin_zero field is not used, and it should be set to 0 by the application before passing the address structure to any sockets call. ═══ 4.5. TCP/IP Specific Network Utility Routines ═══ This section describes the library of network utility routines. The TCP/IP protocol provides a library, TCP32DLL, that can be used in addition to the protocol-independent SO32DLL library. This library provides a set of network utility routines to perform useful tasks such as Internet address translation, domain name resolution, network-byte order translation, and access to the database of useful network information. This library can only be used for the PF_INET protocol family. Network utility routines are described in the following sections. Topics Host Names Information Network Names Information Protocol Names Information Service Names Information Network-Byte Order Translation Internet Address Manipulation Domain Name Resolution Ports ═══ 4.5.1. Host Names Information ═══ The following is a list of host related calls:  gethostbyname()  gethostbyaddr()  sethostent()  gethostent()  endhostent()  gethostname() The gethostbyname() call takes an Internet host name and returns a hostent structure, which contains the name of the host, aliases, host address family and host address. The hostent structure is defined in the header file. The gethostbyaddr() call maps the Internet host address into a hostent structure. The database for these calls is provided by the name server or the ETC\HOSTS file if a name server is not present or is unable to resolve the host name. The sethostent(), gethostent(), and endhostent() calls open, provide sequential access to, and close the ETC\HOSTS file. The gethostname() call gets the name for the local host machine. ═══ 4.5.2. Network Names Information ═══ The following is a list of network related calls:  getnetbyname()  getnetbyaddr()  setnetent()  getnetent()  endnetent() The getnetbyname() call takes a network name and returns a netent structure, which contains the name of the network, aliases, network address family, and network number. The netent structure is defined in the header file. The getnetbyaddr() call maps the network number into a netent structure. The database for these calls is provided by the ETC\NETWORKS file. The setnetent(), getnetent(), and endnetent() calls open, provide sequential access to, and close the ETC\NETWORKS file. ═══ 4.5.3. Protocol Names Information ═══ The following is a list of protocol related calls:  getprotobyname()  getprotobynumber()  setprotoent()  getprotoent()  endprotoent() The getprotobyname() call takes the protocol name and returns a protoent structure, which contains the name of the protocol, aliases, and protocol number. The protoent structure is defined in the header file. The getprotobynumber() call maps the protocol number into a protoent structure. The database for these calls is provided by the ETC\PROTOCOL file. The setprotoent(), getprotoent(), and endprotoent() calls open, provide sequential access to, and close the ETC\PROTOCOL file. ═══ 4.5.4. Service Names Information ═══ The following is a list of service related calls:  getservbyname()  getservbyport()  setservent()  getservent()  endservent() The getservbyname() call takes the service name and protocol, and returns a servent structure that contains the name of the service, aliases, port number, and protocol. The servent structure is defined in the header file. The getservbyport() call maps the port number and protocol into a servent structure. The database for these calls is provided by the ETC\SERVICES file. The setservent(), getservent(), and endservent() calls provide open, provide sequential access to, and close the ETC\SERVICES file. ═══ 4.5.5. Network-Byte Order Translation ═══ Internet domain ports and addresses are usually specified to calls using the network-byte ordering convention. The following calls translate integers from host- to network-byte order and from network- to host-byte order. htonl() Translates host to network, long integer(32-bit) htons() Translates host to network, short integer(16-bit) ntohl() Translates network to host, long integer(32-bit) ntohs() Translates network to host, short integer(16-bit) ═══ 4.5.6. Internet Address Manipulation ═══ The following calls convert Internet addresses and decimal notation, and manipulate the network number and local network address portions of an Internet address: inet_addr() Translates dotted-decimal notation to a 32-bit Internet address (network-byte order). inet_network() Translates dotted-decimal notation to a network number (host-byte order), and zeros in the host part. inet_ntoa() Translates 32-bit Internet address (network-byte order) to dotted-decimal notation. inet_netof() Extracts network number (host-byte order) from 32-bit Internet address (network-byte order). inet_lnaof() Extracts local network address (host-byte order) from 32-bit Internet address (network-byte order). inet_makeaddr() Constructs Internet address (network-byte order) from network number and local network address. ═══ 4.5.7. Domain Name Resolution ═══ In TCP/IP, communication is based on internet addresses. When a TCP/IP application receives a symbolic host name, it calls a host name resolver routine to resolve the symbolic name into an internet address. The host name resolver routine queries a domain name server or a local HOSTS file, or both, to perform the name resolution. If a RESOLV2 file or RESOLV file exists in the ETC subdirectory, the host name resolver routine first tries to resolve the name by querying the name servers specified in that file. If resolution through a name server fails or if a RESOLV2 or RESOLV file does not exist, the host name resolver routine tries to resolve the name locally by searching the HOSTS file in the ETC subdirectory for a match of the symbolic host name. If a match is found, the routine returns the corresponding internet address. If a match is not found, the routine displays a message stating that the host is unknown. ┌──────────────────────────────────────────────────────────┐ │ Table 7. RESOLV and RESOLV2 files │ ├────────────┬──────────────────────┬──────────────────────┤ │ NETWORK │ RESOLV FILE │ RESOLV2 FILE │ │ CONNECTION │ │ │ ├────────────┼──────────────────────┼──────────────────────┤ │ LAN only │ │ X │ │ connection │ │ │ ├────────────┼──────────────────────┼──────────────────────┤ │ SLIP only │ X │ │ │ connection │ │ │ ├────────────┼──────────────────────┼──────────────────────┤ │ LAN and │ X │ X (used for domain │ │ SLIP con- │ │ name resolution) │ │ nections │ │ │ └────────────┴──────────────────────┴──────────────────────┘ Note: TCP/IP does not include a RESOLV2 file. If you want to use one, you must create it and place it in the ETC subdirectory. If you use the Configuration notebook to configure name resolution, the Configuration notebook creates a RESOLV2 file for you. Otherwise you can create one manually. The following resolver calls are used to make, send, and interpret packets for name servers in the Internet domain:  res_mkquery()  res_send()  res_init()  dn_comp()  dn_expand() ═══ 4.5.8. Ports ═══ A port is used to differentiate between different applications on a host using the same protocol (TCP or UDP). It is an additional qualifier used by the system software to get data to the correct application. Physically, a port is a 16-bit integer. Some ports are reserved for particular applications and are called well-known ports. Well-Known Port Assignments contains the well-known port assignments list. ═══ 5. Sockets over Local IPC ═══ This section describes how sockets over Local IPC allow the programmer to communicate between applications on the same machine using the sockets API. Local IPC sockets are not bound to a network protocol, but rather use the underlying host facilities to provide high performance IPC. Topics Getting Started with Sockets Over Local IPC Local IPC Address Format ═══ 5.1. Getting Started with Sockets Over Local IPC ═══ This section provides some basic information for getting started with sockets over Local IPC:  Use PF_OS2, or PF_UNIX for the protocol family  Use AF_OS2, or AF_UNIX for the address family  The following socket types are supported for the Local IPC domain: - Datagram (SOCK_DGRAM) - Stream (SOCK_STREAM) The socket type is passed as a parameter to the socket() call. For additional information, refer to Socket Types and general socket programming concepts in Sockets General Programming Information.  A unique text string is used as a name. Refer to the following section for additional details.  If a connect() socket call is received without an explicit bind(), an implicit bind is automatically performed. In this case, the application does not care about its own name and a unique Local IPC name is generated by Network Services. You can retrieve the Local IPC name by using the getsockname() call. ═══ 5.2. Local IPC Address Format ═══ A socket address in a local system is composed of the three fields in the following sockaddr_un structure: length, address family, and the path name. The structure is located in the header file: struct sockaddr_un { u_short sun_family; /* AF_OS2 or AF_UNIX */ char sun_path[108]; /* path name */ }; struct sockaddr_un un; The sun_family field is set to AF_OS2 or AF_UNIX. The sun_path field is the OS/2 Warp Connect file and pathname to be used as the address of the Local IPC socket. If the address is NULL, the bind call binds a unique local address to the socket descriptor s. Each address is a combination of address family (sun_family) and a character string (sun_path) no longer than 108 characters. Each socket must use a unique character string as its local name to bind a name to a socket. The name in sun_path should begin with "\socket\". For example, struct sockaddr_un un; int sd; sd = socket(PF_OS2, SOCK_STREAM, 0); memset(&un, 0, sizeof(un)); un.sun_family=AF_OS2; strcpy(un.sun_path, "\socket\XYZ", 12); bind(sd, (struct sockaddr *)&un, sizeof(un)); ═══ 6. Sockets over NetBIOS ═══ This section describes the use of sockets with NetBIOS. Each application assigns itself one or more NetBIOS names for each adapter. The NetBIOS protocol maintains a table of the names that a node is known by on the network. NetBIOS supports two types of names: unique and group. When the name is unique, the application binds the name and NetBIOS checks the network to ensure that the name is not already being used as a unique name. NetBIOS supports multicast by allowing applications to bind to a group name and communicate. Topics Getting Started with Sockets Over NetBIOS NetBIOS Address Format ═══ 6.1. Getting Started with Sockets Over NetBIOS ═══ This section provides some basic information for getting started with sockets over NetBIOS:  Use PF_NETBIOS or PF_NB for the protocol family.  Use AF_NETBIOS or AF_NB for the address family.  The following socket types are supported for the NetBIOS domain: - Datagram (SOCK_DGRAM) - Sequenced packet (SOCK_SEQPACKET) The socket type is passed as a parameter to the socket() call. For additional information, refer to Socket Types and general socket programming concepts in Sockets General Programming Information.  If a connect() socket call is received without an explicit bind(), an implicit bind is automatically performed. In this case, the application does not care about its own name, and a unique NetBIOS name is generated by Network Services. You can retrieve the NetBIOS name by using the getsockname() call.  Applications using the NetBIOS communication domain can use sockets in both a connection-oriented (sequenced packet) and connectionless (datagram) mode.  A NetBIOS application on one workstation can use sockets to communicate with an NCB NetBIOS application on a different workstation. ═══ 6.2. NetBIOS Address Format ═══ A socket address in a NetBIOS address family is composed of the six fields in the following sockaddr_nb structure: length, address family, address type, a reserved field, adapter number, and NetBIOS name. This structure is located in the header file: struct sockaddr_nb { u_short snb_family; /* AF_NETBIOS */ short snb_type; /* 0=unique or 1=group */ char snb_nbnetid[NB_NETIDLEN]; /* RESERVED */ unsigned short snb_adapter; /* adapter number */ char snb_name[NAMELEN]; /* NetBIOS name */ } The snb_family field is set to AF_NETBIOS or AF_NB. The address type field (snb_type) is used to specify the name as either a unique (NB_UNIQUE) or a group (NB_GROUP) name. The snb_adapter field contains the adapter number that the name is associated with. The adapter number must be a numeric value in the range 0-255. The snb_name field contains the 16-byte NetBIOS name, and is used as is. If a connect() socket call is received without an explicit bind(), an implicit bind is automatically performed. In this case, the application does not care about its own name and is asking the system to select one for it. A NetBIOS name is generated for this socket by converting the 6-byte MAC address to an ASCII hex string, and postpended with a 2-byte number that increments after each use. You can retrieve the NetBIOS name by using the getsockname() call. Note that for the NetBIOS domain, more than one socket can be bound to the same local address to establish multiple connections to one or more remote destinations. To enable this feature, the socket option SO_REUSEADDR must be set. See setsockopt(). In addition, you can bind more than one address to the same adapter. ═══ 7. Protocol-Independent C Sockets API ═══ The following table briefly describes each protocol-independent socket call supported by Network Services and identifies where you can find the syntax, parameters, and other appropriate information. The socket calls described in this section can be used to access services for all protocols: Note: If you are using the Internet communications domain (PF_INET protocol family), you can use all APIs in the following table and those in TCP/IP Network Utility Routines. ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 8. Protocol-Independent Sockets API Quick Reference │ ├─────────────────┬────────────────────────────────────────────────────┬───────────────────────────────────────────────┤ │ SOCKET CALL │ DESCRIPTION │ LOCATION │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ accept() │ Accepts a connection request from a remote host │ accept() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ bind() │ Binds a local name to the socket │ bind() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ connect() │ Requests a connection to a remote host │ connect() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ getpeername() │ Gets the name of the peer connected to socket │ getpeername() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ getsockname() │ Gets the local socket name │ getsockname() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ getsockopt() │ Gets the socket options associated with a socket │ getsockopt() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ ioctl() │ Performs special operations on socket │ ioctl() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ listen() │ Completes the binding necessary for a socket to │ listen() │ │ │ accept connections and creates a connection │ │ │ │ request queue for incoming requests │ │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ psock_error() │ Writes a short error message to the standard error │ psock_errno() │ │ │ device │ │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ readv() │ Receives data on a socket into a set of buffers │ readv() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ recv() │ Receives data on a connected socket │ recv() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ recvfrom() │ Receives data on a socket │ recvfrom() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ recvmsg() │ Receives data and control information on a socket │ recvmsg() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ select() │ Gets read, write, and exception status on a group │ select() │ │ │ of sockets │ │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ send() │ Sends data on a connected socket │ send() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ sendmsg() │ Sends data and control information on a socket │ sendmsg() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ sendto() │ Sends data on a socket │ sendto() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ setsockopt() │ Sets options associated with a socket │ setsockopt() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ shutdown() │ Shuts down all or part of a full duplex connection │ shutdown() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ so_cancel() │ Cancels a pending blocking sockets API call on a │ so_cancel() │ │ │ socket. │ │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ sock_errno() │ Returns error code set by a socket call │ sock_errno() │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ socket() │ Creates an endpoint for communication and returns │ socket() │ │ │ a socket descriptor representing the endpoint │ │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ soclose() │ Shuts down a socket and frees resources allocated │ soclose() │ │ │ to the socket │ │ ├─────────────────┼────────────────────────────────────────────────────┼───────────────────────────────────────────────┤ │ writev() │ Writes data from a set of specified buffers on a │ writev() │ │ │ socket │ │ └─────────────────┴────────────────────────────────────────────────────┴───────────────────────────────────────────────┘ ═══ 7.1. accept() ═══ The accept() socket call accepts a connection request from a remote host. Syntax #include #include #include #include #include int accept(s, name, namelen) int s; sockaddr *name; int *namelen; Parameters s Socket descriptor name Pointer to a sockaddr structure that contains the socket address of the connection client when the accept() call returns. The format of name is determined by the communications domain where the client resides. This parameter can be NULL if the caller is not interested in the client address. namelen Must initially point to an integer that contains the size in bytes of the storage pointed to by name. On return, that integer contains the size of the data returned in the storage pointed to by name. If name is NULL, namelen is ignored and can be NULL. Description This call is used by a server acting in a connection-oriented mode to accept a connection request from a client. The call accepts the first connection on its queue of pending connection requests. The accept() call creates a new socket descriptor with the same properties as s and returns it to the caller. The new socket descriptor cannot be used to accept new connections. The original socket, s, remains available to accept more connection requests. If the queue has no pending connection requests, accept() blocks the caller unless s is in nonblocking mode. If no connection requests are queued and s is in nonblocking mode, accept() returns -1 and sets the return code to SOCEWOULDBLOCK. The s parameter must be a socket descriptor created with the socket() call. It is usually bound to an address with the bind() call and must be made capable of accepting connections with the listen() call. The listen() call marks the socket as one that accepts connections and allocates a queue to hold pending connection requests. The listen() call allows the caller to place an upper boundary on the size of the queue. The name parameter is a pointer to a buffer where the connection requester address is placed. The name parameter is optional and can be set to be the NULL pointer. If set to NULL, the requester address is not copied into the buffer. The exact format of name depends on the communications domain where the communication request originated. For example, if the connection request originated in the Internet domain, name points to a sockaddr_in structure as defined in the header file . The namelen parameter is used only if name is not NULL. Before calling accept(), you must set the integer pointed to by namelen to the size, in bytes, of the buffer pointed to by name. On successful return, the integer pointed to by namelen contains the actual number of bytes copied into the buffer. If the buffer is not large enough to hold the address, up to namelen bytes of the requester address are copied. This call is used only with SOCK_STREAM or SOCK_SEQPACKET sockets. You cannot screen requesters without calling accept(). The application cannot tell the system the requesters it will accept connections from. The caller can, however, choose to close a connection immediately after discovering the identity of the requester. The select() call can be used to check the socket for incoming connection requests. Return Values A non-negative socket descriptor indicates success; the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using name and namelen would result in an attempt to copy the address into a portion of the caller address space into which information cannot be written. SOCEINTR Interrupted system call. SOCEINVAL Listen() was not called for socket s. SOCENOBUFS Insufficient buffer space available to create the new socket. SOCEOPNOTSUPP The s parameter is not connection-oriented. SOCEWOULDBLOCK The s parameter is in nonblocking mode and no connections are on the queue. SOCECONNABORTED The software caused a connection close. Examples The following are two examples of the accept() call. In the first, the caller wants to have the requester address returned. In the second, the caller does not want to have the requester address returned. int clientsocket; int s; struct sockaddr clientaddress; int addrlen; int accept(int s, struct sockaddr *addr, int *addrlen); /* extracted from sys/socket.h */ /* socket(), bind(), and listen() have been called */ /* EXAMPLE 1: I want the address now */ addrlen = sizeof(clientaddress); clientsocket = accept(s, &clientaddress, &addrlen); /* EXAMPLE 2: I can get the address later using getpeername() */ clientsocket = accept(s, (struct sockaddr *) 0, (int *) 0); ═══ 7.2. bind() ═══ The bind() socket call binds a local name to the socket. Syntax #include #include int bind(s, name, namelen) int s; struct sockaddr *name; int namelen; Parameters s Socket descriptor returned by a previous call to socket() name Pointer to a sockaddr structure containing the name that is to be bound to s namelen Size in bytes of the sockaddr structure pointed to by name Description The bind() call binds a unique local name to the socket with descriptor s. After calling socket(), a descriptor does not have a name associated with it. However, it does belong to a particular addressing family as specified when socket() is called. The exact format of a name depends on the addressing family. The bind() procedure also allows servers to specify from which network interfaces they wish to receive UDP packets and TCP connection requests. Because s was created in the AF_INET domain, the format of the name buffer is expected to be sockaddr_in as defined in the header file : struct in_addr { u_long s_addr; }; struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; The sin_family field must be set to AF_INET. The sin_port field is set to the port to which the application must bind. It must be specified in network byte order. If sin_port is set to 0, the caller leaves it to the system to assign an available port. The application can call getsockname() to discover the port number assigned. The sin_addr field is set to the internet address and must be specified in network byte order. On hosts with more than one network interface (called multihomed hosts), a caller can select the interface with which it is to bind. Subsequently, only UDP packets and TCP connection requests from this interface (which match the bound name) are routed to the application. If sin_addr is set to the constant INADDR_ANY, as defined in , the caller is requesting that the socket be bound to all network interfaces on the host. Subsequently, UDP packets and TCP connections from all interfaces (which match the bound name) are routed to the application. This becomes important when a server offers a service to multiple networks. By leaving the address unspecified, the server can accept all UDP packets and TCP connection requests made for its port, regardless of the network interface on which the requests arrived. The sin_zero field is not used and must be set to all zeros. In the NetBIOS (AF_NET) domain, set all 16 characters in snb_name in the sockaddr_nb structure to binary zero's (null). The system will generate a name for the socket. Return Values The value 0 indicates success; the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCEADDRINUSE The address is already in use. See the SO_REUSEADDR option described under getsockopt() and the SO_REUSEADDR option described under setsockopt(). SOCEADDRNOTAVAIL The address specified is not valid on this host. For example, the Internet address does not specify a valid network interface. SOCEAFNOSUPPORT The address family is not supported. SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using name and namelen would result in an attempt to copy the address into a non-writable portion of the caller's address space. SOCEINVAL The socket is already bound to an address, or namelen is not the expected length. SOCENOBUFS No buffer space is available. Examples Note the following about the bind() call examples:  For the Internet examples, put the Internet address and port in network-byte order. To put the port into network-byte order, use the htons() utility routine to convert a short integer from host-byte order to network-byte order.  For the Internet examples, set the address field using the inet_addr() utility routine, which takes a character string representing the dotted-decimal address of an interface and returns the binary Internet address representation in network-byte order.  Zero the structure before using it to ensure that the name requested does not set any reserved fields. See connect() for examples of how a client might connect to servers. int rc; int s; struct sockaddr_in myname; int bind(int s, struct sockaddr *name, int namelen); /* extracted from sys/socket.h */ /* Bind to a specific interface in the Internet domain */ /* clear the structure */ memset(&myname, 0, sizeof(myname)); myname.sin_family = AF_INET; myname.sin_addr = inet_addr("129.5.24.1"); /* specific interface */ myname.sin_port = htons(1024); . . . rc = bind(s, (struct sockaddr *) &myname, sizeof(myname)); /* Bind to all Internet network interfaces on the system */ /* clear the structure */ memset(&myname, 0, sizeof(myname)); myname.sin_family = AF_INET; myname.sin_addr.s_addr = INADDR_ANY; /* all interfaces */ myname.sin_port = htons(1024); . . . rc = bind(s, (struct sockaddr *) &myname, sizeof(myname)); /* Bind to a specific interface in the Internet domain. Let the system choose a port */ /* clear the structure */ memset(&myname, 0, sizeof(myname)); myname.sin_family = AF_INET; myname.sin_addr = inet_addr("129.5.24.1"); /* specific interface */ myname.sin_port = 0; . . . rc = bind(s, (struct sockaddr *) &myname, sizeof(myname)); /* Bind to a unique NetBIOS name on adapter 0 */ struct sockaddr_nb nbname; memset(&nbname, 0, sizeof(nbname)); nbname.snb_family = AF_NB; nbname.snb_type = NB_UNIQUE; nbname.snb_adapter = 0; strcpy(nbname.snb_name, "NBSERVER"); /* Note that a NetBIOS name is 16 bytes long. In this example, the last 8 bytes are filled with zeros. */ . . . rc = bind(s, (struct sockaddr *) &nbname, sizeof(nbname)); ═══ 7.3. connect() ═══ The connect() socket call requests a connection to a remote host. Syntax #include #include int connect(s, name, namelen) int s; struct sockaddr *name; int namelen; Parameters s Socket descriptor used to originate the connection request name Pointer to a sockaddr structure containing the address of the socket to which a connection will be attempted namelen Size in bytes of the sockaddr structure pointed to by name Description Stream or sequenced packet sockets: The connect() call performs two tasks when called for a stream or sequenced packet socket: 1) it completes the binding if necessary for a socket, and 2) it attempts to create a connection between two sockets. This call is used by the client side of socket-based applications to establish a connection with a server. The remote server must have a passive open pending. This means the server must successfully call bind() and listen() Otherwise, connect() returns -1 and the error value is set to SOCECONNREFUSED. In the Internet communication domain, a timeout occurs if a connection to the remote host is not successful within 75 seconds (1 minute and 15 seconds). There is no timeout for Local IPC. In the NetBIOS communication domain, a timeout occurs if a connection to the host is not successful within the time defined by the NetBIOS protocol parameters Transmit Timer multiplied by Transmit Retry. If s is in blocking mode, the connect() call blocks the caller until the connection is established or until an error is received. If the socket is in nonblocking mode, connect() returns -1 and sets the error value to SOCEINPROGRESS if the connection was successfully initiated. The caller can test the completion of the connection setup by calling: 1) select() to test for the ability to write to the socket, and 2) getsockopt() with option SO_ERROR to test if the connection succeeded. Stream or sequenced packet sockets can call connect() only once. Datagram or raw sockets: The connect() call specifies the destination peer address when called for a datagram or raw socket. Normally, datagram and raw sockets use connectionless data transfer calls such as sendto() and recvfrom(). However, applications can call connect() to specify and store the destination peer address for this socket. The system will then know which address to send data to on this socket. This method of communication allows datagram and raw sockets to be connected. However, data is still not guaranteed to be delivered. Thus, the normal features of connectionless mode sockets is maintained. The address is remembered until another connect() call is made. This permits the use of readv(), recv(), send(), and writev(), which are usually reserved for connection-oriented sockets. The application can still use sendto(), recvfrom(), sendmsg(), and recvmsg(). The advantage of calling connect() and being connected is that the destination peer address does not have to be specified for all datagrams sent. Datagram and raw sockets can call connect() multiple times. The application can reset their destination address by specifying a new address on the connect() call. In addition, the socket can be returned to operate in a connectionless mode by calling connect() with a null destination address. The null address is created by zeroing the sockaddr structure and only setting the address family field. The call to connect will return -1, indicating that the connection to the null address cannot be established. Calling sock_errno() will return SOCEADDRNOTAVAIL. For more information on connecting datagram sockets, see "Description" for sendto(). Return Values The value 0 indicates success; the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCEADDRNOTAVAIL The calling host cannot reach the specified destination. SOCEAFNOSUPPORT The address family is not supported. SOCEALREADY The socket s is marked nonblocking, and a previous connection attempt has not completed. SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCECONNREFUSED The connection request was rejected by the destination host. SOCEFAULT Using name and namelen would result in an attempt to copy the address into a portion of the caller's address space to which data cannot be written. SOCEINPROGRESS The socket s is marked nonblocking, and the connection cannot be completed immediately. The SOCEINPROGRESS value does not indicate an error condition. SOCEINTR Interrupted system call. SOCEINVAL The namelen parameter is not a valid length. SOCEISCONN The socket s is already connected. SOCENETUNREACH The network cannot be reached from this host. SOCETIMEDOUT The connection establishment timed out before a connection was made. SOCENOBUFS No buffer space is available. SOCEOPNOTSUPP The operation is not supported on socket s. Examples Note the following about these connect() call examples:  For the Internet examples, put the Internet address and port in network-byte order. To put the port into network-byte order, use the htons() utility routine to convert a short integer from host-byte order to network-byte order.  For the Internet examples, set the address field using the inet_addr() utility routine, which takes a character string representing the dotted-decimal address of an interface and returns the binary Internet address representation in network-byte order.  To ensure that the name requested does not set any reserved fields, zero the structure before using it. These examples could be used to connect to the servers shown in the examples listed for bind(). int s; struct sockaddr_in servername; int rc; int connect(int s, struct sockaddr *name, int namelen); /* extracted from sys/socket.h */ /* Connect to server bound to a specific interface in the Internet domain */ /* clear the structure */ memset(&servername, 0, sizeof(servername)); servername.sin_family = AF_INET; servername.sin_addr.s_addr = inet_addr("129.5.24.1"); /* specific interface */ servername.sin_port = htons(1024); /* set to the port to which */ /* the server is bound */ . . . rc = connect(s, (struct sockaddr *) &servername, sizeof(servername)); /* Connect to a NetBIOS server */ struct sockaddr_nb nbservername; memset(&nbservername, 0, sizeof(nbservername)); nbservername.snb_family = AF_NB; nbservername.snb_type = NB_UNIQUE; nbservername.snb_adapter = 0; strcpy(nbservername.snb_name, "NBSERVER"); . . . rc = connect(s, (struct sockaddr *) &nbservername, sizeof(nbservername)); ═══ 7.4. getpeername() ═══ The getpeername() socket call gets the name of the peer connected to socket. Syntax #include #include int getpeername(s, name, namelen) int s; struct sockaddr *name; int *namelen; Parameters s Socket descriptor name Pointer to a sockaddr structure. The name of the peer connected to socket s is returned. The exact format of name is determined by the domain where communication occurs. namelen Pointer to the size in bytes of the sockaddr structure pointed to by name Description This call returns the name of the peer connected to socket s. The namelen parameter must be initialized to indicate the size of the space pointed to by name. On return, namelen is set to the size of the peer name copied. If the buffer is too small, the peer name is truncated. This call operates only on connected sockets. Return Values The value 0 indicates success; the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using the name and namelen parameters as specified would result in an attempt to access storage outside of the address space of the caller. SOCENOTCONN The socket is not connected. SOCENOBUFS No buffer space is available. ═══ 7.5. getsockname() ═══ The getsockname() socket call gets the local socket name. Syntax #include #include int getsockname(s, name, namelen) int s; struct sockaddr *name; int *namelen; Parameters s Socket descriptor name Pointer to a sockaddr structure. The name of s is returned. namelen Pointer to the size in bytes of the buffer pointed to by name Description This call returns the name for the socket specified by the s parameter in the structure pointed to by the name parameter. It returns the address to the socket that has been bound. If the socket is not bound to an address, the call returns with the family set and the rest of the structure is set to zero. For example, an unbound socket in the Internet domain would cause the name to point to a sockaddr_in structure with the sin_family field set to AF_INET and all other fields zeroed. The namelen parameter must be initialized to indicate the size of the space pointed to by name and is set to the size of the local name copied. If the buffer is too small, the local name is truncated. Sockets are explicitly assigned a name after a successful call to bind(). Stream and sequenced packet sockets are implicitly assigned a name after a successful call to connect() or accept() if bind() was not called. The getsockname() call is often used to discover the port assigned to a socket after the socket has been implicitly bound to a port. For example, an application can call connect() without previously calling bind(). In this case, the connect() call completes the binding necessary by assigning a port to the socket. Return Values The value 0 indicates success; the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using the name and namelen parameters as specified would result in an attempt to access storage outside of the address space of the caller. SOCENOBUFS No buffer space available. ═══ 7.6. getsockopt() ═══ The getsockopt() socket call gets the socket options associated with a socket. Syntax #include #include int getsockopt(s, level, optname, optval, optlen) int s; int level; int optname; char *optval; int *optlen; Parameters s Socket descriptor level Specifies which option level is being queried for the specified optname optname Name of a specified socket option. Only one option can be specified on a call. optval Pointer to buffer to receive the option data requested optlen Pointer to the size of the buffer Description This call returns the value of a socket option at the socket or protocol level. It can be called for sockets of all domain types. Some options are supported only for specific socket types. You must specify the level of the option and the name of the option to retrieve option values. The following table lists the supported levels. ┌─────────────────────────────────────────────────┐ │ Table 9. Supported Levels │ ├──────────────────────┬──────────────────────────┤ │ Supported Level │ #define in │ ├──────────────────────┼──────────────────────────┤ │ SOL_SOCKET │ │ ├──────────────────────┼──────────────────────────┤ │ IPPROTO_IP │ │ ├──────────────────────┼──────────────────────────┤ │ IPPROTO_TCP │ │ ├──────────────────────┼──────────────────────────┤ │ NBPROTO_NB │ │ └──────────────────────┴──────────────────────────┘ The optval parameter is a pointer to the buffer where the option values are returned. The optlen parameter must be initially set to the size of the buffer before calling getsockopt(). On return, the optlen parameter is set to the actual size of the data returned. For socket options that are boolean, the option is enabled if optval is nonzero and disabled if optval is 0. The following tables list the supported options for getsockopt() at each level (SOL_SOCKET, IPPROTO_IP, IPPROTO_TCP). Detailed descriptions of the options follow each table. ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 10. Supported getsockopt() Socket Options for SOL_SOCKET │ ├──────────────────┬───────────────────────────┬──────────────────────────────┬────────────────────┬───────────────────┤ │ │ │ │ │ BOOLEAN/ │ │ OPTION NAME │ DESCRIPTION │ DOMAINS(*) │ DATA TYPE │ VALUE │ ├──────────────────┼───────────────────────────┼─────────┬─────────┬──────────┼────────────────────┼───────────────────┤ │ SO_BROADCAST │ allows sending of broad- │ I │ │ N │ int │ boolean │ │ │ cast messages │ │ │ │ │ │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_DEBUG │ turn on recording of │ I │ L │ │ int │ boolean │ │ │ debugging information │ │ │ │ │ │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_DONTROUTE │ bypass routing tables │ I │ L │ │ int │ boolean │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_ERROR │ get any pending error and │ I │ L │ │ int │ value │ │ │ clear │ │ │ │ │ │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_KEEPALIVE │ keep connections alive │ I │ │ │ int │ boolean │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_LINGER │ linger on close if data │ I │ │ │ struct linger │ value │ │ │ present │ │ │ │ │ │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_OOBINLINE │ leave received OOB data │ I │ │ │ int │ boolean │ │ │ in-line │ │ │ │ │ │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_RCVBUF │ receive buffer size │ I │ L │ N │ long │ value │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_RCVLOWAT │ receive low-water mark │ I │ L │ │ int │ value │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_REUSEADDR │ allow local address reuse │ I │ │ N │ int │ boolean │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_SNDBUF │ send buffer size │ I │ L │ N │ long │ value │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_SNDLOWAT │ send low-water mark │ I │ L │ │ int │ value │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_TYPE │ get socket type │ I │ L │ N │ int │ value │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_USELOOPBACK │ bypass hardware when pos- │ I │ L │ │ int │ value │ │ │ sible │ │ │ │ │ │ ├──────────────────┴───────────────────────────┴─────────┴─────────┴──────────┴────────────────────┴───────────────────┤ │ NOTE: (*) This column specifies I for Internet, L for Local IPC, and N for NetBIOS communication domains. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ The following options are recognized for SOL_SOCKET: Option Description SO_BROADCAST (datagram sockets only) Retrieves the current ability of the socket to broadcast messages. If this option is enabled, it allows the application to send broadcast messages over s, if the interface specified in the destination supports broadcasting of packets. SO_DEBUG Retrieves the current ability for recording debug information for a socket. SO_DONTROUTE Retrieves the current ability for the socket to bypass routing. When this option is enabled, it causes outgoing messages to bypass the standard routing algorithm and be directed to the appropriate network interface, according to the network portion of the destination address. When enabled, packets can be sent only to directly connected networks (networks this host has an interface for). SO_ERROR Returns any pending error on the socket and clears the error status. It can be used to check for asynchronous errors on connected datagram sockets or for other asynchronous errors (errors that are not returned explicitly by one of the socket calls). SO_KEEPALIVE (stream sockets only) Retrieves the current ability of the socket to send keepalive packets. TCP uses a timer called the keepalive timer. This timer is used to monitor idle connections that might have been disconnected because of a peer crash or timeout. If this option is set on, a keepalive packet is periodically sent to the peer. This is mainly used to allow servers to close connections that are no longer active as a result of clients going away without properly closing connections. SO_LINGER (stream sockets only) Retrieves the current ability of the socket to linger on close if data is present. When this option is enabled and there is unsent data present when soclose() is called, the calling application is blocked during the soclose() call until the data is transmitted or the connection has timed out. If this option is disabled, the soclose() call returns without blocking the caller, and TCP waits to try to send the data. Although the data transfer is usually successful, it cannot be guaranteed, because TCP waits only a finite amount of time trying to send the data. SO_OOBINLINE (stream sockets only) Retrieves the current ability of the socket to receive out-of-band data. When this option is enabled, it causes out-of-band data to be placed in the normal data input queue as it is received, making it available to recv(), and recvfrom() without having to specify the MSG_OOB flag in those calls. When this option is disabled, it causes out-of-band data to be placed in the priority data input queue as it is received, making it available to recv(), and recvfrom(), only by specifying the MSG_OOB flag in those calls. SO_RCVBUF Retrieves buffer size for input. This value tailors the receive buffer size for specific application needs, such as increasing the buffer size for high-volume connections. SO_RCVLOWAT Retrieves receive low-water mark information. SO_REUSEADDR (stream and datagram sockets only) Retrieves the current ability of the socket to reuse local addresses. When enabled, this option allows local addresses that are already in use to be bound. This alters the normal algorithm used in the bind() call. At connect time, the system checks to be sure that no local address and port have the same foreign address and port. The error SOCEADDRINUSE is returned if the association already exists. SO_SNDBUF Retrieves the size of the send buffer. This value tailors the send buffer size for specific application needs, such as increasing the buffer size for high-volume connections. SO_SNDLOWAT Retrieves send low-water mark information. For the NetBIOS domain, this value limits the length of a blocking send call. This value is ignored for nonblocking calls. For the Internet domain, this value is not used. SO_TYPE Returns the type of the socket. On return, the integer pointed to by optval is set to one of the following: SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW. SO_USELOOPBACK Bypasses hardware when possible. struct linger: For the SO_LINGER option, optval points to a linger structure. This structure is defined in and contains the following fields: Field Description l_onoff Option on/off l_linger Linger time The l_onoff field is set to zero if the SO_LINGER option is being disabled. A nonzero value enables the option. The l_linger field specifies the amount of time in seconds to linger on close. A value of zero will cause so_close() to wait until the disconnect completes. ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 11. Supported getsockopt() Socket Options for IPPROTO_TCP │ ├──────────────────────┬────────────────────────────────┬─────────────────────────────────────┬────────────────────────┤ │ Option Name │ Description │ Data Type │ Boolean/ │ │ │ │ │ Value │ ├──────────────────────┼────────────────────────────────┼─────────────────────────────────────┼────────────────────────┤ │ TCP_NODELAY │ don't delay send to coalesce │ int │ boolean │ │ │ packets │ │ │ └──────────────────────┴────────────────────────────────┴─────────────────────────────────────┴────────────────────────┘ The following options are recognized for IPPROTO_TCP: Option Description TCP_NODELAY (stream sockets only) Retrieves the current ability of the socket to disable the buffering algorithm so that the client's TCP sends small packets as soon as possible. This often has no performance effects on LANs, but can degrade performance on Wide Area Networks (WAN). ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 12. Supported getsockopt() Socket Options for NBPROTO_NB │ ├──────────────────────┬────────────────────────────────┬─────────────────────────────────────┬────────────────────────┤ │ Option Name │ Description │ Data Type │ Boolean/ │ │ │ │ │ Value │ ├──────────────────────┼────────────────────────────────┼─────────────────────────────────────┼────────────────────────┤ │ NB_DGRAM_TYPE │ type of datagrams to receive │ int │ value │ └──────────────────────┴────────────────────────────────┴─────────────────────────────────────┴────────────────────────┘ The following option is recognized for NBPROTO_NB: Option Description NB_DGRAM_TYPE (datagram sockets only) Gets type of datagrams to be received on the socket. The possible values are: NB_DGRAM The socket is to receive normal (unicast) datagrams only. NB_BROADCAST The socket is to receive broadcast datagrams only. NB_DGRAM_ANY The socket can receive both normal or broadcast datagrams. Return Values The value 0 indicates success; the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). sock_errno() Value Description SOCEADDRINUSE The address is already in use. SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using optval and optlen parameters would result in an attempt to access memory outside the caller's address space. SOCENOPROTOOPT The optname parameter or level parameter is not recognized. Examples The following are examples of the getsockopt() call. See setsockopt() for examples of how the options are set. int rc; int s; int optval; int optlen; struct linger lstruct; int getsockopt(int s, int level, int optname, char *optval, int *optlen); /* extracted from sys/socket.h */ . . . /* Is out of band data in the normal input queue? */ optlen = sizeof(int); rc = getsockopt( s, SOL_SOCKET, SO_OOBINLINE, (char *) &optval, &optlen); if (rc == 0) { if (optlen == sizeof(int)) { if (optval) /* yes it is in the normal queue */ else /* no it is not */ } } . . . /* Do I linger on close? */ optlen = sizeof(lstruct); rc = getsockopt( s, SOL_SOCKET, SO_LINGER, (char *) &lstruct, &optlen); if (rc == 0) { if (optlen == sizeof(lstruct)) { if (lstruct.l_onoff) /* yes I linger */ else /* no I do not */ } } ═══ 7.7. ioctl() ═══ The ioctl() socket call performs special operations on socket. Syntax #include #include #include #include #include #include int ioctl(s, cmd, data, lendata) int s; int cmd; caddr_t data; int lendata; Parameters s Socket descriptor cmd Command to perform data Pointer to the data associated with cmd lendata Length of the data in bytes Description This call controls the operating characteristics of sockets. The data parameter is a pointer to data associated with the particular command, and its format depends on the command that is requested. Option Description FIOASYNC This option has no effect. FIONBIO Sets or clears nonblocking input/output for a socket. When this option is set, input/output calls will not block until the call is completed. The data parameter is a pointer to an integer. If the integer is 0, nonblocking input/output on the socket is cleared. Otherwise, the socket is set for nonblocking input/output. FIONREAD Gets the number of immediately readable bytes for the socket. The data parameter is a pointer to an integer. Sets the value of the integer to the number of immediately readable characters for the socket. Internet: The following ioctl commands are supported for the Internet domain: Option Description SIOCADDRT Adds a routing table entry. data is a pointer to a rtentry structure, as defined in . The routing table entry, passed as an argument, is added to the routing tables. SIOCATMARK Queries whether the current location in the data input is pointing to out-of-band data. data is a pointer to an integer. Sets the argument to 1 if the socket points to a mark in the data stream for out-of-band data. Otherwise, sets the argument to 0. SIOCDARP Deletes an arp table entry. data is a pointer to a arpreq as defined in . The arp table entry passed as an argument is deleted from the arp tables, if it exists. SIOCDELRT Deletes a routing table entry. data is a pointer to a rtentry structure, as defined in . If it exists, the routing table entry passed as an argument is deleted from the routing tables. SIOCGARP Gets the arp table entries. data is a pointer to an arpreq, as defined in . The arp table entry passed as an argument is returned from the arp tables if it exists. SIOCGIFADDR Gets the network interface address. data is a pointer to an ifreq structure, as defined in . The interface address is returned in the argument. SIOCGIFBRDADDR Gets the network interface broadcast address. data is a pointer to an ifreq structure, as defined in . The interface broadcast address is returned in the argument. SIOCGIFCONF Gets the network interface configuration. data is a pointer to an ifconf structure, as defined in . The interface configuration is returned in the argument. SIOCGIFDSTADDR Gets the network interface destination address. data is a pointer to an ifreq structure, as defined in . The interface destination (point-to-point) address is returned in the argument. SIOCGIFFLAGS Gets the network interface flags. data is a pointer to an ifreq structure, as defined in . The interface flags are returned in the argument. SIOCGIFMETRIC Gets the network interface routing metric. data is a pointer to an ifreq structure, as defined in . The interface routing metric is returned in the argument. SIOCGIFNETMASK Gets the network interface network mask. data is a pointer to an ifreq structure, as defined in . The interface network mask is returned in the argument. SIOCSARP Sets an arp table entry. data is a pointer to an arpreq as defined in . The arp table entry passed as an argument is added to the arp tables. SIOCSIFADDR Sets the network interface address. data is a pointer to an ifreq structure, as defined in . Sets the interface address to the value passed in the argument. SIOCSIFBRDADDR Sets the network interface broadcast address. data is a pointer to an ifreq structure, as defined in . Sets the interface broadcast address to the value passed in the argument. SIOCSIFDSTADDR Sets the network interface destination address. data is a pointer to an ifreq structure, as defined in . Sets the interface destination (point-to-point) address to the value passed in the argument. SIOCSIFFLAGS Sets the network interface flags. data is a pointer to an ifreq structure, as defined in . Sets the interface flags to the values passed in the argument. SIOCSIFMETRIC Sets the network interface routing metric. data is a pointer to an ifreq structure, as defined in . Sets the interface routing metric to the value passed in the argument. SIOCSIFNETMASK Sets the network interface network mask. data is a pointer to an ifreq structure, as defined in . Sets the interface network mask to the value passed in the argument. Return Values The value 0 indicates success; the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). sock_errno() Value Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEINVAL The request is not valid or not supported. SOCEOPNOTSUPP The operation is not supported on the socket. SOCEFAULT Using data and lendata would result in an attempt to access memory outside the caller address space. Examples The following is an example of the ioctl() call. int s; int dontblock; int rc; int ioctl(int s, int cmd, caddr_t data, int lendata); /* extracted from sys\socket.h */ . . . /* Place the socket into nonblocking mode */ dontblock = 1; rc = ioctl(s, FIONBIO, (char *) &dontblock, sizeof(dontblock)); . . . ═══ 7.8. listen() ═══ The listen() socket call completes the binding necessary for a socket to accept connections and creates a connection request queue for incoming requests. Syntax #include #include int listen(s, backlog) int s; int backlog; Parameters s Socket descriptor. backlog Controls the maximum queue length for pending connections. Description The listen() call performs two tasks: 1) completes the binding necessary for a socket s, if bind() has not been called for s. 2) creates a connection request queue of length backlog to queue incoming connection requests. When the queue is full, additional connection requests are ignored. The listen() call indicates a readiness to accept client connection requests. It transforms an active socket into a passive socket. After listen() is called, s can never be used as an active socket to initiate connection requests. Listen() is called after allocating a socket with socket() and after binding a name to s with bind(). Listen() must be called before calling accept(). Listen() can only be called on connection-oriented sockets. If the backlog parameter is less than 0, then listen() interprets backlog as 0. If the backlog parameter is greater than SOMAXCONN, as defined in , then listen() interprets backlog as SOMAXCONN. Return Values The value 0 indicates success, the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEOPNOTSUPP The s parameter is not a socket descriptor that supports the listen() call. ═══ 7.9. psock_errno() ═══ The psock_errno() socket call writes a short error message to the standard error device. Syntax #include void psock_errno(s) char *s; Parameters s Pointer to a buffer Description This call writes a short error message to the standard error display describing the last error encountered during a call to a socket library function. If s is not a NULL pointer and does not point to a null string, the string it points to is printed, followed by a colon, followed by a space, followed by the message. If s is a NULL pointer or points to a null string, only the message is printed. The error code is acquired by calling sock_errno(). The error code is set when errors occur. Subsequent socket calls do not clear the error code. ═══ 7.10. readv() ═══ The readv() socket call receives data on a socket into a set of buffers. Syntax #include #include int readv(s, iov, iovcnt) int s; struct iovec *iov; int iovcnt; Parameters s Socket descriptor iov Pointer to an array of iovec structures iovcnt Number of iovec structures pointed to by the iov parameter. The maximum number of iovec structures is 16. Description This call reads data on a socket with descriptor s and stores it in a set of buffers. The data is scattered into the buffers specified by iov[0]...iov[iovcnt-1]. The iovec structure is defined in and contains the following fields: Field Description iov_base Points to the buffer iov_len Length of the buffer The readv() call applies only to connected sockets. For information on how to use readv() with datagram and raw sockets, see "Datagram or raw sockets" in connect(). This call returns up to the number of bytes in the buffers pointed to by the iov parameter. This number is the sum of all iov_len fields. If less than the number of bytes requested is available, the call returns the number currently available. If data is not available at the socket with descriptor s, the readv() call waits for data to arrive and blocks the caller, unless the socket is in nonblocking mode. See ioctl() for a description of how to set nonblocking mode. The UDP sockets can send and receive datagrams as large as 32739 bytes (32 * 1024, -1, - IP header (20 bytes) - UDP header (8 bytes)). Return Values If successful, the number of bytes read into the buffers is returned. The value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). sock_errno() Value Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using iov and iovcnt would result in an attempt to access memory outside the caller's address space. SOCEINTR Interrupted system call. SOCEINVAL iovcnt was not valid, or one of the fields in the iov array was not valid. SOCEWOULDBLOCK The s parameter is in nonblocking mode and no data is available to read, OR the SO_RCVTIMEO option has been set for socket s and the timeout expired before any data arrived to read. ═══ 7.11. recv() ═══ The socket call receives data on a connected socket. Syntax #include #include int recv(s, buf, len, flags) int s; char *buf; int len; int flags; Parameters s Socket descriptor buf Pointer to the buffer that receives the data len Length of the buffer in bytes pointed to by the buf parameter. flags Set by specifying one or more of the following flags. If you specify more than one flag, use the logical OR operator (|) to separate them. Setting this parameter is supported only for sockets in the Internet domain. MSG_OOB Reads any out-of-band data on the socket. MSG_PEEK Peeks at the data present on the socket; the data is returned but not consumed, so that a subsequent receive operation sees the same data. Description This call receives data on a socket with descriptor s and stores it in the buffer pointed to by buf. The recv() call applies only to connected sockets. For information on how to use recv() with datagram and raw sockets, see "Datagram or raw sockets" in connect(). The recv() call returns the length of the incoming data. If a datagram or sequenced packet is too long to fit in the buffer, the excess is discarded. No data is discarded for stream or sequenced packet sockets. If data is not available at the socket with descriptor s, the recv() call waits for a message to arrive and blocks the caller, unless the socket is in nonblocking mode. See ioctl() for a description of how to set nonblocking mode. Return Values If successful, the length in bytes of the data is returned. The value 0 indicates that the connection is closed. The value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using the buf and len parameters would result in an attempt to access memory outside the caller's address space. SOCEINTR Interrupted system call. SOCEINVAL Invalid argument. SOCEWOULDBLOCK The s parameter is in nonblocking mode and no data is available to receive, or the SO_RCVTIMEO option has been set for socket s and the timeout expired before any data arrived to receive. ═══ 7.12. recvfrom() ═══ The socket call receives data on a socket. Syntax #include #include int recvfrom(s, buf, len, flags, name, namelen) int s; char *buf; int len; int flags; struct sockaddr *name; int *namelen; Parameters s Socket descriptor buf Pointer to the buffer that receives the data len Length of the buffer in bytes pointed to by the buf parameter flags Set by specifying one or more of the following flags. If you specify more than one flag, use the logical OR operator (|) to separate them. Setting this parameter is supported only for sockets in the Internet domain. MSG_OOB Reads any out-of-band data on the socket. MSG_PEEK Peeks at the data present on the socket; the data is returned but not consumed, so that a subsequent receive operation sees the same data. name Pointer to a sockaddr structure (buffer) that data is received from. If name is a nonzero value, the source address is returned. namelen Pointer to the size in bytes of the buffer pointed to by name Description The recvfrom() call receives data on a socket with descriptor s and stores it in a buffer. The recvfrom() call applies to any socket type, whether connected or not. If name is nonzero, the address of the data sender is returned. The namelen parameter is first initialized to the size of the buffer associated with name; on return, it is modified to indicate the actual number of bytes stored there. The recvfrom() call returns the length of the incoming message or data. If a datagram or sequenced packet is too long to fit in the supplied buffer, the excess is discarded. No data is discarded for stream or sequenced packet sockets. If data is not available at the socket with descriptor s, the recvfrom() call waits for a message to arrive and blocks the caller, unless the socket is in nonblocking mode. See ioctl() for a description of how to set nonblocking mode. Return Values If successful, the length, in bytes, of the data is returned. The value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using the buf and len parameters would result in an attempt to access memory outside the caller's address space. SOCEWOULDBLOCK The s parameter is in nonblocking mode and no data is available to receive, or the SO_RCVTIMEO option has been set for socket s and the timeout expired before any data arrived to receive. SOCEINVAL Invalid argument. ═══ 7.13. recvmsg() ═══ The socket call receives data and control information on a specified socket. Syntax #include #include int recvmsg(s, msg, flags) int s; struct msghdr *msg; int flags; Parameters s Socket descriptor msg Pointer to a message header that receives the message flags Set by specifying one or more of the following flags. If you specify more than one flag, use the logical OR operator (|) to separate them. Setting this parameter is supported only for sockets in the Internet domain. MSG_OOB Reads any out-of-band data on the socket. MSG_PEEK Peeks at the data present on the socket; the data is returned but not consumed, so that a subsequent receive operation will see the same data Description This call receives a message on a socket with descriptor s. Network Services supports the following the following msghdr structure. Note: The fields msg_accrights and msg_accrightslen are ignored for the NetBIOS and Local IPC domains. For the internet domain, these fields are not supported, and you must set msg_accrights to NULL and msg_accrightslen to 0. struct msghdr { caddr_t msg_name; /* optional pointer to destination address buffer */ int msg_namelen; /* size of address buffer */ struct iovec *msg_iovec; /* scatter/gather array */ int msg_iovlen; /* how many elements in msg_iov */ caddr_t msg_accrights; /* access rights sent/recvd */ int msg_accrightslen; /* access rights information length */ }; The recvmsg() call applies to connection-oriented or connectionless sockets. This call returns the length of the data received. If a datagram or sequenced packet is too long to fit in the supplied buffer, the excess is discarded. No data is discarded for stream or sequenced packet sockets. If data is not available at the socket with descriptor s, the recvmsg() call waits for a message to arrive and blocks the caller, unless the socket is in nonblocking mode. See ioctl() for a description of how to set nonblocking mode. Return Values If successful, the length of the message in bytes is returned. The value 0 indicates the connection is closed; the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using msg would result in an attempt to access memory outside the caller's address space. SOCEINTR Interrupted system call. SOCENOTCONN The socket is not connected. SOCEWOULDBLOCK The s parameter is in nonblocking mode, and no data is available to receive. ═══ 7.14. select() ═══ The socket call gets read, write, and exception status on a group of sockets. Network Services supports two versions of the select() call:  TCP/IP Version 3.0 for OS/2 Warp Version  Berkeley Software Distributions (BSD) Version Either version of select() can be used. The TCP/IP Version 3.0 for OS/2 Warp version has better performance. However, the BSD version is more portable. ═══ 7.14.1. TCP/IP Version 3.0 for OS/2 Warp Version ═══ In the OS/2 Warp Connect version, the socket numbers are specified as an array of integers, in which the read socket numbers are followed by write socket numbers, followed by the exception pending connection socket numbers. This version monitors the activity on a socket by specifying the number of sockets to be checked for readability, readiness for writing, and exception pending conditions. Syntax #include #include int select(s, noreads, nowrites, noexcepts, timeout) int *s; int noreads; int nowrites; int noexcepts; long timeout; Parameters s Pointer to an array of socket numbers where the read socket numbers are followed by the write socket numbers, and then followed by the exception socket numbers. noreads Number of sockets to be checked for readability. nowrites Number of sockets to be checked for readiness for writing. noexcepts Number of sockets to be checked for exceptional pending conditions. For Network Services sockets, the only exceptional pending condition is out-of-band data in the receive buffer. timeout Maximum interval, in milliseconds, to wait for the selection to complete. Description This call monitors activity on a set of different sockets until a timeout expires, to see if any sockets are ready for reading or writing, or if any exceptional conditions are pending. If the timeout value is 0, select() does not wait before returning. If the timeout value is -1, select() does not timeout, but returns when a socket becomes ready. If the timeout value is a number of milliseconds, select() waits for the specified interval before returning. The select() call checks all indicated sockets at the same time and returns when any of them is ready. It is recommended to reinitialize the socket array every time select() is called. Return Values The number of ready sockets is returned. The value -1 indicates an error. The value 0 indicates an expired time limit. If the return value is greater than 0, the socket numbers in s that were not ready are set to -1. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT The address is not valid. SOCEINVAL Invalid argument. SOCEINTR Interrupted system call. Examples The following is an example of the OS/2 Warp Connect select() call. #define MAX_TIMEOUT 1000 /* input_ready(insock)- Check to see if there is available input on * socket insock. * Returns 1 if input is available. * 0 if input is not available. * -1 on error. */ int input_ready(insock) int insock; /* input socket descriptor */ { int socks[1]; /* array of sockets */ long timeout = MAX_TIMEOUT; /* put socket to check in socks[] */ socks[0] = insock; /* check for READ availability on this socket */ return select(socks, 1, 0, 0, timeout); } ═══ 7.14.2. BSD Version ═══ The BSD version monitors the activity on sockets by specifying a set mask (fd_set) of socket numbers for which the caller wants to read the data, write the data, and check exception pending conditions. The BSD version provides FD_SET, FD_CLR, FD_ISSET, and FD_ZERO macros to add or delete socket numbers from the set mask. Note: You must define #define BSD_SELECT before including the Network Services header files to access the BSD version of the select() call. Otherwise, the OS/2 Warp Connect version is assumed. Syntax #define BSD_SELECT #include #include #include int select(nfds, readfds, writefds, exceptfds, timeout) int nfds; fd_set *readfds; fd_set *writefds; fd_set *exceptfds; struct timeval *timeout; Parameters nfds The range of socket descriptors to be checked. select() tests socket descriptors in the range of 0 to (nfds - 1). readfds Pointer to a bit mask of descriptors to be checked for reading. writefds Pointer to a bit mask of descriptors to be checked for writing. exceptfds Pointer to a bit mask of descriptors to be checked for exceptional pending conditions. For Network Services sockets, the only exceptional pending condition is out-of-band data in the receive buffer. timeout Pointer to the time to wait for the select() call to complete. Description This call monitors activity on a set of different sockets until a timeout expires, to see if any sockets are ready for reading or writing, or if any exceptional conditions are pending. It is recommended to reinitialize readfds, writefds, and exceptfds every time select() is called. If timeout is a NULL pointer, the call blocks indefinitely until one of the requested conditions is satisfied. If timeout is non-NULL, it specifies the maximum time to wait for the call to complete. To poll a set of sockets, the timeout pointer should point to a zeroed timeval structure. The timeval structure is defined in the header file and contains the following fields: Field Description tv_sec Number of seconds tv_usec Number of microseconds A bit mask is made up of an array of integers. Macros are provided to manipulate the bit masks. Macro Description FD_SET(socket, bit_mask_address) Sets the bit for the socket in the bit mask pointed to by bit_mask_address. FD_CLR(socket, bit_mask_address) Clears the bit. FD_ISSET(socket, bit_mask_address) Returns true if the bit is set for this socket descriptor; otherwise, it returns false. FD_ZERO(socket, bit_mask_address) Clears the entire bit mask for all socket descriptors. Note: 1. For macros FD_SET, FD_CLR, FD_ISSET, and FD_ZERO, you define the parameters socket and bit_mask_address in the following manner: int socket; struct fd_set *bit_mask_address; 2. The first nfds descriptors in each bit mask are tested for the specified condition. 3. A socket descriptor with a value of 8 is actually the 9th descriptor in the fd_set (the socket descriptor value of 0 is the first descriptor). To check the socket descriptor 8, nfds would have to be greater than or equal to 9. Socket descriptors are specified by setting bits in a bit mask. Setting any of the descriptor pointers to zero indicates that no checks are to be made for the conditions. For example, setting exceptfds to be a NULL pointer causes the select call to check for only read and write conditions. Return Values The total number of ready sockets (in all bit masks) is returned. The value -1 indicates an error. The value 0 indicates an expired time limit. If the return value is greater than 0, the socket descriptors in each bit mask that are ready are set to 1. All others are set to 0. You can get the specific error code by calling sock_errno() or psock_errno(). sock_errno() Value Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT The address is not valid. SOCEINVAL Invalid argument. Examples The following is an example of the BSD version of the select() call. #define BSD_SELECT . . . fd_set readsocks; fd_set writesocks; fd_set exceptsocks; struct timeval timeout; int number_of_sockets; int number_found; . . . /* set bits in read/write/except bit masks. To set mask for a descriptor s use * readsocks |= fd_set(s); * * set number of sockets to be checked * number_of_sockets = x; */ . . . number_found = select(number_of_sockets, &readsocks, &writesocks, &exceptsocks, &timeout); ═══ 7.15. send() ═══ The socket call sends data on a connected socket. Syntax #include #include int send(s, msg, len, flags) int s; char *msg; int len; int flags; Parameters s Socket descriptor msg Pointer to a buffer containing the message to transmit len Length of the message pointed to by the msg parameter. flags Set by specifying one or more of the following flags. If you specify more than one flag, use the logical OR operator (|) to separate them. Setting this parameter is supported only for sockets in the Internet domain. MSG_OOB Sends out-of-band data on sockets that support SOCK_STREAM communication. MSG_DONTROUTE The SO_DONTROUTE option is turned on for the duration of the operation. This is usually used only by diagnostic or routing programs. Description This call sends data on the socket with descriptor s. The send() call applies to connected sockets. For information on how to use send() with datagram and raw sockets, see "Datagram or raw sockets" in connect(). If buffer space is not available at the socket to hold the message to be sent, the send() call normally blocks, unless the socket is placed in nonblocking mode. See ioctl() for a description of how to set nonblocking mode. Use the select() call to determine when it is possible to send more data. Return Values If successful, the number of bytes of the socket with descriptor s that is added to the send buffer is returned. This may be less than the number of bytes specified in the length parameter. Successful completion does not imply that the data has already been delivered to the receiver. The return value -1 indicates an error was detected on the sending side of the connection. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using the msg and len parameters would result in an attempt to access memory outside the caller's address space. SOCEINTR Interrupted system call. SOCEINVAL Invalid argument. SOCENOBUFS No buffer space is available to send the message. SOCEWOULDBLOCK The s parameter is in nonblocking mode and the data cannot be sent without blocking, or the SO_SNDTIMEO option has been set for socket s and the timeout expired before any data was sent. ═══ 7.16. sendmsg() ═══ The socket call sends data and control information on a specified socket. Syntax #include #include int sendmsg(s, msg, flags) int s; struct msghdr *msg; int flags; Parameters s Socket descriptor msg Pointer to a message header containing a message to be sent flags Set by specifying one or more of the following flags. If you specify more than one flag, use the logical OR operator (|) to separate them. Setting this parameter is supported only for sockets in the Internet domain. MSG_OOB Sends out-of-band data on the socket. MSG_DONTROUTE The SO_DONTROUTE option is turned on for the duration of the operation. This is usually used only by diagnostic or routing programs. Description This call sends a msghdr structure on a socket with descriptor s. Network Services supports the following the following msghdr structure. Note: The fields msg_accrights and msg_accrightslen are ignored for the NetBIOS and Local IPC domains. For the internet domain, these fields are not supported, and you must set msg_accrights to NULL and msg_accrightslen to 0. struct msghdr { caddr_t msg_name; /* optional pointer to destination address buffer */ int msg_namelen; /* size of address buffer */ struct iovec *msg_iovec; /* scatter/gather array */ int msg_iovlen; /* how many elements in msg_iov */ caddr_t msg_accrights; /* access rights sent/recvd */ int msg_accrightslen; /* access rights information length */ }; The sendmsg() call applies to connection-oriented and connectionless sockets. This call returns the length of the data sent. If the socket with descriptor s is not ready for sending data, the sendmsg() call waits unless the socket is in nonblocking mode. See ioctl() for a description of how to set nonblocking mode. Return Values If successful, the number of bytes sent is returned. Successful completion does not guarantee delivery of the data to the receiver. The return value -1 indicates an error was detected on the sending side of the connection. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using msg would result in an attempt to access memory outside the caller's address space. SOCEINTR Interrupted system call. SOCEINVAL msg_namelen is not the size of a valid address for the specified address family. SOCEMSGSIZE The message was too big to be sent as a single datagram. SOCENOBUFS No buffer space is available to send the message. SOCENOTCONN The socket is not connected. SOCEWOULDBLOCK The s parameter is in nonblocking mode and the data cannot be sent without blocking. ═══ 7.17. sendto() ═══ The socket call sends data on a socket. Syntax #include #include int sendto(s, msg, len, flags, to, tolen) int s; char *msg; int len; int flags; struct sockaddr *to; int tolen; Parameters s Socket descriptor msg Pointer to the buffer containing the message to transmit len Length of the message in the buffer pointed to by the msg parameter flags Set to 0 or one or more of the following flags. If you specify more than one flag, use the logical OR operator (|) to separate them. Setting this parameter is supported only for sockets in the Internet domain. MSG_OOB Sends out-of-band data on the socket. MSG_DONTROUTE The SO_DONTROUTE option is turned on for the duration of the operation. This is usually used only by diagnostic or routing programs. to Pointer to a sockaddr structure (buffer) containing the destination address tolen Size in bytes of the buffer pointed to by the to parameter Description This call sends data on the socket with descriptor s. The sendto() call applies to connected or unconnected sockets. For unconnected datagram and raw sockets, the sendto() call sends data to the specified destination address. For stream and sequenced packet sockets the destination address is ignored. Datagram sockets are connected by calling connect(). This identifies the peer to send/receive the datagram. Once a datagram socket is connected to a peer, you may still use the sendto() call but a destination address cannot be included. To change the peer address when using connected datagram sockets, issue a connect() call with a null address. Specifying a null address on a connected datagram socket removes the peer address specification. You can then either issue a sendto() call specifying a different destination address or issue a connect() call to connect to a different peer. For more information on connecting datagram sockets and specifying null addresses, see "Datagram or raw sockets" in connect(). Return Values If successful, the number of bytes sent is returned. Successful completion does not guarantee delivery of the data to the receiver. The return value -1 indicates an error was detected on the sending side. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using the msg and len parameters would result in an attempt to access memory outside the caller's address space. SOCEINVAL The tolen parameter is not the size of a valid address for the specified address family. SOCEMSGSIZE The message was too big to be sent as a single datagram. SOCENOBUFS No buffer space is available to send the message. SOCEWOULDBLOCK The s parameter is in nonblocking mode and the data cannot be sent without blocking, or the SO_SNDTIMEO option has been set for socket s and the timeout expired before any data was sent. SOCENOTCONN The socket is not connected. SOCEDESTADDRREQ Destination address required. ═══ 7.18. setsockopt() ═══ The socket call sets options associated with a socket. Syntax #include #include int setsockopt(s, level, optname, optval, optlen) int s; int level; int optname; char *optval; int optlen; Parameters s Socket descriptor level Specifies which option level is being set optname Name of a specified socket option optval Pointer to the option data optlen Length of the option data Description This call sets options associated with a socket such as enabling debugging at the socket or protocol level, control timeouts, or permit socket data broadcast. Options can exist at the socket or the protocol level; options are always present at the highest socket level. When setting socket options, the level of the option and the name of the option must be specified. The following table lists the supported levels: ┌─────────────────────────────────────────────────┐ │ Table 13. Supported Levels │ ├──────────────────────┬──────────────────────────┤ │ SUPPORTED LEVEL │ #DEFINE IN │ ├──────────────────────┼──────────────────────────┤ │ SOL_SOCKET │ │ ├──────────────────────┼──────────────────────────┤ │ IPPROTO_IP │ │ ├──────────────────────┼──────────────────────────┤ │ IPPROTO_TCP │ │ ├──────────────────────┼──────────────────────────┤ │ NBPROTO_NB │ │ └──────────────────────┴──────────────────────────┘ The optval and optlen parameters are used to pass data used by the particular set command. The optval parameter points to a buffer containing the data needed by the set command. The optval parameter is optional and if data is not needed by the command, can be set to the NULL pointer. The optlen parameter must be set to the size of the data or data type pointed to by optval. For socket options that are toggles, the option is enabled if optval is nonzero and disabled if optval is 0. The following tables list the supported options for setsockopt() at each level (SOL_SOCKET, IPPROTO_IP, IPPROTO_TCP). Detailed descriptions of the options follow each table. ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 14. Supported setsockopt() Socket Options for SOL_SOCKET │ ├──────────────────┬───────────────────────────┬──────────────────────────────┬────────────────────┬───────────────────┤ │ │ │ │ │ BOOLEAN/ │ │ OPTION NAME │ DESCRIPTION │ DOMAINS(*) │ DATA TYPE │ VALUE │ ├──────────────────┼───────────────────────────┼─────────┬─────────┬──────────┼────────────────────┼───────────────────┤ │ SO_BROADCAST │ allows sending of broad- │ I │ │ N │ int │ boolean │ │ │ cast messages │ │ │ │ │ │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_DEBUG │ turn on recording of │ I │ L │ │ int │ boolean │ │ │ debugging information │ │ │ │ │ │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_DONTROUTE │ bypass routing tables │ I │ │ │ int │ boolean │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_KEEPALIVE │ keep connections alive │ I │ │ │ int │ boolean │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_LINGER │ linger on close if data │ I │ │ │ struct linger │ value │ │ │ present │ │ │ │ │ │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_OOBINLINE │ leave received OOB data │ I │ │ │ int │ boolean │ │ │ in-line │ │ │ │ │ │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_RCVBUF │ receive buffer size │ I │ L │ N │ long │ value │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_RCVLOWAT │ receive low-water mark │ I │ L │ │ int │ value │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_REUSEADDR │ allow local address reuse │ I │ │ N │ int │ boolean │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_SNDBUF │ send buffer size │ I │ L │ N │ long │ value │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_SNDLOWAT │ send low-water mark │ I │ L │ │ int │ value │ ├──────────────────┼───────────────────────────┼─────────┼─────────┼──────────┼────────────────────┼───────────────────┤ │ SO_USELOOPBACK │ bypass hardware when pos- │ I │ │ │ int │ value │ │ │ sible │ │ │ │ │ │ ├──────────────────┴───────────────────────────┴─────────┴─────────┴──────────┴────────────────────┴───────────────────┤ │ NOTE: (*) This column specifies I for Internet, L for Local IPC, and N for NetBIOS communication domains. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ The following options are recognized for SOL_SOCKET: Option Description SO_BROADCAST (datagram sockets only) Sets the ability to broadcast messages. If this option is enabled, it allows the application to send broadcast messages over s, if the interface specified in the destination supports broadcasting of packets. SO_DEBUG Sets the ability to record debug information for a socket. SO_DONTROUTE Sets the ability for the socket to bypass the routing of outgoing messages. When this option is enabled, it causes outgoing messages to bypass the standard routing algorithm and be directed to the appropriate network interface according to the network portion of the destination address. When enabled, packets can be sent only to directly connected networks (networks for which this host has an interface). SO_KEEPALIVE (stream sockets only) Sets the ability of the socket to send keepalive packets that will keep the connection alive. TCP uses a timer called the keepalive timer. This timer is used to monitor idle connections that might have been disconnected because of a peer crash or timeout. If this option is set on, a keepalive packet is periodically sent to the peer. This is mainly used to allow servers to close connections that are no longer active as a result of clients going away without properly closing connections. SO_LINGER (stream sockets only) Sets the ability of the socket to linger on close if data is present. When this option is enabled and there is unsent data present when soclose() is called, the calling application is blocked during the soclose() call until the data is transmitted or the connection has timed out. If this option is disabled, the soclose() call returns without blocking the caller, and TCP waits to try to send the data. Although the data transfer is usually successful, it cannot be guaranteed, because TCP waits only a finite amount of time trying to send the data. SO_OOBINLINE (stream sockets only) Sets the ability of the socket to receive out-of-band data. As stated in TCP/IP Illustrated, Volume 1: The Protocols out-of-band data is "a logically separate data path using the same connection as the normal data path." When this option is enabled, it causes out-of-band data to be placed in the normal data input queue as it is received, making it available to recv(), and recvfrom(), without having to specify the MSG_OOB flag in those calls. When this option is disabled, it causes out-of-band data to be placed in the priority data input queue as it is received, making it available to recv(), and recvfrom(), only by specifying the MSG_OOB flag in those calls. SO_RCVBUF Sets buffer size for input. This option sets the size of the receive buffer to the value contained in the buffer pointed to by optval. This allows the buffer size to be tailored for specific application needs, such as increasing the buffer size for high-volume connections. SO_RCVLOWAT Sets receive low-water mark. SO_REUSEADDR (stream and datagram sockets only) Sets the ability of a socket to reuse a local address. When enabled, this option allows local addresses that are already in use to be bound. This alters the normal algorithm used in the bind() call. The system checks at connect time to be sure that no local address and port have the same foreign address and port. The error SOCEADDRINUSE is returned if the association already exists. SO_SNDBUF Sets buffer size for output. This option sets the size of the send buffer to the value contained in the buffer pointed to by optval. This allows the send buffer size to be tailored for specific application needs, such as increasing the buffer size for high-volume connections. SO_SNDLOWAT Sets send low-water mark. SO_USELOOPBACK Bypasses hardware when possible. struct linger: For the SO_LINGER option, optval points to a linger structure. This structure is defined in and contains the following fields: Field Description l_onoff Option on/off l_linger Linger time The l_onoff field is set to zero if the SO_LINGER option is being disabled. A nonzero value enables the option. The l_linger field specifies the amount of time in seconds to linger on close. A value of zero will cause so_close() to wait until the disconnect completes. ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 15. Supported setsockopt() Socket Options for IPPROTO_TCP │ ├──────────────────────┬────────────────────────────────┬─────────────────────────────────────┬────────────────────────┤ │ │ │ │ BOOLEAN/ │ │ OPTION NAME │ DESCRIPTION │ DATA TYPE │ VALUE │ ├──────────────────────┼────────────────────────────────┼─────────────────────────────────────┼────────────────────────┤ │ TCP_NODELAY │ don't delay send to coalesce │ int │ boolean │ │ │ packets │ │ │ └──────────────────────┴────────────────────────────────┴─────────────────────────────────────┴────────────────────────┘ The following options are recognized for IPPROTO_TCP: Option Description TCP_NODELAY (stream sockets only) Setting on disables the buffering algorithm so that the client's TCP sends small packets as soon as possible. This often has no performance effects on LANs, but can degrade performance on Wide Area Networks (WAN). ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 16. Supported setsockopt() Socket Options for NBPROTO_NB │ ├──────────────────────┬────────────────────────────────┬─────────────────────────────────────┬────────────────────────┤ │ │ │ │ BOOLEAN/ │ │ OPTION NAME │ DESCRIPTION │ DATA TYPE │ VALUE │ ├──────────────────────┼────────────────────────────────┼─────────────────────────────────────┼────────────────────────┤ │ NB_DGRAM_TYPE │ type of datagrams to receive │ int │ value │ └──────────────────────┴────────────────────────────────┴─────────────────────────────────────┴────────────────────────┘ The following option is recognized for NBPROTO_NB: Option Description NB_DGRAM_TYPE (datagram sockets only) Sets type of datagrams to be received on the socket. The possible values are: NB_DGRAM The socket is to receive normal (unicast) datagrams only. NB_BROADCAST The socket is to receive broadcast datagrams only. NB_DGRAM_ANY The socket can receive both normal or broadcast datagrams. This option can be changed at any time. Return Values The value 0 indicates success; the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). sock_errno() Value Description SOCEADDRINUSE The address is already in use. SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEFAULT Using optval and optlen parameters would result in an attempt to access memory outside the caller's address space. SOCENOPROTOOPT The optname parameter is unrecognized. SOCEINVAL Invalid argument. SOCENOBUFS No buffer space is available. Examples The following are examples of the setsockopt() call. See getsockopt() for examples of how the options are queried. int rc; int s; int optval; struct linger lstruct; /* extracted from sys/socket.h */ int setsockopt(int s, int level, int optname, char *optval, int optlen); . . . /* I want out of band data in the normal input queue */ optval = 1; rc = setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *) &optval, sizeof(int)); . . . /* I want to linger on close */ lstruct.l_onoff = 1; lstruct.l_linger = 100; rc = setsockopt(s, SOL_SOCKET, SO_LINGER, (char *) &lstruct, sizeof(lstruct)); ═══ 7.19. shutdown() ═══ The socket call shuts down all or part of a full duplex connection. Syntax int shutdown(s, howto) int s; int howto; Parameters s Socket descriptor howto Condition of the shutdown Description This call shuts down all or part of a full duplex connection. Since data flows in one direction are independent of data flowing from the other direction, the shutdown call allows you to independently stop data flow in either direction or all data flows with one API call. For example, you may want to stop the sender(s) from sending data to you, but you still want to send data. Using the shutdown() call is optional. The howto parameter sets the condition for shutting down the connection to socket s. It can be set to one of the following:  0 - no more data can be received on socket s.  1 - no more data can be sent on socket s.  2 - no more data can be sent or received on socket s. Note: In the NetBIOS domain, the shutdown() call is valid, but the function of shutdown() is not implemented. When called, shutdown() will return a successful return code, but no shutdown occurs. Return Values The value 0 indicates success; the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEINVAL The howto parameter was not set to one of the valid values. ═══ 7.20. so_cancel() ═══ The socket call cancels a pending blocking sockets API call on a socket. Syntax #include #include int so_cancel (s) int s; Parameters s Socket descriptor Description This call sends an interrupt signal to a socket with the socket descriptor s. If there is any thread blocking on a sockets API (for example, select()) on the same descriptors, that sockets API returns an SOCEINTR (interrupted system call) socket error code and a return value of -1. The so_cancel call is used in multithreaded applications where one thread needs to 'wake up' another thread which is blocked in a sockets API call. Return Values The value 0 indicates success; the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. ═══ 7.21. sock_errno() ═══ The socket call returns error code set by a socket call. Syntax #include int sock_errno() Description The sock_errno() call returns the last error code set by a socket call. Return Values The value 0 indicates success; the value 1 indicates an error. ═══ 7.22. socket() ═══ The socket call creates an endpoint for communication and returns a socket descriptor representing the endpoint. Syntax #include #include int socket(domain, type, protocol) int domain; int type; int protocol; Parameters domain Communication domain requested type Type of socket created protocol Protocol requested Description This call creates an endpoint for communication and returns a socket descriptor representing the endpoint. Each socket type provides a different communication service. Sockets are deallocated with the soclose() call. The domain parameter specifies a communications domain where communication is to take place. This parameter specifies the protocol family which is used. Protocol Family Description PF_OS2 or PF_UNIX Use addresses in the Local IPC format which take the form of OS/2 Warp Connect file and path names. PF_INET Use addresses in the Internet address format. PF_NETBIOS or PF_NB Use addresses in the NetBIOS address format. The type parameter specifies the type of socket created. The type is analogous with the semantics of the communication requested. These socket type constants are defined in the header file. Refer to Socket Types for additional details. The types supported are: Type Description SOCK_STREAM Provides sequenced, two-way byte streams that are reliable and connection-oriented. It supports a mechanism for out-of-band data. Stream sockets are supported by the Internet (PF_INET) communication domain and Local IPC (PF_OS2, PF_UNIX, or PF_LOCAL). SOCK_DGRAM Provides datagrams, which are connectionless messages of a fixed length whose reliability is not guaranteed. Datagrams can be received out of order, lost, or delivered multiple times. Datagram sockets are supported by the Internet (PF_INET), Local IPC (PF_OS2, PF_UNIX, or PF_LOCAL), and NetBIOS (PF_NETBIOS or PF_NB) communication domains. SOCK_RAW Provides the interface to internal protocols (such as IP and ICMP). Raw sockets are supported by the Internet (PF_INET) communication domain. SOCK_SEQPACKET Provides sequenced byte streams that are reliable and connection-oriented. Data is sent without error or duplication and is received in the same order as it was sent. Sequenced packet sockets are supported by the NetBIOS (PF_NETBIOS or PF_NB) communication domain. The protocol parameter specifies a particular protocol to be used with the socket. If the protocol field is set to 0 (default), the system selects the default protocol number for the domain and socket type requested. Default and valid protocol number-protocol family combinations are in the Socket Protocol Families and Supported Communication Domains section. The getprotobyname() call can be used to get the protocol number for a protocol with a well-known name. Return Values A non-negative socket descriptor return value indicates success. The return value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). sock_errno() Value Description SOCEMFILE The maximum number of sockets are currently in use. SOCEPROTONOSUPPORT The protocol is not supported in the specified domain or the protocol is not supported for the specified socket type. SOCEPROTOTYPE The protocol is the wrong type for the socket. Examples The following are examples of the socket() call. int s; struct protoent *p; struct protoent *getprotobyname(char *name); int socket(int domain, int type, int protocol); /* extracted from sys/socket.h */ . . . /* Get stream socket in Internet domain with default protocol */ s = socket(PF_INET, SOCK_STREAM, 0); . . . /* Get raw socket in Internet domain for ICMP protocol */ p = getprotobyname("icmp"); s = socket(PF_INET, SOCK_RAW, p->p_proto); ═══ 7.23. soclose() ═══ The socket call shuts down a socket and frees resources allocated to the socket. Syntax #include #include int soclose(s) int s; Parameters s Socket descriptor Description This call shuts down the socket associated with the socket descriptor s, and frees resources allocated to the socket. If s refers to a connected socket, the connection is closed. If the SO_LINGER socket option is enabled (see setsockopt() for additional information), then the task will try to send any queued data. If the SO_LINGER socket option is disabled, then the task will flush any data queued to be sent. Return Values The value 0 indicates success; the value -1 indicates an error. You can get the specific error code by calling sock_errno() or psock_errno(). Error Code Description SOCENOTSOCK The s parameter is not a valid socket descriptor. SOCEALREADY The socket s is marked nonblocking, and a previous connection attempt has not completed. ═══ 7.24. writev() ═══ The socket call writes data from a set of specified buffers on a socket. Syntax #include #include int writev(s, iov, iovcnt) int s; struc iovec *iov; int iovcnt; Parameters s Socket descriptor iov Pointer to an array of iovec structures iovcnt Number of iovec structures pointed to by the iov parameter. The maximum number of iovec structures is 32. Description This call writes data on a socket with descriptor s. The data is gathered from the buffers specified by iov[0]...iov[iovcnt-1]. The iovec structure is defined in and contains the following fields: Field Description iov_base Pointer to the buffer iov_len Length of the buffer This call writes iov_len bytes of data. If there is not enough available buffer space to hold the socket data to be transmitted and the socket is in blocking mode, writev() blocks the caller until additional buffer space becomes available. If the socket is in a nonblocking mode, writev() returns -1 and sets return code to SOCEWOULDBLOCK. See ioctl() for a description of how to set nonblocking mode. For datagram sockets, this call sends the entire datagram, provided the datagram fits into the protocol buffers. Stream sockets act like streams of information with no boundaries separating data. For example, if an application sends 1000 bytes, each call to this function can send 1 byte, 10 bytes, or the entire 1000 bytes. For a stream socket, an application can place this call in a loop, calling this function until all data has been sent. Return Values If successful, the number of bytes written is returned. Successful completion does not guarantee the data is written. The return value -1 indicates an error was detected on the sending side of the connection. You can get the specific error code by calling sock_errno() or psock_errno(). sock_errno() Value Description SOCENOTSOCK s is not a valid socket descriptor. SOCEFAULT Using the iov and iovcnt parameters would result in an attempt to access memory outside the caller's address space. SOCEINTR Interrupted system call. SOCEINVAL Invalid argument. SOCENOBUFS Buffer space is not available to send the message. SOCEWOULDBLOCK The s parameter is in nonblocking mode and the data cannot be written without blocking, or the SO_SNDTIMEO option has been set for socket s and the timeout expired before any data was sent. SOCEMSGSIZE The message was too big to be sent as a single datagram. SOCEDESTADDRREQ A destination address is required. ═══ 8. TCP/IP Network Utility Routines ═══ The following table briefly describes each sockets utility function call supported by Network Services and identifies where you can find the syntax, parameters, and other appropriate information. The following network utility calls described in this section can be used to access services only for the Internet communication domain: ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 17. TCP/IP Network Utility Routines Quick Reference │ ├────────────────────┬────────────────────────────────────────────────┬────────────────────────────────────────────────┤ │ SOCKET CALL │ DESCRIPTION │ LOCATION │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ bswap() │ Swaps bytes in a short integer │ bswap() │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ dn_comp() │ Compresses the expanded domain name │ dn_comp() │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ dn_expand() │ Expands a compressed domain name to a full │ dn_expand() │ │ │ domain name │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ endhostent() │ Closes the HOSTS file │ endhostent() │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ endnetent() │ Closes the NETWORKS file │ endnetent() │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ endprotent() │ Closes the PROTOCOL file │ endprotoent() │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ endservent() │ Closes the SERVICES file │ endservent() │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ gethostbyaddr() │ Returns a pointer to information about a host │ gethostbyaddr() │ │ │ specified by an Internet address │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ gethostbyname() │ Returns a pointer to information about a host │ gethostbyname() │ │ │ specified by a host name │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ gethostent() │ Returns a pointer to the next entry in the │ gethostent() │ │ │ HOSTS file │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ gethostid() │ Returns the unique identifier of the current │ gethostid() │ │ │ host │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ gethostname() │ Gets the standard host name for the local host │ gethostname() │ │ │ machine │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ getnetbyaddr() │ Returns a pointer to the NETWORKS file entry │ getnetbyaddr() │ │ │ that contains the specified network address │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ getnetbyname() │ Returns a pointer to the NETWORKS file entry │ getnetbyname() │ │ │ that contains the specified network name │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ getnetent() │ Returns a pointer to the next entry in the │ getnetent() │ │ │ NETWORKS file │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ getprotobyname() │ Returns a pointer to the PROTOCOL file entry │ getprotobyname() │ │ │ specified by a protocol name │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ getprotobynumber() │ Returns a pointer to the PROTOCOL file entry │ getprotobynumber() │ │ │ specified by a protocol number │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ getprotoent() │ Returns a pointer to the next entry in the │ getprotoent() │ │ │ PROTOCOL file │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ getservbyname() │ Returns a pointer to the SERVICES file entry │ getservbyname() │ │ │ specified by a service name │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ getservbyport() │ Returns a pointer to the SERVICES file entry │ getservbyport() │ │ │ specified by a port number │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ getservent() │ Returns a pointer to the next entry in the │ getservent() │ │ │ SERVICES file │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ htonl() │ Translates byte order from host to network for │ htonl() │ │ │ a long integer │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ htons() │ Translates byte order from host to network for │ htons() │ │ │ a short integer │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ inet_addr() │ Constructs an Internet address from character │ inet_addr() │ │ │ strings representing numbers expressed in │ │ │ │ standard dotted-decimal notation │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ inet_lnaof() │ Returns the local network portion of an │ inet_lnaof() │ │ │ Internet address │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ inet_makeaddr() │ Constructs an Internet address from a network │ inet_makeaddr() │ │ │ number and a local address │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ inet_netof() │ Returns the network portion of the Internet │ inet_netof() │ │ │ address in network-byte order │ │ └────────────────────┴────────────────────────────────────────────────┴────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 17. TCP/IP Network Utility Routines Quick Reference │ ├────────────────────┬────────────────────────────────────────────────┬────────────────────────────────────────────────┤ │ SOCKET CALL │ DESCRIPTION │ LOCATION │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ inet_network() │ Constructs a network number from character │ inet_network() │ │ │ strings representing numbers expressed in │ │ │ │ standard dotted-decimal notation │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ inet_ntoa() │ Returns a pointer to a string in dotted- │ inet_ntoa() │ │ │ decimal notation │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ lswap() │ Swaps bytes in a long integer │ lswap() │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ ntohl() │ Translates byte order from network to host for │ ntohl() │ │ │ a long integer │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ ntohs() │ Translates byte order from network to host for │ ntohs() │ │ │ a short integer │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ res_init() │ Reads the RESOLV file for the default domain │ res_init() │ │ │ name │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ res_mkquery() │ Makes query message for the name servers in │ res_mkquery() │ │ │ the Internet domain │ │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ res_send() │ Sends query to a local name server │ res_send() │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ sethostent() │ Opens and rewinds the HOSTS file │ sethostent() │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ setnetent() │ Opens and rewinds the NETWORKS file │ setnetent() │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ setprotoent() │ Opens and rewinds the PROTOCOL file │ setprotoent() │ ├────────────────────┼────────────────────────────────────────────────┼────────────────────────────────────────────────┤ │ setservent() │ Opens and rewinds the SERVICES file │ setservent() │ └────────────────────┴────────────────────────────────────────────────┴────────────────────────────────────────────────┘ ═══ 8.1. bswap() ═══ The socket call swaps bytes in a short integer. Syntax #include #include u_short bswap(a) u_short a; Parameters a Unsigned short integer whose bytes are to be swapped Description This call swaps bytes in a short integer. Return Values Returns the translated short integer. ═══ 8.2. dn_comp() ═══ The socket call compresses the expanded domain name. Syntax #include #include #include #include int dn_comp(exp_dn, comp_dn, length, dnptrs, lastdnptr) u_char *exp_dn; u_char *comp_dn; int length; u_char **dnptrs; u_char **lastdnptr; Parameters exp_dn Pointer to the location of an expanded domain name comp_dn Pointer to an array containing the compressed domain name length Length of the array in bytes pointed to by the comp_dn parameter dnptrs List of pointers to previously compressed names in the current message lastdnptr Pointer to the end of the array pointed to by dnptrs Description This call compresses the domain name pointed to by the exp_dn parameter and stores it in the area pointed to by the comp_dn parameter. It uses the global _res structure, which is defined in the header file. Return Values When successful, the dn_comp() call returns the size of the compressed domain name. If it fails, the call returns a value of -1. ═══ 8.3. dn_expand() ═══ The socket call expands a compressed domain name to a full domain name. Syntax #include #include #include #include int dn_expand(msg, eomorig, comp_dn, exp_dn, length) u_char *msg; u_char *eomorig; u_char *comp_dn; u_char *exp_dn; int length; Parameters msg Pointer to the beginning of a message eomorig Pointer to the end of the original message that contains the compressed domain name comp_dn Pointer to the compressed domain name exp_dn Pointer to a buffer that holds the resulting expanded domain name length Length of the buffer in bytes pointed to by the exp_dn parameter Description This call expands a compressed domain name to a full domain name, converting the expanded name to all uppercase letters. It uses the global _res structure, which is defined in the header file. Return Values If it succeeds, the dn_expand() call returns the size of the expanded domain name. If it fails, the call returns a value of -1. ═══ 8.4. endhostent() ═══ The socket call closes the HOSTS file. Syntax void endhostent() Description This call closes the ETC\HOSTS file, which contains information about known hosts. ═══ 8.5. endnetent() ═══ The socket call closes the NETWORKS file. Syntax void endnetent() Description The endnetent() call closes the ETC\NETWORKS file, which contains information about known networks. ═══ 8.6. endprotoent() ═══ The socket call closes the PROTOCOL file. Syntax void endprotoent() Description This call closes the ETC\PROTOCOL file, which contains information about known protocols. ═══ 8.7. endservent() ═══ The socket call closes the SERVICES file. Syntax void endservent() Description This call closes the ETC\SERVICES file, which contains information about known services. ═══ 8.8. gethostbyaddr() ═══ The socket call returns a pointer to information about a host specified by an Internet address. Syntax #include struct hostent *gethostbyaddr(addr, addrlen, addrfam) char *addr; int addrlen; int addrfam; Parameters addr Pointer to a 32-bit Internet address in network-byte order addrlen Size of addr in bytes addrfam Address family supported (AF_INET) Description This call resolves the host name through a name server, if one is present. If a name server is not present or cannot resolve the host name, gethostbyaddr() searches the ETC\HOSTS file in sequence until a matching host address is found or an end-of-file (EOF) marker is reached. This search order can be reversed by the following statement in your config.sys file: SET USE_HOSTS_FIRST=1 Return Values The return value points to static data that subsequent API calls can modify. This call returns a pointer to a hostent structure for the host address specified on the call and indicates success. A NULL pointer indicates an error or EOF. The header file defines the hostent structure and contains the following elements: Element Description h_name Official name of the host h_aliases Zero-terminated array of alternative names for the host h_addrtype The address family of the network address being returned, always set to AF_INET h_length Length of the address in bytes h_addr Pointer to the network address of the host The value of h_errno indicates the specific error. ┌─────────────────┬───────┬───────────────────────────────────────────────┐ │ H_ERRNO VALUE │ CODE │ DESCRIPTION │ ├─────────────────┼───────┼───────────────────────────────────────────────┤ │ HOST_NOT_FOUND │ 1 │ The host specified by the addr parameter is │ │ │ │ not found. │ ├─────────────────┼───────┼───────────────────────────────────────────────┤ │ TRY_AGAIN │ 2 │ The local server does not receive a response │ │ │ │ from an authorized server. Try again later. │ ├─────────────────┼───────┼───────────────────────────────────────────────┤ │ NO_RECOVERY │ 3 │ This error code indicates an unrecoverable │ │ │ │ error. │ ├─────────────────┼───────┼───────────────────────────────────────────────┤ │ NO_DATA │ 4 │ The requested addr is valid, but does not │ │ │ │ have an Internet address at the name server. │ ├─────────────────┼───────┼───────────────────────────────────────────────┤ │ NO_ADDRESS │ 4 │ The requested addr is valid, but does not │ │ │ │ have an Internet address at the name server. │ └─────────────────┴───────┴───────────────────────────────────────────────┘ ═══ 8.9. gethostbyname() ═══ The socket call returns a pointer to information about a host specified by a host name. Syntax #include struct hostent *gethostbyname(name) char *name; Parameters name Pointer to the name of the host being queried Description: This call resolves the host name through a name server, if one is present. If a name server is not present or is unable to resolve the host name, gethostbyname() searches the ETC\HOSTS file in sequence until a matching host name is found or an EOF marker is reached. This search order can be reversed by the following statement in your config.sys file: SET USE_HOSTS_FIRST=1 Return Values The return value points to static data that subsequent API calls can modify. This call returns a pointer to a hostent structure for the host address specified on the call and indicates success. A NULL pointer indicates an error or EOF. The header file defines the hostent structure and contains the following elements: Element Description h_name Official name of the host h_aliases Zero-terminated array of alternative names for the host h_addrtype The address family of the network address being returned, always set to AF_INET h_length Length of the address in bytes h_addr Pointer to the network address of the host The value of h_errno indicates the specific error. ┌─────────────────┬───────┬───────────────────────────────────────────────┐ │ H_ERRNO VALUE │ CODE │ DESCRIPTION │ ├─────────────────┼───────┼───────────────────────────────────────────────┤ │ HOST_NOT_FOUND │ 1 │ The host specified by the name parameter is │ │ │ │ not found. │ ├─────────────────┼───────┼───────────────────────────────────────────────┤ │ TRY_AGAIN │ 2 │ The local server does not receive a response │ │ │ │ from an authorized server. Try again later. │ ├─────────────────┼───────┼───────────────────────────────────────────────┤ │ NO_RECOVERY │ 3 │ This error code indicates an unrecoverable │ │ │ │ error. │ ├─────────────────┼───────┼───────────────────────────────────────────────┤ │ NO_DATA │ 4 │ The requested name is valid, but does not │ │ │ │ have an Internet address at the name server. │ ├─────────────────┼───────┼───────────────────────────────────────────────┤ │ NO_ADDRESS │ 4 │ The requested name is valid, but does not │ │ │ │ have an Internet address at the name server. │ └─────────────────┴───────┴───────────────────────────────────────────────┘ ═══ 8.10. gethostent() ═══ The socket call returns a pointer to the next entry in the HOSTS file. Syntax #include #include struct hostent *gethostent() Description This call returns a pointer to the next entry in the HOSTS file. Return Values The return value points to static data that subsequent API calls can modify. This call returns a pointer to a hostent structure for the host address specified on the call and indicates success. A NULL pointer indicates an error or EOF. The header file defines the hostent structure and contains the following elements: Element Description h_name Official name of the host h_aliases Zero-terminated array of alternative names for the host h_addrtype The address family of the network address being returned, always set to AF_INET h_length Length of the address in bytes h_addr Pointer to the network address of the host ═══ 8.11. gethostid() ═══ The socket call returns the unique identifier of the current host. Syntax #include u_long gethostid() Description This call gets the unique 32-bit identifier for the current host. Return Values The gethostid() call returns the 32-bit identifier, in host-byte order of the current host, which should be unique across all hosts. This identifier is usually the IP address of the primary interface. The default primary interface is lan0. For a slip only or PPP only configuration, the sl0 or ppp0 is the primary interface. If no primary interface exists, the call returns a hexadecimal of X'FFFFFFFF'. ═══ 8.12. gethostname() ═══ The socket call gets the standard host name for the local host machine. Syntax #include int gethostname(name, namelen) char *name; int namelen; Parameters name Pointer to a buffer namelen Length of the buffer Description This call copies the standard host name for the local host into the buffer specified by the name parameter. The returned name is a null-terminated string. Return Values The value 0 indicates success; the value -1 indicates an error. ═══ 8.13. getnetbyaddr() ═══ The socket call returns a pointer to the NETWORKS file entry that contains the specified network address. Syntax #include struct netent *getnetbyaddr(net, type) u_long net; int type; Parameters net Network address type Address family supported (AF_INET) Description The getnetbyaddr() call searches the ETC\NETWORKS file for the specified network address. Return Values The return value points to static data that subsequent API calls can modify. A pointer to a netent structure indicates success. A NULL pointer indicates an error or EOF. The netent structure is defined in the header file and contains the following elements: Element Description n_name Official name of the network n_aliases An array, terminated with a NULL pointer, of alternative names for the network n_addrtype The address family of the network address being returned, always set to AF_INET n_net Network number, returned in host-byte order ═══ 8.14. getnetbyname() ═══ The socket call returns a pointer to the NETWORKS file entry that contains the specified network name. Syntax #include struct netent *getnetbyname(name) char *name; Parameters name Pointer to a network name Description This call searches the ETC\NETWORKS file for the specified network name. Return Values The getnetbyname() call returns a pointer to a netent structure for the network name specified on the call. The return value points to static data that subsequent API calls can modify. A pointer to a netent structure indicates success. A NULL pointer indicates an error or EOF. The netent structure is defined in the header file and contains the following elements: Element Description n_name Official name of the network n_aliases An array, terminated with a NULL pointer, of alternative names for the network n_addrtype The address family of the network address being returned, always set to AF_INET n_net Network number, returned in host-byte order ═══ 8.15. getnetent() ═══ The socket call returns a pointer to the next entry in the NETWORKS file. Syntax #include struct netent *getnetent() Description This call returns a pointer to the next entry of the ETC\NETWORKS file. Return Values The getnetent() call returns a pointer to the next entry in the ETC\NETWORKS file. The return value points to static data that subsequent API calls can modify. A pointer to a netent structure indicates success. A NULL pointer indicates an error or EOF. The netent structure is defined in the header file and contains the following elements: Element Description n_name Official name of the network n_aliases An array, terminated with a NULL pointer, of alternative names for the network n_addrtype The address family of the network address being returned, always set to AF_INET n_net Network number, returned in host-byte order ═══ 8.16. getprotobyname() ═══ The socket call returns a pointer to the PROTOCOL file entry specified by a protocol name. Syntax #include struct protoent *getprotobyname(name) char *name; Parameters name Pointer to the specified protocol Description This call searches the ETC\PROTOCOL file for the specified protocol name. Return Values The getprotobyname() call returns a pointer to a protoent structure for the network protocol specified on the call. The return value points to static data that subsequent API calls can modify. A pointer to a protoent structure indicates success. A NULL pointer indicates an error or EOF. The protoent structure is defined in the header file and contains the following elements: Element Description p_name Official name of the protocol p_aliases Array, terminated with a NULL pointer, of alternative names for the protocol p_proto Protocol number ═══ 8.17. getprotobynumber() ═══ The socket call returns a pointer to the PROTOCOL file entry specified by a protocol number. Syntax #include struct protoent * getprotobynumber(proto) int proto; Parameters proto Protocol number Description This call searches the ETC\PROTOCOL file for the specified protocol number. Return Values The getprotobynumber() call returns a pointer to a protoent structure for the network protocol specified on the call. The return value points to static data that subsequent API calls can modify. A pointer to a protoent structure indicates success. A NULL pointer indicates an error or EOF. The protoent structure is defined in the header file and contains the following elements: Element Description p_name Official name of the protocol p_aliases Array, terminated with a NULL pointer, of alternative names for the protocol p_proto Protocol number ═══ 8.18. getprotoent() ═══ The socket call returns a pointer to the next entry in the PROTOCOL file. Syntax #include struct protoent *getprotoent() Description This call searches for the next entry in the ETC\PROTOCOL file. Return Values The getprotoent() call returns a pointer to the next entry in the file, ETC\PROTOCOL. The return value points to static data that subsequent API calls can modify. A pointer to a protoent structure indicates success. A NULL pointer indicates an error or EOF. The protoent structure is defined in the header file and contains the following elements: Element Description p_name Official name of the protocol p_aliases Array, terminated with a NULL pointer, of alternative names for the protocol p_proto Protocol number ═══ 8.19. getservbyname() ═══ The socket call returns a pointer to the SERVICES file entry specified by a service name. Syntax #include struct servent *getservbyname(name, proto) char *name; char *proto; Parameters name Pointer to the service name proto Pointer to the specified protocol Description This call searches the ETC\SERVICES file for the specified service name, which must match the protocol if a protocol is stated. Return Values The call returns a pointer to a servent structure for the network service specified on the call. The return value points to static data that subsequent API calls can modify. A pointer to a servent structure indicates success. A NULL pointer indicates an error or EOF. The servent structure is defined in the header file and contains the following elements: Element Description s_name Official name of the service s_aliases Array, terminated with a NULL pointer, of alternative names for the service s_port Port number of the service s_proto Required protocol to contact the service ═══ 8.20. getservbyport() ═══ The socket call returns a pointer to the SERVICES file entry specified by a port number. Syntax #include struct servent *getservbyport(port, proto) int port; char *proto; Parameters port Specified port proto Pointer to the specified protocol Description This call sequentially searches the ETC\SERVICES file for the specified port number, which must match the protocol if a protocol is stated. Return Values The getservbyport() call returns a pointer to a servent structure for the port number specified on the call. The return value points to static data that subsequent API calls can modify. A pointer to a servent structure indicates success. A NULL pointer indicates an error or EOF. The servent structure is defined in the header file and contains the following elements: Element Description s_name Official name of the service s_aliases Array, terminated with a NULL pointer, of alternative names for the service s_port Port number of the service s_proto Required protocol to contact the service ═══ 8.21. getservent() ═══ The socket call returns a pointer to the next entry in the SERVICES file. Syntax #include struct servent *getservent() Description This call searches for the next line in the ETC\SERVICES file. Return Values The getservent() call returns a pointer to the next entry in the ETC\SERVICES file. The return value points to static data that subsequent API calls can modify. A pointer to a servent structure indicates success. A NULL pointer indicates an error or EOF. The servent structure is defined in the header file and contains the following elements: Element Description s_name Official name of the service s_aliases Array, terminated with a NULL pointer, of alternative names for the service s_port Port number of the service s_proto Required protocol to contact the service ═══ 8.22. htonl() ═══ The socket call translates a long integer from host-byte order to network-byte order. Syntax #include #include u_long htonl(a) u_long a; Parameters a Unsigned long integer to be put into network-byte order Description This call translates a long integer from host-byte order to network-byte order. Return Values Returns the translated long integer. ═══ 8.23. htons() ═══ The socket call translates a short integer from host-byte order to network-byte order. Syntax #include #include u_short htons(a) u_short a; Parameters a Unsigned short integer to be put into network-byte order Description This call translates a short integer from host-byte order to network-byte order. Return Values Returns the translated short integer. ═══ 8.24. inet_addr() ═══ The socket call constructs an Internet address from character strings representing numbers expressed in standard dotted-decimal notation. Syntax #include u_long inet_addr(cp) char *cp; Parameters cp A character string in standard dotted-decimal notation Description This call interprets character strings representing numbers expressed in standard dotted-decimal notation and returns numbers suitable for use as an Internet address. Values specified in standard dotted-decimal notation take one of the following forms: a.b.c.d a.b.c a.b a When a four-part address is specified, each part is interpreted as a byte of data and assigned, from left to right, to one of the 4 bytes of an Internet address. When a three-part address is specified, the last part is interpreted as a 16-bit quantity and placed in the two rightmost bytes of the network address. This makes the three-part address format convenient for specifying Class B network addresses as 128.net.host. When a two-part address is specified, the last part is interpreted as a 24-bit quantity and placed in the three rightmost bytes of the network address. This makes the two-part address format convenient for specifying Class A network addresses as net.host. When a one-part address is specified, the value is stored directly in the network address space without any rearrangement of its bytes. Numbers supplied as address parts in standard dotted-decimal notation can be decimal, hexadecimal, or octal. Numbers are interpreted in C language syntax. A leading 0x implies hexadecimal; a leading 0 implies octal. A number without a leading 0 implies decimal. Return Values The Internet address is returned in network-byte order. ═══ 8.25. inet_lnaof() ═══ The socket call returns the local network portion of an Internet address. Syntax #include #include u_long inet_lnaof(in) struct in_addr in; Parameters in Host Internet address Description This call breaks apart the Internet host address and returns the local network address portion. Return Values The local network address is returned in host-byte order. ═══ 8.26. inet_makeaddr() ═══ The socket call constructs an Internet address from a network number and a local address. Syntax #include #include struct in_addr inet_makeaddr(net, lna) u_long net; u_long lna; Parameters net Network number lna Local network address Description This call takes a network number and a local network address and constructs an Internet address. Return Values The Internet address is returned in network-byte order. ═══ 8.27. inet_netof() ═══ The socket call returns the network portion of the Internet host address in network-byte order. Syntax #include #include u_long inet_netof(in) struct in_addr in; Parameters in Internet address in network-byte order Description This call breaks apart the Internet host address and returns the network number portion. Return Values The network number is returned in host-byte order. ═══ 8.28. inet_network() ═══ The socket call constructs a network number from character strings representing numbers expressed in standard dotted-decimal notation. Syntax #include u_long inet_network(cp) char *cp; Parameters cp A character string in standard dotted-decimal notation Description This call interprets character strings representing numbers expressed in standard dotted-decimal notation and returns numbers suitable for use as a network number. Return Values The network number is returned in host-byte order. ═══ 8.29. inet_ntoa() ═══ The socket call returns a pointer to a string in dotted-decimal notation. Syntax #include #include char *inet_ntoa(in) struct in_addr in; Parameters in Host Internet address Description This call returns a pointer to a string expressed in the dotted-decimal notation. The inet_ntoa() call accepts an Internet address expressed as a 32-bit quantity in network-byte order and returns a string expressed in dotted-decimal notation. Return Values Returns a pointer to the Internet address expressed in dotted-decimal notation. ═══ 8.30. lswap() ═══ The socket call swaps bytes in a long integer. Syntax #include #include u_long lswap(a) u_long a; Parameters a Unsigned long integer whose bytes are to be swapped Description This call swaps bytes in a long integer. Return Values Returns the translated long integer. ═══ 8.31. ntohl() ═══ The socket call translates a long integer from network-byte order to host-byte order. Syntax #include #include u_long ntohl(a) u_long a; Parameters a Unsigned long integer to be put into host-byte order Description This call translates a long integer from network-byte order to host-byte order. Return Values Returns the translated long integer. ═══ 8.32. ntohs() ═══ The socket call translates a short integer from network-byte order to host-byte order. Syntax #include #include u_short ntohs(a) u_short a; Parameters a Unsigned short integer to be put into host-byte order Description This call translates a short integer from network-byte order to host-byte order. Return Values The ntohs() call returns the translated short integer. ═══ 8.33. res_init() ═══ The socket call initializes the default domain name. Syntax include #include #include #include void res_init() Description This call reads the ETC\RESOLV and ETC\RESOLV2 files for the domain name information and for the IP addresses of the initial hosts running the name server. If none of these files exist, name resolution will use the ETC\HOSTS file. One of these files should be operational. The call stores domain name information in the global _res structure, which is defined in the header file. ═══ 8.34. res_mkquery() ═══ The socket call makes a query message for the name servers in the Internet domain. Syntax #include #include #include #include int res_mkquery (op, dname, class, type, data, datalen, newrr, buf, buflen) int op; char *dname; int class; int type; char *data; int datalen; struct rrec *newrr; char *buf; int buflen; Parameters op The usual type is QUERY, but you can set the parameter to any query type defined in the header file. dname Pointer to the domain name. If dname points to a single label and the RES_DEFNAMES bit in the _res structure defined in the header file is set, the call appends dname to the current domain name. The current domain name is defined in the ETC\RESOLV file. class One of the following values: C_IN ARPA Internet C_CHAOS Chaos network at MIT type One of the following type values for resources and queries: T_A Host address T_NS Authoritative server T_MD Mail destination T_MF Mail forwarder T_CNAME Canonical name T_SOA Start of authority zone T_MB Mailbox domain name T_MG Mail group member T_MR Mail rename name T_NULL NULL resource record T_WKS Well-known service T_PTR Domain name pointer T_HINFO Host information T_MINFO Mailbox information T_MX Mail routing information T_UINFO User information T_UID User ID T_GID Group ID data Pointer to the data sent to the name server as a search key datalen Size of the data parameter in bytes newrr Reserved. Unused parameter. buf Pointer to the query message buflen Length of the buffer in bytes pointed to by the buf parameter Description This call makes a query message for the name servers in the Internet domain and puts that query message in the location pointed by the buf parameter. It uses global _res structure, which is defined in the header file. Return Values If it succeeds, the res_mkquery() call returns the size of the query. If the query is larger than the value of buflen, the call fails and returns a value of -1. ═══ 8.35. res_send() ═══ The socket call sends a query to a local name server. Syntax #include #include #include #include int re_send(msg, msglen, ans, anslen) char *msg; int msglen; char *ans; int anslen; Parameters msg Pointer to the beginning of a message msglen Length of the buffer in bytes pointed to by the msg parameter ans Pointer to the location where the received response is stored anslen Length of the buffer in bytes pointed by the ans parameter Description This call sends a query to the local name server and calls the res_init() call if the RES_INIT option of the global _res structure is not set. It also handles timeouts and retries. It uses the global _res structure, which is defined in the header file. Return Values If it succeeds, the call returns the length of the message. If it fails, the call returns a value of -1. ═══ 8.36. sethostent() ═══ The socket call opens and rewinds the HOSTS file. Syntax #include void sethostent(stayopen) int stayopen; Parameters stayopen Allows the ETC\HOSTS file to stay open after each call Description This call opens and rewinds the ETC\HOSTS file. If the stayopen parameter is nonzero, the ETC\HOSTS file stays open after each of the gethost calls. Return Values The sethostent() call returns a global pointer to the FILE structure defined in the header file. A NULL pointer indicates an error or EOF. ═══ 8.37. setnetent() ═══ The socket call opens and rewinds the NETWORKS file. Syntax #include void setnetent(stayopen) int stayopen; Parameters stayopen Allows the ETC\NETWORKS file to stay open after each call Description This call opens and rewinds the ETC\NETWORKS file, which contains information about known networks. If the stayopen parameter is nonzero, the ETC\NETWORKS file stays open after each of the getnet calls. Return Values The setnetent() call returns a global pointer to the FILE structure defined in the header file. A NULL pointer indicates an error or EOF. ═══ 8.38. setprotoent() ═══ The socket call opens and rewinds the PROTOCOL file. Syntax #include void setprotoent(stayopen) int stayopen; Parameters stayopen Allows the ETC\PROTOCOL file to stay open after each call Description This call opens and rewinds the ETC\PROTOCOL file, which contains information about known protocols. If the stayopen parameter is nonzero, the ETC\PROTOCOL file stays open after each of the getproto calls. Return Values The setprotoent() call returns a global pointer to the FILE structure defined in the header file. A NULL pointer indicates an error or EOF. ═══ 8.39. setservent() ═══ The socket call opens and rewinds the SERVICES file. Syntax #include void setservent(stayopen) int stayopen; Parameters stayopen Allows the ETC\SERVICES file to stay open after each call Description This call opens and rewinds the ETC\SERVICES file, which contains information about known services and well-known ports. If the stayopen parameter is nonzero, the ETC\SERVICES file stays open after each of the getserv calls. Return Values The setservent() call returns a global pointer to the FILE structure defined in the header file. A NULL pointer indicates an error or EOF. ═══ 9. Remote Procedure Calls (RPCs) ═══ This section describes the high-level remote procedure calls (RPCs) implemented in TCP/IP for OS/2, including the RPC programming interface to the C language and communication between processes. Topics RPC Protocol Remote Programs and Procedures Portmapper eXternal Data Representation (XDR) RPC Intermediate Layer RPC Lowest Layer rpcgen Command rpcinfo Command enum clnt_stat Structure Remote Procedure Call Library Porting an RPC API Application Compiling and Linking an RPC API Application Remote Procedure and eXternal Data Representation Calls ═══ 9.1. RPC Protocol ═══ The RPC protocol enables remote execution of subroutines across a TCP/IP network. RPC, together with the eXternal Data Representation (XDR) protocol, defines a standard for representing data that is independent of internal protocols or formatting. RPCs can communicate between processes on the same or different hosts. The RPC protocol enables users to work with remote procedures as if the procedures were local. The remote procedure calls are defined through routines contained in the RPC protocol. Each call message is matched with a reply message. The RPC protocol is a message-passing protocol that implements other non-RPC protocols, such as batching and broadcasting remote calls. The RPC protocol also supports callback procedures and the select subroutine on the server side. RPC provides an authentication process that identifies the server and client to each other. RPC includes a slot for the authentication parameters on every remote procedure call so that the caller can identify itself to the server. The client package generates and returns authentication parameters. RPC supports various types of authentication, such as the UNIX systems. In RPC, each server supplies a program that is a set of procedures. The combination of a host address, a program number, and a procedure number specifies one remote service procedure. In the RPC model, the client makes a procedure call to send a data packet to the server. When the packet arrives, the server calls a dispatch routine, performs whatever service is requested, and sends a reply back to the client. The procedure call then returns to the client. RPC is divided into two layers: intermediate and lowest. Generally, you use the RPC interface to communicate between processes on different workstations in a network. However, RPC works just as well for communication between different processes on the same workstation. The Portmapper program maps RPC program and version numbers to a transport-specific port number. The Portmapper program makes dynamic binding of remote programs possible. To write network applications using RPC, programmers need a working knowledge of network theory and C programming language. For most applications, understanding the RPC mechanisms usually hidden by the RPCGEN protocol compiler is also helpful. However, RPCGEN makes understanding the details of RPC unnecessary. The figures in The RPC Interface give an overview of the high-level RPC client and server processes from initialization through cleanup. See the SAMPLES\RPC directory for sample RPC client, server, and raw data stream programs. RPCGEN samples are in the SAMPLES\RPCGEN directory. For more information about the RPC and XDR protocols, see the Sun Microsystems publication, Networking on the Sun Workstation: Remote Procedure Call Programming Guide, RFC 1057 and RFC 1014. ═══ 9.2. The RPC Interface ═══ The RPC model is similar to the local procedure call model. In the local model, the caller places the argument to a procedure in a specified location such as a result register. Then, the caller transfers control to the procedure. The caller eventually regains control, extracts the results of the procedure, and continues the execution. RPC works in the same way: One thread of control winds logically through the caller and server processes as follows: 1. The caller process sends a call message that includes the procedure parameters to the server process and then waits for a reply message (blocks). 2. A process on the server side, which is dormant until the arrival of the call message, extracts the procedure parameters, computes the results, and sends a reply message. Then the server waits for the next call message. 3. A process on the caller side receives the reply message and extracts the results of the procedure. The caller then resumes the execution. See the following figures for an illustration of the RPC model: Remote Procedure Call (Client) Remote Procedure Call (Server) ═══ 9.3. Remote Programs and Procedures ═══ The RPC call message has three unsigned fields:  Remote program number  Remote program version number  Remote procedure number The three fields uniquely identify the procedure to be called. The program number defines a group of related remote procedures, each of which has a different procedure number. Each program also has a version number. The central system authority administers the program number. A remote program number is assigned by groups of 0x20000000, as shown in the following list: Program Number Description 0-1xxxxxxx Is predefined and administered by the OS/2 TCP/IP system. 20000000-3xxxxxxx Represents the user defined numbers 40000000-5xxxxxxx Represents transient numbers 60000000-7xxxxxxx Reserved 80000000-9xxxxxxx Reserved a0000000-bxxxxxxx Reserved c0000000-dxxxxxxx Reserved e0000000-fxxxxxxx Reserved ═══ 9.4. Portmapper ═══ This section describes the Portmapper service and its uses. Topics Portmapper Protocol Registering and Unregistering a Port with Portmapper Contacting Portmapper Portmapper Procedures ═══ 9.4.1. Portmapper Protocol ═══ The Portmapper protocol defines a network service that clients use to look up the port number of any remote program supported by the server. The client programs must find the port numbers of the server programs that they intend to use. The Portmapper program:  Maps RPC program and version numbers to transport specific port numbers.  Makes dynamic binding of remote programs. This is desirable because the range of reserved port numbers is small, and the number of potential remote programs is large. When running only the Portmapper program on a reserved port, you can determine the port numbers of other remote programs by querying Portmapper.  Supports both the UDP and TCP protocols. The RPC client contacts Portmapper on port number 111 on either of these protocols. ═══ 9.4.2. Registering and Unregistering a Port with Portmapper ═══ Portmapper is the only network service that must have a dedicated port (111). Other RPC network services can be assigned port numbers statically or dynamically, if the services register their ports with the host's local Portmapper. The RPC server can register or unregister their services by using the following calls: svc_register() Associates a program with the service dispatch routine svc_unregister() Removes all local mappings to dispatch routines and port numbers registerrpc() Registers a procedure with the local Portmapper and creates a control structure to remember the server procedure and its XDR routine ═══ 9.4.3. Contacting Portmapper ═══ To find the port of a remote program, the client sends an RPC request to well-known port 111 of the server's host. If Portmapper has a port number entry for the remote program, Portmapper provides the port number in the RPC reply. The client then requests the remote program by sending an RPC request to the port number provided by Portmapper. Clients can save port numbers of recently called remote programs to avoid having to contact Portmapper for each request to a server. RPC also provides the following calls for interfacing with Portmapper: Call Description pmap_getmaps() Returns a list of current program-to-port mappings on the foreign host pmap_getport() Returns the port number associated with the remove program, version, and transport protocol pmap_rmtcall() Instructs Portmapper to make an RPC call to a procedure on the host pmap_set() Sets the mapping of a program to a port on the local machine pmap_unset() Removes mappings associated with the program and version number on the local machine xdr_pmap() Translates an RPC procedure identification xdr_pmaplist() Translates a variable number of RPC procedure identifications ═══ 9.4.4. Portmapper Procedures ═══ The Portmapper program supports the following procedures: Procedure Description PMAPPROC_NULL Has no parameters. A caller can use the return code to determine if Portmapper is running. PMAPPROC_SET Registers itself with the Portmapper program on the same machine. It passes the:  Program number  Program version number  Transport protocol number  Port number The procedure has successfully established the mapping if the return value is TRUE. The procedure does not establish a mapping if one already exists. PMAPPROC_UNSET Unregisters the program and version numbers with Portmapper on the same machine. PMAPPROC_GETPORT Returns the port number when given a program number, version number, and transport protocol number. A port value of 0 indicates the program has not been registered. PMAPPROC_DUMP Takes no input, but returns a list of program, version, protocol, and port numbers. PMAPPROC_CALLIT Allows a caller to call another remote procedure on the same machine without knowing the remote procedure's port number. The PMAPPROC_CALLIT procedure sends a response only if the procedure is successfully run. ═══ 9.5. eXternal Data Representation (XDR) ═══ This section describes the eXternal Data Representation (XDR) standard and its use. Topics The XDR Standard Basic Block Size The XDR Subroutine Format XDR Data Types and their Filter Primitives XDR Nonfilter Primitives ═══ 9.5.1. The XDR Standard ═══ An eXternal Data Representation (XDR) is a data representation standard that is independent of languages, operating systems, manufacturers, and hardware architecture. This standard enables networked computers to share data regardless of the machine on which the data is produced or consumed. The XDR language permits transfer of data between diverse computer architectures and has been used to communicate data between diverse machines. An XDR approach to standardizing data representations is canonical. That is, XDR defines a single byte (big endian), a single floating-point representation (IEEE), and so on. Any program running on any machine can use XDR to create portable data by translating its local representation to the XDR standards. Similarly, any program running on any machine can read portable data by translating the XDR standard representations to its local equivalents. The XDR standard is the backbone of the RPC, because data for remote procedure calls is sent using the XDR standard. To use XDR routines, C programs must include the header file, which is automatically included by the header file. ═══ 9.5.2. Basic Block Size ═══ The XDR language is based on the assumption that bytes (an octet) can be ported to, and encoded on, media that preserve the meaning of the bytes across the hardware boundaries of data. XDR does not represent bit fields or bit maps; it represents data in blocks of multiples of 4 bytes (32 bits). If the bytes needed to contain the data are not a multiple of four, enough (0 to 3) bytes to make the total byte count a multiple of four follow the n bytes. The bytes are read from, or written to, a byte stream in order. The order dictates that byte m precedes m+1. Bytes are ported and encoded from low order to high order in local area networks (LANs). Representing data in standardized formats resolves situations that occur when different byte-ordering formats exist on networked machines. This also enables machines with different structure-alignment algorithms to communicate with each other. ═══ 9.5.3. The XDR Subroutine Format ═══ An XDR routine is associated with each data type. XDR routines have the following format: xdr_xxx(xdrs,dp) XDR *xdrs; xxx *dp; { } The routine has the following parameters: xxx XDR data type. xdrs Opaque handle that points to an XDR stream. The system passes the opaque handle pointer to the primitive XDR routines. dp Address of the data value that is to be encoded or decoded. If they succeed, the XDR routines return a value of 1; if they do not succeed, they return a value of 0. ═══ 9.5.4. XDR Data Types and their Filter Primitives ═══ The following basic and constructed data types are defined in the XDR standard: ┌───────────────────────────────────────┬──────────────────────────────────────┐ │ o Integers │ o Structures │ │ o Enumeration │ o Discriminated unions │ │ o Booleans │ o Void │ │ o Floating-point decimals │ o Constants │ │ o Opaque data │ o Typedef │ │ o Arrays │ o Optional data │ │ o Strings │ o Pointers │ └───────────────────────────────────────┴──────────────────────────────────────┘ The XDR filter primitives are routines that define the basic and constructed data types. The XDR language provides RPC programmers with a specification for uniform representation that includes filter primitives for basic and constructed data types. The basic data types include: ┌───────────────────────────────────────┬──────────────────────────────────────┐ │ o Integers │ o Void │ │ o Enumeration │ o Constants │ │ o Booleans │ o Typedef │ │ o Floating point decimals │ o Optional data │ └───────────────────────────────────────┴──────────────────────────────────────┘ The constructed data types include: ┌───────────────────────────────────────┬──────────────────────────────────────┐ │ o Arrays │ o Structures │ │ o Opaque data │ o Discriminated unions │ │ o Strings │ o Pointers │ │ o Byte arrays │ │ └───────────────────────────────────────┴──────────────────────────────────────┘ The XDR standard translates both basic and constructed data types. For basic data types such as integer, XDR provides basic filter primitives that:  Serialize information from the local host's representation to XDR representation  Deserialize information from the XDR representation to the local host's representation For constructed data types, XDR provides constructed filter primitives that allow the use of basic data types (such as integers and floating-point numbers) to create more complex constructs (such as arrays and discriminated unions). ═══ 9.5.4.1. Integer Filter Primitives ═══ The XDR filters cover signed and unsigned integers, as well as signed and unsigned short and long integers. The routines for XDR integer filters are: Routine Description xdr_int() Translates between C integers and their external representations xdr_u_int() Translates between C unsigned integers and their external representations xdr_long() Translates between C long integers and their external representations xdr_u_long() Translates between C unsigned long integers and their external representations xdr_short() Translates between C short integers and their external representations xdr_u_short() Translates between C unsigned short integers and their external representations ═══ 9.5.4.2. Enumeration Filter Primitives ═══ The XDR library provides a primitive for generic enumerations based on the assumption that a C enumeration value (enum) has the same representation. A special enumeration in XDR, known as the Boolean, provides a value of 0 or 1 represented internally in a binary notation. The routines for the XDR library enumeration filters are: Routine Description xdr_enum() Translates between C language enums and their external representations xdr_bool() Translates between Booleans and their external representations ═══ 9.5.4.3. Floating-Point Filter Primitives ═══ The XDR library provides primitives that translate between floating-point data and their external representations. Floating-point data encodes an integer with an exponent. Floats and double-precision numbers compose floating-point data. Note: Numbers are represented as Institute of Electrical and Electronics Engineers (IEEE) standard floating points. Routines might fail when decoding IEEE representations into machine specific representations. The routines for the XDR floating-point filters are: Routine Description xdr_float() Translates between C language floats and their external representations xdr_double() Translates between C language double-precision numbers and their external representations ═══ 9.5.4.4. Opaque Data Filter Primitive ═══ Opaque data is composed of bytes of a fixed size that are not interpreted as they pass through the data streams. Opaque data bytes, such as handles, are passed between server and client without being inspected by the client. The client uses the data as it is and then returns it to the server. By definition, the actual data contained in the opaque object is not portable between computers. The XDR library includes the following routine for opaque data: Routine Description xdr_opaque() Translates between opaque data and its external representation ═══ 9.5.4.5. Array Filter Primitives ═══ Arrays are constructed filter primitives that can be generic arrays or byte arrays. The XDR library provides filter primitives for handling both types of arrays. ═══ 9.5.4.5.1. Generic Arrays ═══ These consist of arbitrary elements. You use them in much the same way as byte arrays, which handle a subset of generic arrays where the size of the elements is 1 and their external descriptions are predetermined. The primitive for generic arrays requires an additional parameter to define the size of the element in the array and to call an XDR routine to encode or decode each element in the array. The XDR library includes the following routines for generic arrays: Routine Description xdr_array() Translates between variable-length arrays and their corresponding external representations xdr_vector() Translates between fixed-length arrays and their corresponding external representations ═══ 9.5.4.5.2. Byte Arrays ═══ These differ from strings by having a byte count. That is, the length of the array is set to an unsigned integer. They also differ in that byte arrays do not end with a null character. The XDR library provides a primitive for byte arrays. External and internal representations of byte arrays are the same. The XDR library includes the following routine for byte arrays: Routine Description xdr_bytes() Translates between counted byte string arrays and their external representations ═══ 9.5.4.6. String Filter Primitives ═══ A string is a constructed filter primitive that consists of a sequence of bytes terminated by a null byte. The null byte does not figure into the length of the string. Externally, strings are represented by a sequence of American Standard Code Information Interchange (ASCII) characters. Internally, XDR represents them as pointers to characters with the designation char *. The XDR library includes primitives for the following string routines: Routine Description xdr_string() Translates between C language strings and their external representations xdr_wrapstring() Calls the xdr_string subroutine ═══ 9.5.4.7. Primitive for Pointers to Structures ═══ The XDR library provides the primitive for pointers so that structures referenced within other structures can be easily serialized, deserialized, and released. The XDR library includes the following routine for pointers to structures: Routine Description xdr_reference() Provides pointer chasing within structures ═══ 9.5.4.8. Primitive for Discriminated Unions ═══ A discriminated union is a C language union, which is an object that holds several data types. One arm of the union contains an enumeration value (enum_t), or discriminant, that holds a specific object to be processed over the system first. The XDR library includes the following routine for discriminated unions: Routine Description xdr_union() Translates between discriminated unions and their external representations ═══ 9.5.4.9. Passing Routines without Data ═══ Sometimes an XDR routine must be supplied to the RPC system, but no data is required or passed. The XDR library provides the following primitive for this function: Routine Description xdr_void() Supplies an XDR subroutine to the RPC system without sending data ═══ 9.5.5. XDR Nonfilter Primitives ═══ Use the XDR nonfilter primitives to create, manipulate, implement, and destroy XDR data streams. These primitives allow you to:  Describe the data stream position  Change the data stream position  Destroy a data stream ═══ 9.5.5.1. Creating and Using XDR Data Streams ═══ You get XDR data streams by calling creation routines that take arguments specifically designed to the properties of the stream. There are existing XDR data streams for serializing or deserializing data in standard input and output streams, memory streams, and record streams. Note: RPC clients do not have to create XDR streams, because the RPC system creates and passes these streams to the client. The types of data streams include:  Standard I/O streams  Memory streams  Record streams ═══ 9.5.5.1.1. Standard I/O Streams ═══ XDR data streams serialize and deserialize standard input/output( I/O) by calling the standard I/O creation routine to initialize the XDR data stream pointed to by the xdrs parameter. The XDR library includes the following routine for standard I/O data streams: Routine Description xdrstdio_create() Initializes the XDR data stream pointed to by the xdrs parameter ═══ 9.5.5.1.2. Memory Streams ═══ XDR data streams serialize and deserialize data from memory by calling the XDR memory creation routine to initialize, in local memory, the XDR stream pointed at by the xdrs parameter. In RPC, the UDP/IP implementation of remote procedure calls uses this routine to build entire call and reply messages in memory before sending the message to the recipient. The XDR library includes the following routine for memory data streams: Routine Description xdrmem_create() Initializes, in local memory, the XDR stream pointed to by the xdrs parameter ═══ 9.5.5.1.3. Record Streams ═══ Record streams are XDR streams built on top of record fragments, which are built on TCP/IP streams. TCP/IP is a connection protocol for transporting large streams of data at one time rather than transporting a single data packet at a time. The primary use of a record stream is to interface remote procedure calls to TCP connections. It can also be used to stream data into or out of normal files. XDR provides the following routines for use with record streams: Routine Description xdrrec_create() Provides an XDR stream that can contain long sequences of records xdrrec_endofrecord() Causes the current outgoing data to be marked as a record xdrrec_skiprecord() Causes the position of an input stream to move to the beginning of the next record xdrrec_eof() Checks the buffer for an input stream that identifies the end of file (EOF) ═══ 9.5.5.2. Manipulating an XDR Data Stream ═══ XDR provides the following routines for describing the data stream position and changing the data stream position: Routine Description xdr_getpos() Returns an unsigned integer that describes the current position in the data stream xdr_setpos() Changes the current position in the XDR stream ═══ 9.5.5.3. Implementing an XDR Data Stream ═══ You can create and implement XDR data streams. The following example shows the abstract data types (XDR handle) required for you to implement your own XDR streams. They contain operations applied to the stream (an operation vector for the particular implementation) and two private fields for using that implementation. enum xdr_op { XDR_ENCODE=0, XDR_DECODE=1, XDR_FREE=2 }; typedef struct xdr { enum xdr_op x_op; struct xdr_ops { bool_t (*x_getlong)(struct xdr *, long *); bool_t (*x_putlong)(struct xdr *, long *); bool_t (*x_getbytes)(struct xdr *, caddr_t, u_int); /* get some bytes from " */ bool_t (*x_putbytes)(struct xdr *, caddr_t, u_int); /* put some bytes to " */ u_int (*x_getpostn)(struct xdr *); bool_t (*x_setpostn)(struct xdr *,u_int); long * (*x_inline)(struct xdr *,u_int); void (*x_destroy)(struct xdr *); } *x_ops; caddr_t x_public; caddr_t x_private; caddr_t x_base; int x_handy; } XDR; The following parameters are pointers to XDR stream manipulation routines: Parameter Description x_getlong Gets long integer values from the data stream. x_putlong Puts long integer values into the data stream. x_getbytes Gets bytes from the data streams. x_putbytes Puts bytes into the data streams. x_getpostn Returns the stream offset. x_setpostn Repositions the offset. x_inline Points to an internal data buffer, used for any purpose. x_destroy Frees the private data structure. x_ops Specifies the current operation being performed on the stream. This field is important to the XDR primitives, but the stream's implementation does not depend on the value of this parameter. The following fields are specific to a stream's implementation: Field Description x_public Specific user data that is private to the stream's implementation and that is not used by the XDR primitive x_private Points to the private data x_base Contains the position information in the data stream that is private to the user implementation x_handy Data can contain extra information as necessary ═══ 9.5.5.4. Destroying an XDR Data Stream ═══ XDR provides a routine that destroys the XDR stream pointed to by the xdrs parameter and frees the private data structures allocated to the stream. Routine Description xdr_destroy() Destroys the XDR stream pointed to by the xdrs parameter The use of the XDR stream handle is undefined after it is destroyed. ═══ 9.6. RPC Intermediate Layer ═══ The calls of the RPC intermediate layer are: Routine Description registerrpc() Registers a procedure with the local Portmapper callrpc() Calls a remote procedure on the specified system svc_run() Accepts RPC requests and calls the appropriate service using svc_getreq() The transport mechanism is the User Datagram Protocol (UDP). The UDP transport mechanism handles only arguments and results that are less than 8K bytes in length. At this level, RPC does not allow time-out specifications, choice of transport, or process control, in case of errors. If you need this kind of control, consider the lowest layer of RPC. With only these three RPC calls, you can write a powerful RPC-based network application. The sequence of events follows: 1. Use the registerrpc() call to register your remote program with the local Portmapper. See Portmapper for more information. The following is an example of an RPC server: /* define remote program number and version */ #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1 #define RMTPROCNUM (u_long)0x1 #include #include main() { int *rmtprog(); /* register remote program with portmapper */ registerrpc(RMTPROGNUM, RMTPROGVER, RMTPROCNUM, rmtprog, xdr_int, xdr_int); /* infinite loop, waits for RPC request from client */ svc_run(); printf("Error: svc_run should never reach this point \n"); exit(1); } int * rmtprog(inproc) /* remote program */ int *inproc; { int *outproc; ... /* Process request */ ... return (outproc); } The registerrpc() call registers a C procedure rmtprog, which corresponds to a given RPC procedure number. The registerrpc() call has six parameters:  The first three parameters, RMTPROGNUM, RMTPROGVER, and RMTPROCNUM, are the program, version, and procedure numbers of the remote procedure to be registered.  The fourth parameter, rmtprog, is the name of the local procedure that implements the remote procedure.  The last two parameters, xdr_int, are the XDR filters for the remote procedure's arguments and results. After registering a procedure, the RPC server goes into an infinite loop waiting for a client request to service. 2. The RPC client uses callrpc() to make a service request to the RPC server. The following is an example of an RPC client using the callrpc() call: /* define remote program number and version */ #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1 #define RMTPROCNUM (u_long)0x1 #include #include main() { int inproc=100, outproc, rstat; ... /* service request to host RPCSERVER_HOST */ if (rstat = callrpc("RPCSERVER_HOST", RMTPROGNUM, RMTPROGVER, RMTPROCNUM, xdr_int, (char *)&inproc, xdr_int, (char *)&outproc)!= 0) { clnt_perrno(rstat); /* Why callrpc() failed ? */ exit(1); } ... } The callrpc() call has eight parameters:  The first is the name of the remote server machine.  The next three parameters are the program, version, and procedure numbers.  The fifth and sixth parameters are an XDR filter, and an argument to be encoded and passed to the remote procedure.  The final two parameters are a filter for decoding the results returned by the remote procedure, and a pointer to the place where the procedure's results are to be stored. You handle multiple arguments and results by embedding them in structures. The callrpc() call returns 0 if it succeeds, otherwise nonzero. The exact meaning of the returned code is in the header file and is an enum clnt_stat structure cast into an integer. ═══ 9.7. RPC Lowest Layer ═══ This section describes the lowest layer of RPC and when to use it. Topics When to Use the RPC Lowest Layer Server Side Program Client Side Program ═══ 9.7.1. When to Use the RPC Lowest Layer ═══ Use the lowest layer of RPC in the following situations:  You need to use TCP. The intermediate layer uses UDP, which restricts RPC calls to 8K bytes of data. TCP permits calls to send long streams of data.  You want to allocate and free memory while serializing or deserializing messages with XDR routines. No RPC call at the intermediate level explicitly permits freeing memory. XDR routines are used for memory allocation as well as for serializing and deserializing.  You need to perform authentication on the client side or the server side by supplying credentials or verifying them. ═══ 9.7.2. Server Side Program ═══ The following is an example of the lowest layer of RPC on the server side program: #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1L #define LONGPROC 1 #define STRINGPROC 2 #define MAXLEN 100 #include #include #include main(argc, argv) int argc; char *argv[ ]; { int rmtprog(); SVCXPRT *transp; ... /* create TCP transport handle */ transp = svctcp_create(RPC_ANYSOCK, 1024*10, 1024*10); /* or create UDP transport handle */ /* transp = svcudp_create(RPC_ANYSOCK); */ if (transp == NULL) /* check transport handle creation */ { fprintf(stderr, "can't create an RPC server transport\n"); exit(-1); } /* If exists, remove the mapping of remote program and port */ pmap_unset(RMTPROGNUM, RMTPROGVER); /* register remote program (TCP transport) with local portmapper */ if (!svc_register(transp, RMTPROGNUM, RMTPROGVER, rmtprog, IPPROTO_TCP)) /* or register remote program (UDP transport) with local portmapper */ /* if (!svc_register(transp, RMTPROGNUM, RMTPROGVER, rmtprog,*/ /* IPPROTO_UDP)) */ { fprintf(stderr, "can't register rmtprog() service\n"); exit(-1); } svc_run(); printf("Error:svc_run should never reaches this point \n"); exit(1); } rmtprog(rqstp, transp) /* code for remote program */ struct svc_req *rqstp; SVCXPRT *transp; { long in_long,out_long; char buf[100], *in_string=buf, *out_string=buf; ... switch((int)rqstp->rq_proc) /* Which procedure ? */ { case NULLPROC: if (!svc_sendreply(transp,xdr_void, 0)) { fprintf(stderr,"can't reply to RPC call\n"); exit(-1); } return; case LONGPROC: ... /* Process the request */ if (!svc_sendreply(transp,xdr_long,&out_long)) { fprintf(stderr,"can't reply to RPC call\n"); exit(-1); } return; case STRINGPROC: /* send received "Hello" message back */ /* to client */ svc_getargs(transp,xdr_wrapstring,(char *)&in_string); strcpy(out_string,in_string); /* send a reply back to a RPC client */ if (!svc_sendreply(transp,xdr_wrapstring, (char *)&out_string)) { fprintf(stderr,"can't reply to RPC call\n"); exit(-1); } return; case ... : ... /* Any Remote procedure in RMTPROGNUM program */ ... default: /* Requested procedure not found */ svcerr_noproc(transp); return; } } The following steps describe the lowest layer of RPC on the server side program: 1. Service the transport handle. The svctcp_create() and svcudp_create() calls create TCP and UDP transport handles (SVCXPRT) respectively, used for receiving and replying to RPC messages. The SVCXPRT transport handle structure is defined in the header file. If the argument of the svctcp_create() call is RPC_ANYSOCK, the RPC library creates a socket on which to receive and reply to remote procedure calls. The svctcp_create() and clnttcp_create() calls cause the RPC library calls to bind the appropriate socket, if it is not already bound. If the argument of the svctcp_create() call is not RPC_ANYSOCK, the svctcp_create() call expects its argument to be a valid socket number. If you specify your own socket, it can be bound or unbound. If it is bound to a port by you, the port numbers of the svctcp_create() and clnttcp_create() calls must match. If the send and receive buffer size parameter of svctcp_create() is 0, the system selects a reasonable default. 2. Register the rmtprog service with Portmapper. If the rmtprog service terminated abnormally the last time it was used, the pmap_unset() call erases any trace of it before restarting. The pmap_unset() call erases the entry for RMTPROGNUM from the Portmapper's table. A service can register its port number with the local Portmapper service by specifying a nonzero protocol number in the svc_register() call. A programmer at the client machine can determine the server port number by consulting Portmapper at the server machine. You can do this automatically by specifying 0 as the port number in the clntudp_create() or clnttcp_create() calls. Finally, the program and version number are associated with the rmtprog procedure. The final argument to the svc_register() call is the protocol being used, which in this case is IPPROTO_TCP. Register at the program level, not at the procedure level. 3. Run the remote program RMTPROG. The rmtprog service routine must call and dispatch the appropriate XDR calls based on the procedure number. Unlike the registerrpc() call, which performs them automatically, the rmtprog routine requires two tasks:  When the NULLPROC procedure (currently 0) returns with no results, use it as a simple test for detecting whether a remote program is running.  Check for incorrect procedure numbers. If you detect one, call the svcerr_noproc() call to handle the error. As an example, the procedure STRINGPROC has an argument for a character string and returns the character string back to the client. The svc_getargs() call takes an SVCXPRT handle, the xdr_wrapstring() call, and a pointer that indicates where to place the input. The user service (rmtprog) serializes the results and returns them to the RPC caller through the svc_sendreply() call. Parameters of the svc_sendreply() call include the:  SVCXPRT handle  XDR routine, which indicates return data type  Pointer to the data to be returned ═══ 9.7.3. Client Side Program ═══ The following is an example of the lowest layer of RPC on the client side program: #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1L #define STRINGPROC (u_long)2 #include #include #include #include main(argc, argv) int argc; char *argv[ ]; { struct hostent *hp; struct timeval pertry_timeout, total_timeout; struct sockaddr_in server_addr; int sock = RPC_ANYSOCK; static char buf[100], *strc_in= "Hello", *strc_out=buf; char *parrc_in, *parrc_out; register CLIENT *clnt; enum clnt_stat cs; ... /* get the Internet address of RPC server host */ if ((hp = gethostbyname("RPCSERVER_HOST")) == NULL) { fprintf(stderr,"Can't get address for %s\n","RPCSERVER_HOST"); exit (-1); } pertry_timeout.tv_sec = 3; pertry_timeout.tv_usec = 0; /* set sockaddr_in structure */ bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr.s_addr, hp->h_length); server_addr.sin_family = AF_INET; server_addr.sin_port = 0; /* create clnt TCP handle */ if ((clnt = clnttcp_create(&server_addr, RMTPROGNUM, RMTPROGVER, &sock, 1024*10, 1024*10)) == NULL) { clnt_pcreateerror("clnttcp_create fail"); /* Why failed ? */ exit(-1); } /* * create clnt UDP handle * if ((clnt = clntudp_create(&server_addr, RMTPROGNUM, RMTPROGVER, * pertry_timeout, &sock)) == NULL) * { * clnt_pcreateerror("clntudp_create fail"); * exit(-1); * } */ total_timeout.tv_sec = 10; total_timeout.tv_usec = 0; ... /*call the remote procedure STRINGPROC associated with */ /*client handle (clnt) */ cs=clnt_call(clnt, STRINGPROC,xdr_wrapstring, (char *)&strc_in[j], xdr_wrapstring, (char *)&strc_out,total_timeout); if (cs != RPC_SUCCESS) printf("*Error* clnt_call fail :\n"); clnt_destroy(clnt); /* deallocate any memory associated */ /* with clnt handle */ ... } The following steps describe the lowest layer of RPC on the client side program: 1. Determine the internet address of the RPC server host. Use the gethostbyname() call to determine the internet address of the host, which is running the RPC server. Initialize the socaddr_in structure, found in the header file. If you are not familiar with socket calls, see Sockets General Programming Information. 2. Use the client RPC handle. The clnttcp_create() and clntudp_create() calls create TCP and UDP client RPC handles (CLIENT), respectively. The CLIENT structure is defined in the header file. There are six parameters for the clnttcp_create() call:  Server address  Program number  Version number  Pointer to a valid socket descriptor  Send buffer size  Receive buffer size Use the same parameters for the clntudp_create() call, except for the send and receive buffer size. Instead, specify a time-out value (between tries). 3. Call the remote procedure. The low-level version of the callrpc() call is the clnt_call(), which has seven parameters:  CLIENT pointer  Remote procedure number (STRINGPROC)  XDR call for serializing the argument  Pointer to the argument  XDR call for deserializing the return value from the RPC server  Pointer to where the return value is to be placed  Total time in seconds to wait for a reply For UDP transport, the number of tries is the clnt_call() time-out divided by the clntudp_create() time-out. The return code RPC_SUCCESS indicates a successful call; otherwise, an error has occurred. You find the RPC error code in the header file. The clnt_destroy() call always deallocates the space associated with the client handle. If the RPC library opened the socket associated with the client handle, the clnt_destroy() call closes it. If you open the socket, it stays open. ═══ 9.8. rpcgen Command ═══ Use the rpcgen command to generate C code to implement an RPC protocol. The input to RPCGEN is a language similar to C, known as RPC language. You normally use rpcgen infile to generate the following four output files. For example, if the infile is named PROTO.X, rpcgen generates:  A header file called PROTO.H  XDR routines called PROTOX.C  Server-side stubs called PROTOS.C  Client-side stubs called PROTOC.C For more information on the rpcgen command, see the Sun Microsystems publication, Networking on the Sun Workstation: Remote Procedure Call Programming Guide. Syntax >>──rpcgen── infile──>< >>──rpcgen──┬─ -c─┬──┬─────────────┬──┬─────────┬──>< ├─ -h─┤ └─ -o outfile─┘ └─ infile─┘ ├─ -l─┤ └─ -m─┘ >>──rpcgen── -s transport──┬─────────────┬──┬─────────┬──>< └─ -o outfile─┘ └─ infile─┘ Parameters -c Compiles into XDR routines. -h Compiles into C data definitions (a header file). -l Compiles into client-side stubs. -m Compiles into server-side stubs without generating a main routine. -o outfile Specifies the name of the output file. If none is specified, standard output is used for -c, -h, -l, -m, and -s modes. infile Specifies the name of the input file written in the RPC language. -s transport Compiles into server-side stubs, using the given transport. ═══ 9.9. rpcinfo Command ═══ The rpcinfo command makes an RPC call to the RPC server and reports the status of the server, which is registered and operational with Portmapper. Syntax rpcinfo for a Host ┌─ local_host─┐ >>──rpcinfo── -p──┼─────────────┼──┬─────────────┬──>< └─ host───────┘ └─ > filename─┘ rpcinfo for a Host Using UDP >>──rpcinfo──┬─────────────┬── -u host prognum──┬──────────┬───────────────────> └─ -n portnum─┘ └─ versnum─┘ >──┬─────────────┬──>< └─ > filename─┘ rpcinfo for a Host Using TCP >>──rpcinfo──┬─────────────┬── -t host prognum──┬──────────┬───────────────────> └─ -n portnum─┘ └─ versnum─┘ >──┬─────────────┬──>< └─ > filename─┘ rpcinfo for a Broadcast to Hosts Using UDP >>──rpcinfo──┬─────┬── prognum── versnum──┬─────────────┬──>< └─ -b─┘ └─ > filename─┘ Parameters -p host Queries the Portmapper about the specified host and prints a list of all registered RPC programs. If the host is not specified, the system defaults to the local host name. > filename Specifies a file to which to redirect the list of registered RPC programs. -n portnum Specifies the port number to be used for the -t and -u parameters. This value replaces the port number that is given by the Portmapper. -u host prognum versnum Sends an RPC call to procedure 0 of prognum and versnum on the specified host using UDP and reports whether a response is received. -t host prognum versnum Sends an RPC call to procedure 0 of prognum and versnum on the specified host using TCP and reports whether a response is received. -b prognum versnum Sends an RPC broadcast to procedure 0 of the specified prognum and versnum using UDP and reports all hosts that respond. The prognum argument can be either a name or a number. If you specify a versnum, the rpcinfo command tries to call that version of the specified program. Otherwise, it tries to find all the registered version numbers for the program you specify by calling version 0; then it tries to call each registered version. The TCPIP\ETC\RPC file is associated with the rpcinfo command. This file contains a list of server names and their corresponding RPC program numbers and aliases. Examples Use the rpcinfo command as follows to display RPC services registered on the local host: rpcinfo -p Examples Use the rpcinfo command as follows to display RPC services registered on a remote host named charm: rpcinfo -p charm Examples Use the rpcinfo command as follows to display the status of a particular RPC program on the remote host named charm: rpcinfo -u charm 100003 2 or rpcinfo -u charm nfs 2 In the previous examples, the rpcinfo command shows one of the following: Program 100003 Version 2 ready and waiting or Program 100003 Version 2 is not available Examples Use the rpcinfo command as follows to display all hosts on the local network that are running a certain version of a specific RPC server: rpcinfo -b 100003 2 or rpcinfo -b nfsprog 2 In these examples, the rpcinfo command lists all hosts that are running Version 2 of the NFS daemon. Note: The version number is required for the -b parameter. ═══ 9.10. enum clnt_stat Structure ═══ The enum clnt_stat structure is defined in the file. RPCs frequently return enum clnt_stat information. The format of the enum clnt_stat structure follows: enum clnt_stat { RPC_SUCCESS=0, /* call succeeded */ /* * local errors */ RPC_CANTENCODEARGS=1, /* can't encode arguments */ RPC_CANTDECODERES=2, /* can't decode results */ RPC_CANTSEND=3, /* failure in sending call */ RPC_CANTRECV=4, /* failure in receiving result */ RPC_TIMEDOUT=5, /* call timed out */ /* * remote errors */ RPC_VERSMISMATCH=6, /* RPC versions not compatible */ RPC_AUTHERROR=7, /* authentication error */ RPC_PROGUNAVAIL=8, /* program not available */ RPC_PROGVERSMISMATCH=9, /* program version mismatched */ RPC_PROCUNAVAIL=10, /* procedure unavailable */ RPC_CANTDECODEARGS=11, /* decode arguments error */ RPC_SYSTEMERROR=12, /* generic "other problem" */ /* * callrpc errors */ RPC_UNKNOWNHOST=13, /* unknown host name */ /* * create errors */ RPC_PMAPFAILURE=14, /* the pmapper failed in its call */ RPC_PROGNOTREGISTERED=15, /* remote program is not registered */ /* * unspecified error */ RPC_FAILED=16 }; ═══ 9.11. Remote Procedure Call Library ═══ To use the RPCs described in this section, you must have the following header files in your TCPIP\INCLUDE directory: RPC Header File What It Contains RPC\AUTH.H Authentication interface RPC\AUTH_UNI.H Protocol for UNIX-style authentication parameters for RPC RPC\CLNT.H Client-side remote procedure call interface RPC\PMAP_CLN.H Supplies C routines to get to PORTMAP services RPC\PMAP_PRO.H Protocol for the local binder service, or pmap RPC\RPC.H Includes the RPC header files necessary to do remote procedure calling RPC\RPC_MSG.H Message definitions RPC\RPCNETDB.H Data definitions for network utility calls RPC\RPCTYPES.H RPC additions to RPC\SVC.H Server-side remote procedure call interface RPC\SVC_AUTH.H Service side of RPC authentication RPC\XDR.H eXternal Data Representation serialization routines The RPC routines are in the RPC32DLL.LIB file in the LIB directory. You must also have the SO32DLL.LIB and TCP32DLL.LIB files in your LIB directory. Put the following statement at the beginning of any file using RPC code: #include You must define the OS/2 variable by doing one of the following:  Place #define OS2 at the top of each file that includes TCP/IP header files.  Use the /DOS2 option when compiling the source for your application. ═══ 9.12. Porting an RPC API Application ═══ The IBM OS/2 RPC implementation differs from the Sun Microsystems RPC implementation as follows:  The global variables svc_socks[] and noregistered are used in place of the svc_fds global variable. See svc_socks [] for the use of these variables.  Functions that rely on file descriptor structures are not supported.  The svc_getreq() call supports the socks and noavail global variables. In the Sun Microsystems implementation, the svc_getreq() call supports the rdfds global variable.  TYPES.H for RPC has been renamed to RPCTYPES.H. ═══ 9.13. Compiling and Linking an RPC API Application ═══ Follow these steps to compile and link the RPC API application using an IBM 32-bit compiler for OS/2: 1. Set your environment variables to find the following:  Executable programs  Link libraries  Header files You can set the environment variables in your CONFIG.SYS file. An example of entries you might have in your CONFIG.SYS file follows: LIBPATH=E:\IBMC\DLL;C:\MPTN\DLL;C:\TCPIP\DLL; SET PATH=E:\IBMC\BIN; SET DPATH=E:\IBMC\LOCALE;E:\IBMC\HELP; SET LIB=E:\IBMC\LIB;C:\MPTN\LIB;C:\TCPIP\LIB; SET INCLUDE=E:\IBMC\INCLUDE;C:\TCPIP\INCLUDE; SET HELP=E:\IBMC\HELP; SET BOOKSHELF=E:\IBMC\HELP; SET TMP=E:\IBMC\TMP SET TZ=EST5EDT,0,0,0,0,0,0,0,0,0 2. To compile your program, enter: icc /Ti /DOS2 /Sm /Ss /Q /Su4 /Gt /C myprog.c 3. To create an executable program, you can enter: For VisualAge C++ ilink /NOFREEFORMAT /De myprog,myprog.exe,NULL,so32dll.lib tcp32dll.lib rpc32dll.lib For C Set++ link386 /De myprog,myprog.exe,NULL,so32dll.lib tcp32dll.lib rpc32dll.lib Note: 1. The RPC API is not re-entrant. If you are using a multithreaded program, you must serialize the access to the APIs. 2. Before you run a program, verify that the DLL files are in a directory listed in the LIBPATH system environment variable. 3. For more information about the compile and link options, and dynamic link libraries, see the User's Guide provided with your compiler. ═══ 9.14. Remote Procedure and eXternal Data Representation Calls ═══ This section provides the syntax, parameters, and other appropriate information for each remote procedure and eXternal Data Representation call supported by TCP/IP for OS/2. Topics auth_destroy() authnone_create() authunix_create() authunix_create_default() callrpc() clnt_broadcast() clnt_call() clnt_destroy() clnt_freeres() clnt_geterr() clnt_pcreateerror() clnt_perrno() clnt_perror() clntraw_create() clnttcp_create() clntudp_create() get_myaddress() getrpcbyname() getrpcbynumber() getrpcent() pmap_getmaps() pmap_getport() pmap_rmtcall() pmap_set() pmap_unset() registerrpc() rpc_createerr svc_destroy() svc_freeargs() svc_getargs() svc_getcaller() svc_getreq() svc_register() svc_run() svc_sendreply() svc_socks [] svc_unregister() svcerr_auth() svcerr_decode() svcerr_noproc() svcerr_noprog() svcerr_progvers() svcerr_systemerr() svcerr_weakauth() svcraw_create() svctcp_create() svcudp_create() xdr_accepted_reply() xdr_array() xdr_authunix_parms() xdr_bool() xdr_bytes() xdr_callhdr() xdr_callmsg() xdr_double() xdr_enum() xdr_float() xdr_getpos() xdr_inline() xdr_int() xdr_long() xdr_opaque() xdr_opaque_auth() xdr_pmap() xdr_pmaplist() xdr_reference() xdr_rejected_reply() xdr_replymsg() xdr_setpos() xdr_short() xdr_string() xdr_u_int() xdr_u_long() xdr_u_short() xdr_union() xdr_vector() xdr_void() xdr_wrapstring() xdrmem_create() xdrrec_create() xdrrec_endofrecord() xdrrec_eof() xdrrec_skiprecord() xdrstdio_create() xprt_register() xprt_unregister() ═══ 9.14.1. auth_destroy() ═══ The auth_destroy() call destroys authentication information. Syntax #include void auth_destroy(auth) AUTH *auth; Parameters auth Pointer to authentication information Description The auth_destroy() call deletes the authentication information for auth. After you call this procedure, auth is undefined. Related Calls authnone_create() authunix_create() authunix_create_default() ═══ 9.14.2. authnone_create() ═══ The authnone_create() call creates and returns a NULL RPC authentication handle. Syntax #include AUTH * authnone_create() Description The authnone_create() call creates and returns an RPC authentication handle. The handle passes the NULL authentication on each call. Related Calls auth_destroy() authunix_create() authunix_create_default() ═══ 9.14.3. authunix_create() ═══ The authunix_create() call creates and returns a UNIX-based authentication handle. Syntax #include AUTH * authunix_create(host, uid, gid, len, aup_gids) char *host; int uid; int gid; int len; int *aup_gids; Parameters host Pointer to the symbolic name of the host where the desired server is located uid User's user ID gid User's group ID len Length of the information pointed to by aup_gids aup_gids Pointer to an array of groups to which the user belongs Description The authunix_create() call creates and returns an authentication handle that contains UNIX-based authentication information. Related Calls auth_destroy() authnone_create() authunix_create_default() ═══ 9.14.4. authunix_create_default() ═══ The authunix_create_default() call calls authunix_create() with default parameters. Syntax #include AUTH * authunix_create_default() Description The authunix_create_default() call calls authunix_create() with default parameters. Related Calls auth_destroy() authnone_create() authunix_create() ═══ 9.14.5. callrpc() ═══ The callrpc() call calls remote procedures. Syntax #include enum clnt_stat callrpc(host, prognum, versnum, procnum, inproc, in, outproc, out) char *host; u_long prognum; u_long versnum; u_long procnum; xdrproc_t inproc; char *in; xdrproc_t outproc; char *out; Parameters host Pointer to the symbolic name of the host where the desired server is located prognum Program number of the remote procedure versnum Version number of the remote procedure procnum Procedure number of the remote procedure inproc XDR procedure used to encode the arguments of the remote procedure in Pointer to the arguments of the remote procedure outproc XDR procedure used to decode the results of the remote procedure out Pointer to the results of the remote procedure Return Values RPC_SUCCESS indicates success; otherwise, an error has occurred. The results of the remote procedure call return to out. Description The callrpc() call calls the remote procedure described by prognum, versnum, and procnum running on the host system. It encodes and decodes the parameters for transfer. Note: 1. You can use clnt_perrno() to translate the return code into messages. 2. callrpc() cannot call the procedure xdr_enum. See xdr_enum() for more information. 3. This procedure uses UDP as its transport layer. See clntudp_create() for more information. Examples #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1 #define RMTPROCNUM (u_long)0x1 int inproc=100, outproc, rstat; ... /* service request to host RPCSERVER_HOST */ if (rstat = callrpc("RPCSERVER_HOST", RMTPROGNUM, RMTPROGVER, RMTPROCNUM, xdr_int, (char *)&inproc, xdr_int, (char *)&outproc)!= 0) { clnt_perrno(rstat); exit(1); } ... Related Calls clnt_call() clnt_perrno() clntudp_create() ═══ 9.14.6. clnt_broadcast() ═══ The clnt_broadcast() call broadcasts a remote program to all locally connected broadcast networks. Syntax #include enum clnt_stat clnt_broadcast(prognum, versnum, procnum, inproc, in, outproc, out, eachresult) u_long prognum; u_long versnum; u_long procnum; xdrproc_t inproc; caddr_t in; xdrproc_t outproc; caddr_t out; resultproc_t eachresult; Parameters prognum Program number of the remote procedure versnum Version number of the remote procedure procnum Procedure number of the remote procedure inproc XDR procedure used to encode the arguments of the remote procedure in Pointer to the arguments of the remote procedure outproc XDR procedure used to decode the results of the remote procedure out Pointer to the results of the remote procedure eachresult Procedure called after each response Note: resultproc_t is a type definition: typedef bool_t (*resultproc_t) (); Return Values If eachresult() returns 0, clnt_broadcast() waits for more replies; otherwise, eachresult() returns the appropriate status. Note: Broadcast sockets are limited in size to the maximum transfer unit of the data link. Description The clnt_broadcast() call broadcasts a remote program described by prognum, versnum, and procnum to all locally connected broadcast networks. Each time clnt_broadcast() receives a response, it calls eachresult(). The format of eachresult() is: #include #include bool_t eachresult(out, addr) char *out; struct sockaddr_in *addr; Parameters out Has the same function as it does for clnt_broadcast(), except that the output of the remote procedure is decoded addr Pointer to the address of the machine that sent the results Examples enum clnt_stat cs; u_long prognum, versnum; ... cs = clnt_broadcast(prognum, versnum, NULLPROC, xdr_void, (char *)NULL, xdr_void, (char *)NULL, eachresult); if ((cs != RPC_SUCCESS) && (cs != RPC_TIMEDOUT)) { fprintf( " broadcast failed: \n"); exit(-1); } ... bool_t eachresult(out, addr) void *out; /* Nothing comes back */ struct sockaddr_in *addr; /* Reply from whom */ { register struct hostent *hp; ... hp = gethostbyaddr((char *) &addr->sin_addr, sizeof addr->sin_addr, AF_INET); printf("%s %s\n", inet_ntoa(addr->sin_addr), hp->h_name); ... return(FALSE); } Related Calls callrpc() clnt_call() ═══ 9.14.7. clnt_call() ═══ The clnt_call() call calls the remote procedure associated with the client handle. Syntax #include enum clnt_stat clnt_call(clnt, procnum, inproc, in, outproc, out, tout) CLIENT *clnt; u_long procnum; xdrproc_t inproc; char *in; xdrproc_t outproc; char *out; struct timeval tout; Parameters clnt Pointer to a client handle that was previously obtained using clntraw_create(), clnttcp_create(), or clntudp_create() procnum Remote procedure number inproc XDR procedure used to encode procnum's arguments in Pointer to the remote procedure's arguments outproc XDR procedure used to decode the remote procedure's results out Pointer to the remote procedure's results tout Time allowed for the server to respond, in units of 0.1 seconds Return Values RPC_SUCCESS indicates success; otherwise, an error has occurred. The results of the remote procedure call are returned to out. Description The clnt_call() call calls the remote procedure (procnum) associated with the client handle (clnt). Examples u_long procnum; register CLIENT *clnt; enum clnt_stat cs; struct timeval total_timeout; int intsend, intrecv; cs=clnt_call(clnt, procnum, xdr_int, &intsend, xdr_int, &intrecv, total_timeout); if ( cs != RPC_SUCCESS) printf("*Error* clnt_call fail :\n"); Related Calls callrpc() clnt_perror() clntraw_create() clnttcp_create() clntudp_create() ═══ 9.14.8. clnt_destroy() ═══ The clnt_destroy() call destroys a client's RPC handle. Syntax #include void clnt_destroy(clnt) CLIENT *clnt; Parameters clnt Pointer to a client handle that was previously created using clntudp_create(), clnttcp_create(), or clntraw_create() Description The clnt_destroy() call deletes a client RPC transport handle. This procedure involves the deallocation of private data resources, including clnt. After you use this procedure, clnt is undefined. Open sockets associated with clnt must be closed. Related Calls clntraw_create() clnttcp_create() clntudp_create() ═══ 9.14.9. clnt_freeres() ═══ The clnt_freeres() call deallocates resources assigned for decoding the results of an RPC. Syntax #include bool_t clnt_freeres(clnt, outproc, out) CLIENT *clnt; xdrproc_t outproc; char *out; Parameters clnt Pointer to a client handle that was previously obtained using clntraw_create(), clnttcp_create(), or clntudp_create() outproc XDR procedure used to decode the remote procedure's results out Pointer to the results of the remote procedure Return Values The value 1 indicates success; the value 0 indicates an error. Description The clnt_freeres() call de-allocates any resources that were assigned by the system to decode the results of an RPC. Related Calls clntraw_create() clnttcp_create() clntudp_create() ═══ 9.14.10. clnt_geterr() ═══ The clnt_geterr() call copies the error structure from a client's handle to the local structure. Syntax #include void clnt_geterr(clnt, errp) CLIENT *clnt; struct rpc_err *errp; Parameters clnt Pointer to a client handle that was previously obtained using clntraw_create(), clnttcp_create(), or clntudp_create() errp Pointer to the address into which the error structure is copied Description The clnt_geterr() call copies the error structure from the client handle to the structure at address errp. Examples u_long procnum; register CLIENT *clnt; enum clnt_stat cs; struct timeval total_timeout; int intsend = 100, intrecv; struct rpc_err error; ... total_timeout.tv_sec = 20; total_timeout.tv_usec = 0; ... cs=clnt_call(clnt, procnum, xdr_int, &intsend, xdr_int, &intrecv, total_timeout); if ( cs != RPC_SUCCESS) { clnt_geterr(clnt, &error); clnt_perror(clnt, "recv from server"); } ... Related Calls clnt_call() clnt_pcreateerror() clnt_perrno() clnt_perror() clntraw_create() clnttcp_create() clntudp_create() ═══ 9.14.11. clnt_pcreateerror() ═══ The clnt_pcreateerror() call indicates why a client handle cannot be created. Syntax #include void clnt_pcreateerror(s) char *s; Parameters s Pointer to a string that is to be printed in front of the message. The string is followed by a colon. Description The clnt_pcreateerror() call writes a message to the standard error device, indicating why a client handle cannot be created. Use this procedure after the clntraw_create(), clnttcp_create(), or clntudp_create() call fails. For an example of the clnt_pcreateerror() call, see clnttcp_create(). Related Calls clnt_geterr() clnt_perrno() clnt_perror() clntraw_create() clnttcp_create() clntudp_create() ═══ 9.14.12. clnt_perrno() ═══ The clnt_perrno() call writes a message to the standard error device corresponding to the condition indicated by stat. Syntax #include void clnt_perrno(stat) enum clnt_stat stat; Parameters stat The client status Description The clnt_perrno() call writes a message to the standard error device corresponding to the condition indicated by stat. Use this procedure after callrpc() and clnt_broadcast() if there is an error. Related Calls callrpc() clnt_geterr() clnt_pcreateerror() clnt_perror() ═══ 9.14.13. clnt_perror() ═══ The clnt_perror() call writes an error message indicating why RPC failed. Syntax #include void clnt_perror(clnt, s) CLIENT *clnt; char *s; Parameters clnt Pointer to a client handle that was previously obtained using clntudp_create(), clnttcp_create(), or clntraw_create(). s Pointer to a string that is to be printed in front of the message. The string is followed by a colon. Description The clnt_perror() call writes a message to the standard error device, indicating why an RPC failed. Use this procedure after clnt_call() if there is an error. For an example of the clnt_perror() call, see clnt_geterr(). Related Calls clnt_call() clnt_geterr() clnt_pcreateerror() clnt_perrno() clntraw_create() clnttcp_create() clntudp_create() ═══ 9.14.14. clntraw_create() ═══ The clntraw_create() call creates a client transport handle to use in a single task. Syntax #include CLIENT * clntraw_create(prognum, versnum) u_long prognum; u_long versnum; Parameters prognum Remote program number versnum Version number of the remote program Return Values NULL indicates failure. Description The clntraw_create() call creates a dummy client for the remote double (prognum, versnum). Because messages are passed using a buffer within the address space of the local process, the server should also use the same address space, which simulates RPC programs within one address space. See svcraw_create() for more information. Related Calls clnt_call() clnt_destroy() clnt_pcreateerror() clnttcp_create() clntudp_create() svcraw_create() ═══ 9.14.15. clnttcp_create() ═══ The clnttcp_create() call creates an RPC client transport handle for the remote program using TCP transport. Syntax #include CLIENT * clnttcp_create(addr, prognum, versnum, sockp, sendsz, recvsz) struct sockaddr_in *addr; u_long prognum; u_long versnum; int *sockp; u_int sendsz; u_int recvsz; Parameters addr Pointer to the internet address of the remote program. If addr points to a port number of 0, addr is set to the port on which the remote program is receiving. prognum Remote program number. versnum Version number of the remote program. sockp Pointer to the socket. If sockp is RPC_ANYSOCK, then this routine opens a new socket and sets sockp. sendsz Size of the send buffer. Specify 0 to have clnttcp_create() pick a suitable default size. recvsz Size of the receive buffer. Specify 0 to have clnttcp_create() pick a suitable default size. Return Values NULL indicates failure. Description The clnttcp_create() call creates an RPC client transport handle for the remote program specified by (prognum, versnum). The client uses TCP as the transport layer. Examples #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1L register CLIENT *clnt; int sock = RPC_ANYSOCK; /* can be also valid socket descriptor */ struct hostent *hp; struct sockaddr_in server_addr; /* get the internet address of RPC server */ if ((hp = gethostbyname("RPCSERVER_HOST") == NULL) { fprintf(stderr,"Can't get address for %s\n",argv[2]); exit (-1); } bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr.s_addr, hp->h_length); server_addr.sin_family = AF_INET; server_addr.sin_port = 0; /* create TCP handle */ if ((clnt = clnttcp_create(&server_addr, RMTPROGNUM, RMTPROGVER, &sock, 1024*10, 1024*10)) == NULL) { clnt_pcreateerror("clnttcp_create"); exit(-1); } Related Calls clnt_destroy() clnt_pcreateerror() clntraw_create() clntudp_create() ═══ 9.14.16. clntudp_create() ═══ The clntudp_create() call creates an RPC client transport handle for the remote program using UDP transport. Syntax #include #include CLIENT * clntudp_create(addr, prognum, versnum, wait, sockp) struct sockaddr_in *addr; u_long prognum; u_long versnum; struct timeval wait; int *sockp; Parameters addr Pointer to the internet address of the remote program. If addr points to a port number of 0, addr is set to the port on which the remote program is receiving. The remote PORTMAP service is used for this. prognum Remote program number. versnum Version number of the remote program. wait Interval at which UDP resends the call request, until either a response is received or the call times out. Set the time-out length using the clnt_call() procedure. sockp Pointer to the socket. If sockp is RPC_ANYSOCK, this routine opens a new socket and sets sockp. Return Values NULL indicates failure. Description The clntudp_create() call creates a client transport handle for the remote program (prognum) with version (versnum). UDP is used as the transport layer. Note: Do not use this procedure with procedures that use large arguments or return large results. UDP RPC messages can contain only 2K bytes of encoded data. Examples #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1L register CLIENT *clnt; int sock = RPC_ANYSOCK; /* can be also valid socket descriptor */ struct hostent *hp; struct timeval pertry_timeout; struct sockaddr_in server_addr; /* get the internet address of RPC server */ if ((hp = gethostbyname("RPC_HOST") == NULL) { fprintf(stderr,"Can't get address for %s\n",argv[2]); exit (-1); } pertry_timeout.tv_sec = 3; pertry_timeout.tv_usec = 0; bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr.s_addr, hp->h_length); server_addr.sin_family = AF_INET; server_addr.sin_port = 0; /* create UDP handle */ if ((clnt = clntudp_create(&server_addr, RMTPROGNUM, RMTPROGVER, pertry_timeout, &sock)) == NULL) { clnt_pcreateerror("clntudp_create"); exit(-1); } Related Calls clnt_destroy() clnt_pcreateerror() clntraw_create() clnttcp_create() ═══ 9.14.17. get_myaddress() ═══ The get_myaddress() call returns the local host's internet address. Syntax #include void get_myaddress(addr) struct sockaddr_in *addr; Parameters addr Pointer to the location where the local internet address is placed Description The get_myaddress() call puts the local host's internet address into addr. The port number (addr->sin_port) is set to htons (PMAPPORT), which is 111. ═══ 9.14.18. getrpcbyname() ═══ The getrpcbyname() call returns an RPC program entry specified by a name in the RPC file. Syntax #include struct rpcent *getrpcbyname(name) char *name; Parameters name Pointer to the specified RPC program Return Values The getrpcbyname() call returns a pointer to an object with the rpcent structure for the RPC program specified on the call. The rpcent structure is defined in the header file and contains the following elements: Element Description r_name The name of the server for this RPC program r_aliases A zero terminated list of alternate names for the RPC program r_number The RPC program number for this service The return value points to static data that later calls overwrite. A pointer to an rpcent structure indicates success. A NULL pointer indicates an error or EOF. Description The getrpcbyname() call sequentially searches from the beginning of the TCPIP\ETC\RPC file until it finds a matching RPC program name or encounters EOF. Related Calls getrpcbynumber() getrpcent() ═══ 9.14.19. getrpcbynumber() ═══ The getrpcbynumber() call returns an RPC program entry specified by a number in the RPC file. Syntax #include struct rpcent *getrpcbynumber(number) u_long number; Parameters number RPC program number Return Values The getrpcbynumber() call returns a pointer to an object with the rpcent structure for the RPC program specified on the call. The rpcent structure is defined in header file and contains the following elements: Element Description r_name The name of the server for this RPC program r_aliases A zero terminated list of alternate names for the RPC program r_number The RPC program number for this service The return value points to static data that later calls overwrite. A pointer to an rpcent structure indicates success. A NULL pointer indicates an error or EOF. Description The getrpcbynumber() call sequentially searches from the beginning of the TCPIP\ETC\RPC file until it finds a matching RPC program number or encounters EOF. Related Calls getrpcbyname() getrpcent() ═══ 9.14.20. getrpcent() ═══ The getrpcent() call returns the next entry in the RPC file. Syntax #include struct rpcent *getrpcent() Return Values The getrpcent() call returns a pointer to the next entry in the TCPIP\ETC\RPC file. The rpcent structure is defined in the header file and contains the following elements: Element Description r_name The name of the server for this RPC program r_aliases A zero terminated list of alternate names for the RPC program r_number The RPC program number for this service The return value points to static data that later calls overwrite. A pointer to an rpcent structure indicates success. A NULL pointer indicates an error or EOF. Description The getrpcent() call searches for the next line in the TCPIP\ETC\RPC file. Related Calls getrpcbyname() getrpcbynumber() ═══ 9.14.21. pmap_getmaps() ═══ The pmap_getmaps() call returns a list of current program-to-port mappings on a specified remote host's Portmapper. Syntax #include struct pmaplist * pmap_getmaps(addr) struct sockaddr_in *addr; Parameters addr Pointer to the internet address of the remote host Description The pmap_getmaps() call returns a list of current program-to-port mappings on the remote host's Portmapper specified by addr. Examples struct hostent *hp; struct sockaddr_in pmapper_addr; struct pmaplist *my_pmaplist = NULL; if ((hp = gethostbyname("PMAP_HOST") == NULL) { fprintf(stderr,"Can't get address for %s\n","PMAP_HOST"); exit (-1); } bcopy(hp->h_addr, (caddr_t)&pmapper_addr.sin_addr.s_addr, hp->h_length); pmapper_addr.sin_family = AF_INET; pmapper_addr.sin_port = 0; /* * get the list of program, version, protocol and port number * from remote portmapper * * struct pmap { * long unsigned pm_prog; * long unsigned pm_vers; * long unsigned pm_prot; * long unsigned pm_port; * }; * struct pmaplist { * struct pmap pml_map; * struct pmaplist *pml_next; * }; */ my_pmaplist = pmap_getmaps(&pmapper_addr); ... Related Calls pmap_getport() pmap_rmtcall() pmap_set() pmap_unset() ═══ 9.14.22. pmap_getport() ═══ The pmap_getport() call returns the port number associated with a remote program. Syntax #include u_short pmap_getport(addr, prognum, versnum, protocol) struct sockaddr_in *addr; u_long prognum; u_long versnum; u_long protocol; Parameters addr Pointer to the internet address of the remote host prognum Program number to be mapped versnum Version number of the program to be mapped protocol Transport protocol used by the program Return Values The value 0 indicates that the mapping does not exist or that the remote PORTMAP could not be contacted. If Portmapper cannot be contacted, rpc_createerr contains the RPC status. Description The pmap_getport() call returns the port number associated with the remote program (prognum), the version (versnum), and the transport protocol (protocol). Related Calls pmap_getmaps() pmap_rmtcall() pmap_set() pmap_unset() ═══ 9.14.23. pmap_rmtcall() ═══ The pmap_rmtcall() call instructs Portmapper to make an RPC call to a procedure on a host on your behalf. Syntax #include #include enum clnt_stat pmap_rmtcall(addr, prognum, versnum, procnum, inproc, in, outproc, out, tout, portp) struct sockaddr_in *addr; u_long prognum; u_long versnum; u_long procnum; xdrproc_t inproc; char *in; xdrproc_t outproc; char *out; struct timeval tout; u_long *portp; Parameters addr Pointer to the internet address of the foreign host prognum Remote program number versnum Version number of the remote program procnum Procedure to be called inproc XDR procedure that encodes the arguments of the remote procedure in Pointer to the arguments of the remote procedure outproc XDR procedure that decodes the results of the remote procedure out Pointer to the results of the remote procedure tout Time-out period for the remote request portp Port number of the triple (prognum, versnum, procnum), if the call from the remote PORTMAP service succeeds Return Values RPC_SUCCESS indicates success; otherwise, an error has occurred. The results of the remote procedure call return to out. Description The pmap_rmtcall() call instructs Portmapper to make an RPC call to a procedure on a host, on your behalf. Use this procedure only for ping-type functions. Examples int inproc, outproc,rc; u_long portp; struct timeval total_timeout; struct sockaddr_in *addr; ... get_myaddress(addr); ... total_timeout.tv_sec = 20; total_timeout.tv_usec = 0; rc = pmap_rmtcall(addr,RMTPROGNUM,RMTPROGVER,RMTPROCNUM,xdr_int, &inproc,xdr_int,&outproc,total_timeout,&portp); if (rc != 0) { fprintf(stderr,"error: pmap_rmtcall() failed: %d \n",rc); clnt_perrno(rc); exit(1); } Related Calls pmap_getmaps() pmap_getport() pmap_set() pmap_unset() ═══ 9.14.24. pmap_set() ═══ The pmap_set() call sets the mapping of a server program to a port on the local machine's Portmapper. Syntax #include bool_t pmap_set(prognum, versnum, protocol, port) u_long prognum; u_long versnum; u_long protocol; u_short port; Parameters prognum Local program number versnum Version number of the local program protocol Transport protocol used by the local program port Port to which the local program is mapped Return Values The value 1 indicates success; the value 0 indicates an error. Description The pmap_set() call sets the mapping of the program (specified by prognum, versnum, and protocol) to port on the local machine's Portmapper. This procedure is automatically called by the svc_register() procedure. Related Calls pmap_getmaps() pmap_getport() pmap_rmtcall() pmap_unset() ═══ 9.14.25. pmap_unset() ═══ The pmap_unset() call removes the mappings on the local machine's Portmapper. Syntax #include bool_t pmap_unset(prognum, versnum) u_long prognum; u_long versnum; Parameters prognum Local program number versnum Version number of the local program Return Values The value 1 indicates success; the value 0 indicates an error. Description The pmap_unset() call removes the mappings associated with prognum and versnum on the local machine's Portmapper. All ports for each transport protocol currently mapping the prognum and versnum are removed from the PORTMAP service. Examples #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1L ... /* remove the mapping of remote program */ /* and its port from local portmapper */ pmap_unset(RMTPROGNUM, RMTPROGVER); ... Related Calls pmap_getmaps() pmap_getport() pmap_rmtcall() pmap_set() ═══ 9.14.26. registerrpc() ═══ The registerrpc() call registers a procedure with the local Portmapper. Syntax #include int registerrpc(prognum, versnum, procnum, procname, inproc, outproc) u_long prognum; u_long versnum; u_long procnum; char *(*procname) (); xdrproc_t inproc; xdrproc_t outproc; Parameters prognum Program number to register. versnum Version number to register. procnum Procedure number to register. procname Procedure that is called when the registered program is requested. procname must accept a pointer to its arguments and return a static pointer to its results. inproc XDR procedure that decodes the arguments. outproc XDR procedure that encodes the results. Note: You cannot use xdr_enum() as an argument to registerrpc(). See xdr_enum() for more information. Return Values The value 0 indicates success; the value -1 indicates an error. Description The registerrpc() call registers a procedure with the local Portmapper and creates a control structure to remember the server procedure and its XDR routine. The svc_run() call uses the control structure. Procedures registered using registerrpc() are accessed using the UDP transport layer. Examples #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1 #define RMTPROCNUM (u_long)0x1 main() { int *rmtprog(); /* register remote program with portmapper */ registerrpc(RMTPROGNUM, RMTPROGVER, RMTPROCNUM, rmtprog, xdr_int, xdr_int); /* infinite loop, waits for RPC request from client */ svc_run(); printf("Error: svc_run should never reach this point \n"); exit(1); } int * rmtprog(inproc) /* remote program */ int *inproc; { int *outproc; ... /* Process request */ ... return (outproc); } Related Calls svc_register() svc_run() ═══ 9.14.27. rpc_createerr ═══ rpc_createerr is a global variable set when any RPC client creation routine fails. Syntax #include struct rpc_createerr rpc_createerr; Description rpc_createerr is a global variable that is set when any RPC client creation routine fails. Use clnt_pcreateerror() to print the message. ═══ 9.14.28. svc_destroy() ═══ The svc_destroy() call destroys the RPC service transport handle. Syntax #include void svc_destroy(xprt) SVCXPRT *xprt; Parameter xprt Pointer to the service transport handle Description The svc_destroy() call deletes the RPC service transport handle xprt, which becomes undefined after this routine is called. Related Calls svcraw_create() svctcp_create() svcudp_create() ═══ 9.14.29. svc_freeargs() ═══ The svc_freeargs() call frees storage allocated for argument decoding. Syntax #include bool_t svc_freeargs(xprt, inproc, in) SVCXPRT *xprt; xdrproc_t inproc; char *in; Parameters xprt Pointer to the service transport handle inproc XDR routine that decodes the arguments in Pointer to the input arguments Return Values The value 1 indicates success; the value 0 indicates an error. Description The svc_freeargs() call frees storage allocated to decode the arguments received by svc_getargs(). Related Calls svc_getargs() ═══ 9.14.30. svc_getargs() ═══ The svc_getargs() call decodes arguments from an RPC request. Syntax #include bool_t svc_getargs(xprt, inproc, in) SVCXPRT *xprt; xdrproc_t inproc; char *in; Parameters xprt Pointer to the service transport handle inproc XDR routine that decodes the arguments in Pointer to the decoded arguments Return Values The value 1 indicates success; the value 0 indicates an error. Description The svc_getargs() call uses the XDR routine inproc to decode the arguments of an RPC request associated with the RPC service transport handle xprt. The results are placed at address in. Examples #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1L ... SVCXPRT *transp; transp = svcudp_create(RPC_ANYSOCK); if (transp == NULL) { fprintf(stderr, "can't create an RPC server transport\n"); exit(-1); } pmap_unset(RMTPROGNUM, RMTPROGVER); if (!svc_register(transp, RMTPROGNUM, RMTPROGVER, rmtprog, IPPROTO_UDP)) { fprintf(stderr, "can't register rmtprog() service\n"); exit(-1); } printf("rmtprog() service registered.\n"); svc_run(); printf("Error:svc_run should never reach this point \n"); exit(1); ... rmtprog(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { int intrecv; switch((int)rqstp->rq_proc) { case PROCNUM1: svc_getargs(transp, xdr_int, &intrecv); ... return; case PROCNUM2: ... } ... } Related Calls svc_freeargs() ═══ 9.14.31. svc_getcaller() ═══ The svc_getcaller() call gets the network address of the client associated with the service transport handle. Syntax #include struct sockaddr_in * svc_getcaller(xprt) SVCXPRT *xprt; Parameters xprt Pointer to the service transport handle Description This call gets the network address of the client associated with the service transport handle. ═══ 9.14.32. svc_getreq() ═══ The svc_getreq() call implements asynchronous event processing and returns control to the program after all sockets have been serviced. Syntax #include void svc_getreq(socks, noavail) int socks[]; int noavail; Parameters socks Array of socket descriptors noavail Integer specifying the number of socket descriptors in the array Description Use the svc_getreq() call rather than svc_run() to do asynchronous event processing. The routine returns control to the program when all sockets in the socks array have been serviced. Related Calls svc_run() svc_socks[] ═══ 9.14.33. svc_register() ═══ The svc_register() call registers procedures on the local Portmapper. Syntax #include #include bool_t svc_register(xprt, prognum, versnum, dispatch, protocol) SVCXPRT *xprt; u_long prognum; u_long versnum; void (*dispatch) (); int protocol; Parameters xprt Pointer to the service transport handle. prognum Program number to be registered. versnum Version number of the program to be registered. dispatch Dispatch routine associated with prognum and versnum. The structure of the dispatch routine is as follows: dispatch(request, xprt) struct svc_req *request; SVCXPRT *xprt; protocol Protocol used. The value is generally one of the following:  0 (zero)  IPPROTO_UDP  IPPROTO_TCP When you use a value of 0, the service is not registered with Portmapper. Return Values The value 1 indicates success; the value 0 indicates an error. Description The svc_register() call associates the specified program with the service dispatch routine dispatch. Note: When you use a toy RPC service transport created with svcraw_create(), make a call to xprt_register() immediately after a call to svc_register(). Examples #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1L SVCXPRT *transp; /* register the remote program with local portmapper */ if (!svc_register(transp, RMTPROGNUM, RMTPROGVER, rmtprog, IPPROTO_UDP)) { fprintf(stderr, "can't register rmtprog() service\n"); exit(-1); } /* code for remote program; rmtprog */ rmtprog(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { ... ... } Related Calls registerrpc() svc_unregister() xprt_register() ═══ 9.14.34. svc_run() ═══ The svc_run() call accepts RPC requests and calls the appropriate service. Syntax #include void svc_run() Description The svc_run() call accepts RPC requests and calls the appropriate service using svc_getreq(). The svc_run() call does not return control to the caller. Examples #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1L ... SVCXPRT *transp; transp = svcudp_create(RPC_ANYSOCK); if (transp == NULL) { fprintf(stderr, "can't create an RPC server transport\n"); exit(-1); } pmap_unset(RMTPROGNUM, RMTPROGVER); if (!svc_register(transp, RMTPROGNUM, RMTPROGVER, rmtprog, IPPROTO_UDP)) { fprintf(stderr, "can't register rmtprog() service\n"); exit(-1); } printf("rmtprog() service registered.\n"); svc_run(); printf("Error:svc_run should never reach this point \n"); exit(1); ... rmtprog(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { ... } Related Calls registerrpc() svc_getreq() ═══ 9.14.35. svc_sendreply() ═══ The svc_sendreply() call sends the results of an RPC to the caller. Syntax #include bool_t svc_sendreply(xprt, outproc, out) SVCXPRT *xprt; xdrproc_t outproc; char *out; Parameters xprt Pointer to the caller's transport handle outproc XDR procedure that encodes the results out Pointer to the results Return Values The value 1 indicates success; the value 0 indicates an error. Description The service dispatch routine calls the svc_sendreply() call to send the results of the call to the caller. Examples #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1L ... SVCXPRT *transp; transp = svcudp_create(RPC_ANYSOCK); if (transp == NULL) { fprintf(stderr, "can't create an RPC server transport\n"); exit(-1); } pmap_unset(RMTPROGNUM, RMTPROGVER); if (!svc_register(transp, RMTPROGNUM, RMTPROGVER, rmtprog, IPPROTO_UDP)) { fprintf(stderr, "can't register rmtprog() service\n"); exit(-1); } printf("rmtprog() service registered.\n"); svc_run(); printf("Error:svc_run should never reach this point \n"); exit(1); ... rmtprog(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { int intrecv; int replysend; switch((int)rqstp->rq_proc) { case PROCNUM0: svc_getargs(transp, xdr_int, &intrecv); ... /* process intrecv parameter */ replysend = ( intrecv * 1000) + 100; /* send reply to client */ if (!svc_sendreply(transp, xdr_int, &replysend)) { fprintf(stderr,"can't reply to RPC call\n"); exit(-1); } return; case PROCNUM1: ... ... } ... } ═══ 9.14.36. svc_socks [] ═══ svc_socks[] is an array of socket descriptors being serviced. Syntax #include int svc_socks[]; #include int noregistered; Description svc_socks[] is an array of socket descriptors being serviced. noregistered is an integer that specifies the number of socket descriptors in svc_socks[]. Related Calls svc_getreq() ═══ 9.14.37. svc_unregister() ═══ The svc_unregister() call removes the local mapping. Syntax #include void svc_unregister(prognum, versnum) u_long prognum; u_long versnum; Parameters prognum Program number of the removed program versnum Version number of the removed program Description The svc_unregister() call removes all local mappings of (prognum, versnum) to dispatch routines and (prognum, versnum, *) to port numbers. Examples #define RMTPROGNUM (u_long)0x3fffffffL #define RMTPROGVER (u_long)0x1L ... /* unregister remote program from local portmapper */ svc_unregister(RMTPROGNUM, RMTPROGVER); ... Related Calls svc_register() ═══ 9.14.38. svcerr_auth() ═══ The svcerr_auth() call sends an error reply when the service dispatch routine cannot execute an RPC request because of authentication errors. Syntax #include void svcerr_auth(xprt, why) SVCXPRT *xprt; enum auth_stat why; Parameters xprt Pointer to the service transport handle why Reason why the call is refused Description A service dispatch routine that refuses to run an RPC request because of authentication errors calls svcerr_auth(). Related Calls svcerr_decode() svcerr_noproc() svcerr_noprog() svcerr_progvers() svcerr_systemerr() svcerr_weakauth() ═══ 9.14.39. svcerr_decode() ═══ The svcerr_decode() call sends an error reply when the service dispatch routine cannot decode its parameters. Syntax #include void svcerr_decode(xprt) SVCXPRT *xprt; Parameters xprt Pointer to the service transport handle Description A service dispatch routine that cannot decode its parameters calls svcerr_decode(). Related Calls svcerr_auth() svcerr_noproc() svcerr_noprog() svcerr_progvers() svcerr_systemerr() svcerr_weakauth() ═══ 9.14.40. svcerr_noproc() ═══ The svcerr_noproc() call sends an error reply when the service dispatch routine cannot call the procedure requested. Syntax #include void svcerr_noproc(xprt) SVCXPRT *xprt; Parameters xprt Pointer to the service transport handle Description A service dispatch routine that does not implement the requested procedure calls the svcerr_noproc() call. Related Calls svcerr_auth() svcerr_decode() svcerr_noprog() svcerr_progvers() svcerr_systemerr() svcerr_weakauth() ═══ 9.14.41. svcerr_noprog() ═══ The svcerr_noprog() call sends an error code when the requested program is not registered. Syntax #include void svcerr_noprog(xprt) SVCXPRT *xprt; Parameters xprt Pointer to the service transport handle Description Use the svcerr_noprog() call when the desired program is not registered. Related Calls svcerr_auth() svcerr_decode() svcerr_noproc() svcerr_progvers() svcerr_systemerr() svcerr_weakauth() ═══ 9.14.42. svcerr_progvers() ═══ The svcerr_progvers() call sends the low version number and high version number of RPC service when the version numbers of two RPC programs do not match. Syntax #include void svcerr_progvers(xprt, low_vers, high_vers) SVCXPRT *xprt; u_long low_vers; u_long high_vers; Parameters xprt Pointer to the service transport handle low_vers Low version number high_vers High version number Description A service dispatch routine calls the svcerr_progvers() call when the version numbers of two RPC programs do not match. The call sends the supported low version and high version of RPC service. Related Calls svcerr_decode() svcerr_noproc() svcerr_noprog() svcerr_progvers() svcerr_systemerr() svcerr_weakauth() ═══ 9.14.43. svcerr_systemerr() ═══ The svcerr_systemerr() call sends an error reply when the service dispatch routine detects a system error that has not been handled. Syntax #include void svcerr_systemerr(xprt) SVCXPRT *xprt; Parameters xprt Pointer to the service transport handle Description A service dispatch routine calls the svcerr_systemerr() call when it detects a system error that is not handled by the protocol. Related Calls svcerr_auth() svcerr_decode() svcerr_noproc() svcerr_noprog() svcerr_progvers() svcerr_weakauth() ═══ 9.14.44. svcerr_weakauth() ═══ The svcerr_weakauth() call sends an error reply when the service dispatch routine cannot run an RPC because of weak authentication parameters. Syntax #include void svcerr_progvers(xprt) SVCXPRT *xprt; Parameters xprt Pointer to the service transport handle Description A service dispatch routine calls the svcerr_weakauth() call when it cannot run an RPC because of correct but weak authentication parameters Note: This is the equivalent of svcerr_auth(xprt, AUTH_TOOWEAK). Related Calls svcerr_auth() svcerr_decode() svcerr_noproc() svcerr_noprog() svcerr_progvers() svcerr_systemerr() ═══ 9.14.45. svcraw_create() ═══ The svcraw_create() call creates a local RPC service transport handle to simulate RPC programs within one host. Syntax #include SVCXPRT * svcraw_create() Return Values NULL indicates failure. Description The svcraw_create() call creates a local RPC service transport used for timings, to which it returns a pointer. Because messages are passed using a buffer within the address space of the local process, the client process must also use the same address space. This allows the simulation of RPC programs within one host. See clntraw_create() for more information. Related Calls clntraw_create() svc_destroy() svctcp_create() svcudp_create() ═══ 9.14.46. svctcp_create() ═══ The svctcp_create() call creates a TCP-based service transport. Syntax #include SVCXPRT * svctcp_create(sock, send_buf_size, recv_buf_size) int sock; u_int send_buf_size; u_int recv_buf_size; Parameters sock Socket descriptor. If sock is RPC_ANYSOCK, a new socket is created. If the socket is not bound to a local TCP port, it is bound to an arbitrary port. send_buf_size Size of the send buffer. Specify 0 if you want the call to pick a suitable default value. recv_buf_size Size of the receive buffer. Specify 0 if you want the call to pick a suitable default value. Return Values NULL indicates failure. Description The svctcp_create() call creates a TCP-based service transport to which it returns a pointer. xprt->xp_sock contains the transport's socket descriptor; xprt->xp_port contains the transport's port number. Examples ... SVCXPRT *transp; transp = svctcp_create(RPC_ANYSOCK, 1024*10, 1024*10); ... Related Calls svc_destroy() svcraw_create() svcudp_create() ═══ 9.14.47. svcudp_create() ═══ The svcudp_create() call creates a UDP-based service transport. Syntax #include SVCXPRT * svcudp_create(sockp) int sockp; Parameters sockp The socket number associated with the service transport handle. If sockp is RPC_ANYSOCK, a new socket is created. If the socket is not bound to a local port, it is bound to an arbitrary port. Return Values NULL indicates failure. Description The svcudp_create() call creates a UDP-based service transport to which it returns a pointer. xprt->xp_sock contains the transport's socket descriptor. xprt->xp_port contains the transport's port number. Examples ... SVCXPRT *transp; transp = svcudp_create(RPC_ANYSOCK); ... Related Calls svc_destroy() svcraw_create() svctcp_create() ═══ 9.14.48. xdr_accepted_reply() ═══ The xdr_accepted_reply() call translates between an RPC reply message and its external representation. Syntax #include bool_t xdr_accepted_reply(xdrs, ar) XDR *xdrs; struct accepted_reply *ar; Parameters xdrs Pointer to an XDR stream ar Pointer to the reply to be represented Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_accepted_reply() call translates between an RPC reply message and its external representation. ═══ 9.14.49. xdr_array() ═══ The xdr_array() call translates between an array and its external representation. Syntax #include bool_t xdr_array(xdrs, arrp, sizep, maxsize, elsize, elproc) XDR *xdrs; char **arrp; u_int *sizep; u_int maxsize; u_int elsize; xdrproc_t elproc; Parameters xdrs Pointer to an XDR stream arrp Address of the pointer to the array sizep Pointer to the element count of the array maxsize Maximum number of elements accepted elsize Size of each of the array's elements, found using sizeof() elproc XDR routine that translates an individual array element Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_array() call translates between an array and its external representation. Examples struct myarray { int *arrdata; u_int arrlength; }; void xdr_myarray(xdrsp,arrp) XDR *xdrsp; struct myarray *arrp; { xdr_array(xdrsp,(caddr_t *)&arrp->arrdata,&arrp->arrlength, MAXLEN,sizeof(int),xdr_int); } ... static int arrc_in[10],arrc_out[10]; ... u_long procnum; register CLIENT *clnt; enum clnt_stat cs; struct timeval total_timeout; ... total_timeout.tv_sec = 20; total_timeout.tv_usec = 0; ... myarrc_in.arrdata = & arrc_in[0]; myarrc_in.arrlength = ( sizeof(arrc_in) / sizeof (int) ); myarrc_out.arrdata = & arrc_out[0]; myarrc_out.arrlength = ( sizeof(arrc_out) / sizeof (int) ); cs=clnt_call(clnt, procnum, xdr_myarray, (char *) &myarrc_in, xdr_myarray, (char *)&myarrc_out, total_timeout); if ( cs != RPC_SUCCESS) printf("*Error* clnt_call fail :\n"); ... ═══ 9.14.50. xdr_authunix_parms() ═══ The xdr_authunix_parms() call translates between UNIX-based authentication information and its external representation. Syntax #include bool_t xdr_authunix_parms(xdrs, aupp) XDR *xdrs; struct authunix_parms *aupp; Parameters xdrs Pointer to an XDR stream aupp Pointer to the authentication information Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_authunix_parms() call translates between UNIX-based authentication information and its external representation. ═══ 9.14.51. xdr_bool() ═══ The xdr_bool() call translates between a Boolean and its external representation. Syntax #include bool_t xdr_bool(xdrs, bp) XDR *xdrs; bool_t *bp; Parameters xdrs Pointer to an XDR stream bp Pointer to the Boolean Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_bool() call translates between a Boolean and its external representation. ═══ 9.14.52. xdr_bytes() ═══ The xdr_bytes() call translates between byte strings and their external representations. Syntax #include bool_t xdr_bytes(xdrs, sp, sizep, maxsize) XDR *xdrs; char **sp; u_int *sizep; u_int maxsize; Parameters xdrs Pointer to an XDR stream sp Pointer to a pointer to the byte string sizep Pointer to the byte string size maxsize Maximum size of the byte string Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_bytes() call translates between byte strings and their external representations. Examples struct mybytes { char *bytdata; u_int bytlength; }; void xdr_mybytes(xdrsp,arrp) XDR *xdrsp; struct mybytes *arrp; { xdr_bytes(xdrsp,(caddr_t *)&arrp->bytdata,&arrp->bytlength,MAXLEN); } ... char *bytc_in ,*bytc_out; ... u_long procnum; register CLIENT *clnt; enum clnt_stat cs; struct timeval total_timeout; ... total_timeout.tv_sec = 20; total_timeout.tv_usec = 0; ... mybytc_in.bytdata = bytc_in; mybytc_in.bytlength = strlen(bytc_in)+1; cs=clnt_call(clnt, procnum, xdr_mybytes, (caddr_t *) &mybytc_in, xdr_mybytes, (caddr_t *)&mybytc_out, total_timeout); if ( cs != RPC_SUCCESS) printf("*Error* clnt_call fail :\n"); ═══ 9.14.53. xdr_callhdr() ═══ The xdr_callhdr() call translates between an RPC message header and its external representation. Syntax #include void xdr_callhdr(xdrs, chdr) XDR *xdrs; struct rpc_msg *chdr; Parameters xdrs Pointer to the XDR stream chdr Pointer to the call header Description The xdr_callhdr() call translates between an RPC message header and its external representation. ═══ 9.14.54. xdr_callmsg() ═══ The xdr_callmsg() call translates between RPC call messages (header and authentication, not argument data) and their external representations. Syntax #include void xdr_callmsg(xdrs, cmsg) XDR *xdrs; struct rpc_msg *cmsg; Parameters xdrs Pointer to the XDR stream cmsg Pointer to the call message Description The xdr_callmsg() call translates between RPC call messages (header and authentication, not argument data) and their external representations. ═══ 9.14.55. xdr_double() ═══ The xdr_double() call translates between C double-precision numbers and their external representations. Syntax #include bool_t xdr_double(xdrs, dp) XDR *xdrs; double *dp; Parameters xdrs Pointer to the XDR stream dp Pointer to a double-precision number Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_double() call translates between C double-precision numbers and their external representations. ═══ 9.14.56. xdr_enum() ═══ The xdr_enum() call translates between C-enumerated groups and their external representations. Syntax #include bool_t xdr_enum(xdrs, ep) XDR *xdrs; enum_t *ep; Parameters xdrs Pointer to the XDR stream ep Pointer to the enumerated number Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_enum() call translates between C-enumerated groups and their external representations. When you call the procedures callrpc() and registerrpc(), create a stub procedure for both the server and the client before the procedure of the application program using xdr_enum(). Verify that this procedure looks like the following: #include void static xdr_enum_t(xdrs, ep) XDR *xdrs; enum_t *ep; { xdr_enum(xdrs, ep) } The xdr_enum_t procedure is used as the inproc and outproc in both the client and server RPCs. For example, an RPC client would contain the following lines: . . . error = callrpc(argv[1],ENUMRCVPROG,VERSION,ENUMRCVPROC, xdr_enum_t,&innumber,xdr_enum_t,&outnumber); . . . An RPC server would contain the following line: . . . registerrpc(ENUMRCVPROG,VERSION,ENUMRCVPROC,xdr_enum_t, xdr_enum_t); . . . ═══ 9.14.57. xdr_float() ═══ The xdr_float() call translates between C floating-point numbers and their external representations. Syntax #include bool_t xdr_float(xdrs, fp) XDR *xdrs; float *fp; Parameters xdrs Pointer to the XDR stream fp Pointer to the floating-point number Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_float() call translates between C floating-point numbers and their external representations. ═══ 9.14.58. xdr_getpos() ═══ The xdr_getpos() call starts the get-position routine associated with the XDR stream, xdrs. Syntax #include u_int xdr_getpos(xdrs) XDR *xdrs; Parameters xdrs Pointer to the XDR stream Return Values The xdr_getpos() call returns an unsigned integer, which indicates the position of the XDR byte stream. Description The xdr_getpos() call starts the get-position routine associated with the XDR stream, xdrs. Related Calls xdr_setpos ═══ 9.14.59. xdr_inline() ═══ The xdr_inline() call returns a pointer to a continuous piece of the XDR stream's buffer. Syntax #include long * xdr_inline(xdrs, len) XDR *xdrs; int len; Parameters xdrs Pointer to the XDR stream len Length in bytes of the desired buffer Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_inline() call returns a pointer to a continuous piece of the XDR stream's buffer. The value is long * rather than char *, because the external data representation of any object is always an integer multiple of 32 bits. Note: xdr_inline() might return NULL if there is not enough space in the stream buffer to satisfy the request. ═══ 9.14.60. xdr_int() ═══ The xdr_int() call translates between C integers and their external representations. Syntax #include bool_t xdr_int(xdrs, ip) XDR *xdrs; int *ip; Parameters xdrs Pointer to the XDR stream ip Pointer to the integer Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_int() call translates between C integers and their external representations. ═══ 9.14.61. xdr_long() ═══ The xdr_long() call translates between C long integers and their external representations. Syntax #include bool_t xdr_long(xdrs, lp) XDR *xdrs; long *lp; Parameters xdrs Pointer to an XDR stream lp Pointer to the long integer Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_long() call translates between C long integers and their external representations. ═══ 9.14.62. xdr_opaque() ═══ The xdr_opaque() call translates between fixed-size opaque data and its external representation. Syntax #include bool_t xdr_opaque(xdrs, cp, cnt) XDR *xdrs; char *cp; u_int cnt; Parameters xdrs Pointer to an XDR stream cp Pointer to the opaque object cnt Size of the opaque object Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_opaque() call translates between fixed-size opaque data and its external representation. ═══ 9.14.63. xdr_opaque_auth() ═══ The xdr_opaque_auth() call translates between RPC message authentications and their external representations. Syntax #include bool_t xdr_opaque_auth(xdrs, ap) XDR *xdrs; struct opaque_auth *ap; Parameters xdrs Pointer to an XDR stream ap Pointer to the opaque authentication information Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_opaque_auth() call translates between RPC message authentications and their external representations. ═══ 9.14.64. xdr_pmap() ═══ The xdr_pmap() call translates an RPC procedure identification, such as is used in calls to Portmapper. Syntax #include bool_t xdr_pmap(xdrs, regs) XDR *xdrs; struct pmap *regs; Parameters xdrs Pointer to an XDR stream regs Pointer to the PORTMAP parameters Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_pmap() call translates an RPC procedure identification, such as is used in calls to Portmapper. ═══ 9.14.65. xdr_pmaplist() ═══ The xdr_pmaplist() call translates a variable number of RPC procedure identifications, such as those Portmapper creates. Syntax #include bool_t xdr_pmaplist(xdrs, rp) XDR *xdrs; struct pmaplist **rp; Parameters xdrs Pointer to an XDR stream rp Pointer to a pointer to the PORTMAP data array Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_pmaplist() call translates a variable number of RPC procedure identifications, such as those Portmapper creates. ═══ 9.14.66. xdr_reference() ═══ The xdr_reference() call provides pointer chasing within structures. Syntax #include bool_t xdr_reference(xdrs, pp, size, proc) XDR *xdrs; char **pp; u_int size; xdrproc_t proc; Parameters xdrs Pointer to an XDR stream pp Pointer to a pointer size Size of the target proc XDR procedure that translates an individual element of the type addressed by the pointer Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_reference() call provides pointer chasing within structures. ═══ 9.14.67. xdr_rejected_reply() ═══ The xdr_rejected_reply() call translates between rejected RPC reply messages and their external representations. Syntax #include bool_t xdr_rejected_reply(xdrs, rr) XDR *xdrs; struct rejected_reply *rr; Parameters xdrs Pointer to an XDR stream rr Pointer to the rejected reply Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_rejected_reply() call translates between rejected RPC reply messages and their external representations. ═══ 9.14.68. xdr_replymsg() ═══ The xdr_replymsg() call translates between RPC reply messages and their external representations. Syntax #include bool_t xdr_replymsg(xdrs, rmsg) XDR *xdrs; struct rpc_msg *rmsg; Parameters xdrs Pointer to an XDR stream rmsg Pointer to the reply message Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_replymsg() call translates between RPC reply messages and their external representations. ═══ 9.14.69. xdr_setpos() ═══ The xdr_setpos() starts the set-position routine associated with a XDR stream, xdrs. Syntax #include int xdr_setpos(xdrs, pos) XDR *xdrs; u_int pos; Parameters xdrs Pointer to an XDR stream pos Position value obtained from xdr_getpos() Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_setpos() call starts the set-position routine associated with the XDR stream, xdrs. Related Calls xdr_getpos ═══ 9.14.70. xdr_short() ═══ The xdr_short() call translates between C short integers and their external representations. Syntax #include bool_t xdr_short(xdrs, sp) XDR *xdrs; short *sp; Parameters xdrs Pointer to an XDR stream sp Pointer to the short integer Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_short() call translates between C short integers and their external representations. ═══ 9.14.71. xdr_string() ═══ The xdr_string() call translates between C strings and their external representations. Syntax #include bool_t xdr_string(xdrs, sp, maxsize) XDR *xdrs; char **sp; u_int maxsize; Parameters xdrs Pointer to an XDR stream sp Pointer to a pointer to the string maxsize Maximum size of the string Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_string() call translates between C strings and their external representations. ═══ 9.14.72. xdr_u_int() ═══ The xdr_u_int() call translates between C unsigned integers and their external representations. Syntax #include bool_t xdr_u_int(xdrs, up) XDR *xdrs; unsigned *up; Parameters xdrs Pointer to an XDR stream up Pointer to the unsigned integer Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_u_int() call translates between C unsigned integers and their external representations. ═══ 9.14.73. xdr_u_long() ═══ The xdr_u_long() call translates between C unsigned long integers and their external representations. Syntax #include bool_t xdr_u_long(xdrs, ulp) XDR *xdrs; u_long *ulp; Parameters xdrs Pointer to an XDR stream ulp Pointer to the unsigned long integer Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_u_long() call translates between C unsigned long integers and their external representations. ═══ 9.14.74. xdr_u_short() ═══ The xdr_u_short() call translates between C unsigned short integers and their external representations. Syntax #include bool_t xdr_u_short(xdrs, usp) XDR *xdrs; u_short *usp; Parameters xdrs Pointer to an XDR stream usp Pointer to the unsigned short integer Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_u_short() call translates between C unsigned short integers and their external representations. ═══ 9.14.75. xdr_union() ═══ The xdr_union() call translates between a discriminated C union and its external representation. Syntax #include bool_t xdr_union(xdrs, dscmp, unp, choices, dfault) XDR *xdrs; int *dscmp; char *unp; struct xdr_discrim *choices; xdrproc_t dfault; Parameters xdrs Pointer to an XDR stream dscmp Pointer to the union's discriminant unp Pointer to the union choices Pointer to an array detailing the XDR procedure to use on each arm of the union dfault Default XDR procedure to use Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_union() call translates between a discriminated C union and its external representation. ═══ 9.14.76. xdr_vector() ═══ The xdr_vector() call translates between a fixed-length array and its external representation. Syntax #include bool_t xdr_vector(xdrs, basep, nelem, elemsize, xdr_elem) XDR *xdrs; char *basep; u_int nelem; u_int elemsize; xdrproc_t xdr_elem Parameters xdrs Pointer to the XDR stream basep Pointer to the base of the array nelem Element count of the array elemsize Size of each of the array's elements, found by using the sizeof() operator xdr_elem Pointer to the XDR routine that translates an individual array element Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_vector() call translates between a fixed-length array and its external representation. Unlike variable-length arrays, the storage of fixed-length arrays is static and unfreeable. ═══ 9.14.77. xdr_void() ═══ The xdr_void() call returns a value of 1. Syntax #include bool_t xdr_void() Return Values The xdr_void() call always returns a value of 1. Description The xdr_void() call is used like a command that does not require any other XDR functions. You can place this call in the inproc or outproc parameter of the clnt_call() function when you do not need to move data. Related Calls callrpc() clnt_broadcast() clnt_call() clnt_freeres() pmap_rmtcall() registerrpc() svc_freeargs() svc_getargs() svc_sendreply() ═══ 9.14.78. xdr_wrapstring() ═══ The xdr_wrapstring() call translates between strings and their external representations. Syntax #include bool_t xdr_wrapstring(xdrs, sp) XDR *xdrs; char **sp; Parameters xdrs Pointer to an XDR stream sp Pointer to a pointer to the string Return Values The value 1 indicates success; the value 0 indicates an error. Description The xdr_wrapstring() call is the same as calling xdr_string() with a maximum size of MAXUNSIGNED. It is useful because many RPC procedures implicitly start two-parameter XDR routines, and xdr_string() is a three-parameter routine. ═══ 9.14.79. xdrmem_create() ═══ The xdrmem_create() call initializes the XDR stream pointed to by xdrs. Syntax #include void xdrmem_create(xdrs, addr, size, op) XDR *xdrs; char *addr; u_int size; enum xdr_op op; Parameters xdrs Pointer to an XDR stream addr Pointer to the memory location size Maximum size of addr, in multiples of 4 op The direction of the XDR stream (either XDR_ENCODE, XDR_DECODE, or XDR_FREE) Description The xdrmem_create() call initializes the XDR stream pointed to by xdrs. Data is written to, or read from, addr. ═══ 9.14.80. xdrrec_create() ═══ The xdrrec_create() call initializes the XDR stream pointed to by xdrs. Syntax #include void xdrrec_create(xdrs, sendsize, recvsize, handle, readit, writeit) XDR *xdrs; u_int sendsize; u_int recvsize; char *handle; int (*readit)(); int (*writeit)(); Parameters xdrs Pointer to an XDR stream. sendsize Size of the send buffer. Specify 0 to choose the default. recvsize Size of the receive buffer. Specify 0 to choose the default. handle First parameter passed to readit() and writeit(). readit() Called when a stream's input buffer is empty. writeit() Called when a stream's output buffer is full. Description The xdrrec_create() call initializes the XDR stream pointed to by xdrs. Note: The caller must set the op field in the xdrs structure. Warning: This XDR procedure implements an intermediate record string. Additional bytes in the XDR stream provide record boundary information. ═══ 9.14.81. xdrrec_endofrecord() ═══ The xdrrec_endofrecord() call marks the data in the output buffer as a completed record. Syntax #include bool_t xdrrec_endofrecord(xdrs, sendnow) XDR *xdrs; int sendnow; Parameters xdrs Pointer to an XDR stream sendnow Specifies nonzero to write out data in the output buffer Return Values The value 1 indicates success; the value 0 indicates an error. Description You can start the xdrrec_endofrecord() call only on streams created by xdrrec_create(). Data in the output buffer is marked as a complete record. ═══ 9.14.82. xdrrec_eof() ═══ The xdrrec_eof() call marks the end of the file, after using the rest of the current record in the XDR stream. Syntax #include bool_t xdrrec_eof(xdrs) XDR *xdrs; int empty; Parameters xdrs Pointer to an XDR stream Return Values The value 1 indicates the current record has been consumed; the value 0 indicates continued input on the stream. Description You can start the xdrrec_eof() call only on streams created by xdrrec_create(). ═══ 9.14.83. xdrrec_skiprecord() ═══ The xdrrec_skiprecord() call discards the rest of the XDR stream's current record in the input buffer. Syntax #include bool_t xdrrec_skiprecord(xdrs) XDR *xdrs; Parameters xdrs Pointer to an XDR stream Return Values The value 1 indicates success; the value 0 indicates an error. Description You can start the xdrrec_skiprecord() call only on streams created by xdrrec_create(). The XDR implementation is instructed to discard the remaining data in the input buffer. Related Calls xdrrec_create() ═══ 9.14.84. xdrstdio_create() ═══ The xdrstdio_create() call initializes the XDR stream pointed to by xdrs. Syntax #include #include void xdrstdio_create(xdrs, file, op) XDR *xdrs; FILE *file; enum xdr_op op; Parameters xdrs Pointer to an XDR stream file File name for the input and output stream op The direction of the XDR stream (either XDR_ENCODE, XDR_DECODE, or XDR_FREE) Description The xdrstdio_create() call initializes the XDR stream pointed to by xdrs. Data is written to or read from the standard I/O stream or file. ═══ 9.14.85. xprt_register() ═══ The xprt_register() call registers service transport handles with the RPC service package. Syntax #include void xprt_register(xprt) SVCXPRT *xprt; Parameters xprt Pointer to the service transport handle Description The xprt_register() call registers service transport handles with the RPC service package. This routine also modifies the global variable svc_socks[]. Related Calls svc_register() ═══ 9.14.86. xprt_unregister() ═══ The xprt_unregister() call unregisters the RPC service transport handle. Syntax #include void xprt_unregister(xprt) SVCXPRT *xprt; Parameters xprt Pointer to the service transport handle Description The xprt_unregister() call unregisters an RPC service transport handle. A transport handle should be unregistered with the RPC service package before it is destroyed. This routine also modifies the global variable svc_socks[]. ═══ 10. File Transfer Protocol Application Programming Interface ═══ This section describes the FTP API routines supported by TCP/IP Version 3.0 for OS/2 Warp. The File Transfer Protocol (FTP) API allows applications to have a client interface for file transfer. Applications written to this interface can communicate with multiple FTP servers at the same time. The interface supports a maximum of 256 simultaneous connections and enables third-party proxy transfers between pairs of FTP servers. Consecutive third-party transfers are allowed between any sequence of pairs of FTP servers. The FTP API tracks the servers to which an application is currently connected. When a new request for FTP service is requested, the API checks whether a connection to the server exists and establishes one if it does not exist. If the server has dropped the connection since last use, the API re-establishes it. Note: The FTP API is not re-entrant. If you are using a multithreaded program, you must serialize the access to the APIs. For example, without serialization, the program may fail if it has two threads running concurrently and each thread has its own connection to a server. Topics FTP API Call Library Compiling and Linking an FTP API Application Return Values FTP API Calls ═══ 10.1. FTP API Call Library ═══ To use the FTP API described in this section, you must have the header file in your TCPIP\INCLUDE directory. The FTP API routines are in the FTPAPI.LIB file in the LIB directory. You must also have the SO32DLL.LIB and TCP32DLL.LIB files in your LIB directory. Put the following statement at the top of any file using FTP API code: #include Define the OS2 variable to the compiler by doing one of the following:  Place #define OS2 at the top of each file that includes TCP/IP header files.  Use the /DOS2 option when compiling the source for your application. ═══ 10.2. Compiling and Linking an FTP API Application ═══ Follow these steps to compile and link the FTP API application using an IBM 32-bit compiler for OS/2: 1. Set your environment variables to find the following:  Executable programs  Link libraries  Header files You can set the environment variables in your CONFIG.SYS file. The following is an example of the entries you might have in your CONFIG.SYS file: LIBPATH=E:\IBMC\DLL;C:\MPTN\DLL;C:\TCPIP\DLL; SET PATH=E:\IBMC\BIN; SET DPATH=E:\IBMC\LOCALE;E:\IBMC\HELP; SET LIB=E:\IBMC\LIB;C:\MPTN\LIB;C:\TCPIP\LIB; SET INCLUDE=E:\IBMC\INCLUDE;C:\TCPIP\INCLUDE; SET HELP=E:\IBMC\HELP; SET BOOKSHELF=E:\IBMC\HELP; SET TMP=E:\IBMC\TMP SET TZ=EST5EDT,0,0,0,0,0,0,0,0,0 2. To compile your program, enter: icc /Ti /DOS2 /Sm /Ss /Q /Gt /C myprog.c 3. To create an executable program, you can enter: For VisualAge C++ ilink /NOFREEFORMAT /De myprog,myprog.exe,NULL,so32dll.lib tcp32dll.lib ftpapi.lib For C Set++ link386 /De myprog,myprog.exe,NULL,so32dll.lib tcp32dll.lib ftpapi.lib Note: 1. Programs that recognize long file names must indicate this by including the NEWFILES statement in their module definition file. 2. The FTP API is not re-entrant. If you are using a multithreaded program, you must serialize the access to the APIs. 3. Before you run a program, verify that the DLL files are in a directory listed in the LIBPATH system environment variable. 4. For more information about the compile and link options, and dynamic link libraries, see the User's Guide provided with your compiler. ═══ 10.3. Return Values ═══ Most functions return a value of -1 to indicate failure and a value of 0 to indicate success. Two functions do not return 0 and -1 values: ftplogoff(), which is of type void, and ping(), which returns an error code rather than storing the return value in ftperrno. When the value is -1, the global integer variable ftperrno is set to one of the following codes: Return Code Description FTPSERVICE Unknown service. FTPHOST Unknown host. FTPSOCKET Unable to obtain socket. FTPCONNECT Unable to connect to server. FTPLOGIN Login failed. FTPABORT Transfer aborted. FTPLOCALFILE Problem opening the local file. FTPDATACONN Problem initializing data connection. FTPCOMMAND Command failed. FTPPROXYTHIRD Proxy server does not support third party. FTPNOPRIMARY No primary connection for proxy transfer. ═══ 10.4. FTP API Calls ═══ This section provides the syntax, parameters, and other appropriate information for each FTP API call supported by TCP/IP Version 3.0 for OS/2 Warp. Topics ftpappend() ftpcd() ftpdelete() ftpdir() ftpget() ftplogoff() ftpls() ftpmkd() ftpping() ftpproxy() ftpput() ftpputunique() ftppwd() ftpquote() ftprename() ftprmd() ftpsite() ftpsys() ftptrcoff() ftptrcon() ftpver() ping() ═══ 10.4.1. ftpappend() ═══ The ftpappend() call appends information to a remote file. Syntax #include int ftpappend(host, userid, passwd, acct, local, remote, transfertype) char *host; char *userid; char *passwd; char *acct; char *local; char *remote; int transfertype; Parameters host Host running the FTP server. userid ID used for logon. passwd Password of the user ID. acct Account (when needed); can be NULL. local Local file name. remote Remote file name. transfertype Specifies a binary or ASCII transfer. T_ASCII is for ASCII, T_BINARY is for binary. Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpappend() call appends information to a remote file. Examples int rc; rc=ftpappend("conypc","jason","ehgr1",NULL,"abc.doc","new.doc",T_ASCII); The local ASCII file abc.doc is appended to the file new.doc in the current working directory at the host conypc. ═══ 10.4.2. ftpcd() ═══ The ftpcd() call changes the current working directory on a host. Syntax #include int ftpcd(host, userid, passwd, acct, dir) char *host; char *userid; char *passwd; char *acct, char *dir; Parameters host Host running the FTP server userid ID used for logon passwd Password of the user ID acct Account (when needed); can be NULL dir New working directory Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpcd() call changes the current working directory. Examples int rc; rc=ftpcd("conypc","jason","ehgr1",NULL,"mydir"); The current working directory is changed to mydir on the host conypc using the user ID jason and the password ehgr1. ═══ 10.4.3. ftpdelete() ═══ The ftpdelete() call deletes files on a remote host. Syntax #include int ftpdelete(host, userid, passwd, acct, name) char *host; char *userid; char *passwd; char *acct; char *name; Parameters host Host running the FTP server userid ID used for logon passwd Password of the user ID acct Account (when needed); can be NULL name File to be deleted Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpdelete() call deletes a file on a host. Examples int rc; rc=ftpdelete("conypc","jason","ehgr1",NULL,"abc.1"); The file abc.1 is deleted on the host conypc using the user ID jason and the password ehgr1. ═══ 10.4.4. ftpdir() ═══ The ftpdir() call gets a directory in wide format from a host. Syntax #include int ftpdir(host, userid, passwd, acct, local, pattern,) char *host; char *userid; char *passwd; char *acct; char *local; char *pattern; Parameters host Host running the FTP server. userid ID used for logon. passwd Password of the user ID. acct Account (when needed); can be NULL. local Local file name. pattern The file name or pattern of the files to be listed on the foreign host. Patterns are any combination of ASCII characters. The following two characters have special meaning: * Shows that any character or group of characters can occupy that position in the pattern. ? Shows that any single character can occupy that position in the pattern. Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpdir() call gets a directory in wide format from a host. Examples int rc; rc=ftpdir("conypc","jason","ehgr1",NULL,"conypc.dir","*.c"); ftpdir() gets a directory of *.c files in wide format, and stores the directory in a local file, conypc.dir. ═══ 10.4.5. ftpget() ═══ The ftpget() call gets a file from an FTP server. Syntax #include int ftpget(host, userid, passwd, acct, local, remote, mode, transfertype) char *host; char *userid; char *passwd; char *acct; char *local; char *remote; char *mode; int transfertype; Parameters host Host running the FTP server. userid ID used for logon. passwd Password of the user ID. acct Account (when needed); can be NULL. local Local file name. remote Remote file name. mode Either w for write or a for append. transfertype Specifies a binary or ASCII transfer. T_ASCII is for ASCII, T_BINARY is for binary. Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpget() call gets a file from an FTP server. Examples int rc; rc=ftpget("conypc","jason","ehgr1",NULL,"new.doc","abc.doc","w",T_ASCII); The system copies the ASCII file abc.doc on the host conypc into the local current working directory as the file new.doc. If the file new.doc already exists in the local current working directory, the contents of the file abc.doc overwrite the file new.doc. ═══ 10.4.6. ftplogoff() ═══ The ftplogoff() call closes all current connections. Syntax #include void ftplogoff() Description The ftplogoff() call closes all current connections. An application must call this before terminating. ═══ 10.4.7. ftpls() ═══ The ftpls() call gets directory information in short format from a remote host and writes it to a local file. Syntax #include int ftpls(host, userid, passwd, acct, local, pattern) char *host; char *userid; char *passwd; char *acct; char *local; char *pattern; Parameters host Host running the FTP server. userid ID used for logon. passwd Password of the user ID. acct Account (when needed); can be NULL. local Local file into which the information is placed. pattern The file name or pattern of the files to be listed on the foreign host. Patterns are any combination of ASCII characters. The following two characters have special meaning: * Shows that any character or group of characters can occupy that position in the pattern. ? Shows that any single character can occupy that position in the pattern. Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpls() call gets directory information in short format from a host and writes it to a local file. Examples int rc; rc=ftpls("conypc","jason","ehgr1",NULL,"conypc.dir","*.c"); ftpls() gets a directory of *.c files in short format and stores the names in the local file conypc.dir. ═══ 10.4.8. ftpmkd() ═══ The ftpmkd() call creates a new directory on a target machine. Syntax #include int ftpmkd(host, userid, passwd, acct, dir) char *host; char *userid; char *passwd; char *acct; char *dir; Parameters host Host running the FTP server userid ID used for logon passwd Password of the user ID acct Account (when needed); can be NULL dir Directory to be created Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpmkd() call creates a new directory on a host. Examples int rc; rc=ftpmkd("conypc","jason","ehgr1",NULL,"mydir"); The directory mydir is created on the host conypc, using the user ID jason and the password ehgr1. ═══ 10.4.9. ftpping() ═══ The ftpping() call resolves a host name and sends a ping to the remote host to determine if that host is responding. Syntax #include int ftpping(host, len, addr) char *host; int len; unsigned long *addr; Parameters host Host running the FTP server len Length of the ping packets addr Buffer in which to return the host internet address Return Values The following are ftpping() call return codes and their corresponding descriptions: Return Code Description PINGREPLY Host does not reply PINGSOCKET Unable to obtain socket PINGPROTO Unknown protocol ICMP PINGSEND Send failed PINGRECV Recv failed PINGHOST Unknown host Description The ftpping() call tries to resolve the host name through a name server. If the name server is not present, ftpping() searches the TCPIP\ETC\HOSTS file for a matching host name. Unlike the ping() call, ftpping() could take several seconds because it must resolve the host name before it sends a ping. For this reason, use ftpping() only in the first try to determine if the host is responding. The ftpping() call sets the addr parameter to the internet address of the host. After the first try, use this address value to call ping. If the ftpping() return value is positive, the return value is the number of milliseconds it took for the echo to return. If the return value is negative, it contains an error code. The parameter len specifies the length of the ping packet(s). Examples int rc; unsigned long addr; rc = ftpping("conypc", 256, &addr); The ftpping() call sends a 256-byte ping packet to the host conypc. ═══ 10.4.10. ftpproxy() ═══ The ftpproxy() call transfers a file between two remote servers without sending the file to the local host. Syntax #include int ftpproxy(host1, userid1, passwd1, acct1, host2, userid2, passwd2, acct2, fn1, fn2, transfertype) char *host1; char *userid1; char *passwd1; char *acct1; char *host2; char *userid2; char *passwd2; char *acct2; char *fn1; char *fn2; int transfertype; Parameters host1 Target host running the FTP server. userid1 ID used for logon on host 1. passwd1 Password of the user ID on host 1. acct1 Account for host 1 (when needed); can be NULL. host2 Source host running the FTP server. userid2 ID used for logon on host 2. passwd2 Password of the user ID on host 2. acct2 Account for host 2 (when needed); can be NULL. fn1 File to be written on host 1. fn2 File to be copied from host 2. transfertype Specifies a binary or ASCII transfer. T_ASCII is for ASCII, T_BINARY is for binary. Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpproxy() call copies a file on a specified source host directly to a specified target host, without involving the requesting host in the file transfer. This call is functionally the same as the FTP client subcommand proxy put. Note: For ftpproxy() to complete successfully, both the source and the target hosts must be running the FTP servers. In addition, ftpproxy() does not support connections through a firewall. Examples int rc; rc=ftpproxy("pc1","oleg","erst",NULL, /* target host information*/ "pc2","yan", "dssa1", NULL, /* source host information*/ "\tmp\newdoc.1", /* target file name */ "\tmp\doc.1", /* source file name */ T_ASCII); /* ascii transfer */ The ASCII file \tmp\doc.1 on the host pc2 is copied to host pc1 as the file \tmp\newdoc.1. ═══ 10.4.11. ftpput() ═══ The ftpput() call transfers a file to a host. Syntax #include int ftpput(host, userid, passwd, acct, local, remote, transfertype) char *host; char *userid; char *passwd; char *acct; char *local; char *remote; int transfertype; Parameters host Host running the FTP server. userid ID used for logon. passwd Password of the user ID. acct Account (when needed); can be NULL. local Local file name. remote Remote file name. transfertype Specifies a binary or ASCII transfer. T_ASCII is for ASCII, T_BINARY is for binary. Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpput() call transfers a file to an FTP server. Examples int rc; rc=ftpput("conypc","jason","ehgr1",NULL,"abc.doc","new.doc",T_ASCII); The system copies the ASCII file abc.doc on the local current working directory to the current working directory of the host conypc as file new.doc. If the file new.doc already exists, the contents of the file abc.doc overwrite the file new.doc. ═══ 10.4.12. ftpputunique() ═══ The ftpputunique() call transfers a file to a host and ensures it is created with a unique name. Syntax #include int ftpputunique(host, userid, passwd, acct, local, remote, transfertype) char *host; char *userid; char *passwd; char *acct; char *local; char *remote; int transfertype; Parameters host Host running the FTP server. userid ID used for logon. passwd Password of the user ID. acct Account (when needed); can be NULL. local Local file name. remote Remote file name. transfertype Specifies a binary or ASCII transfer. T_ASCII is for ASCII, T_BINARY is for binary. Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpputunique() call copies a local file to a file on a specified host. It guarantees that the new file will have a unique name and that the new file will not overwrite a file with the same name. If the file already exists on the host, a new and unique file name is created and used as the target of the file transfer. Examples int rc; rc=ftpputunique( "conypc","jason","ehgr1",NULL,"abc.doc", "new.doc",T_ASCII); The ASCII file abc.doc is copied to the current working directory of the host conypc as file new.doc, unless the file new.doc already exists. If the file new.doc already exists, the file new.doc is given a new name unique within the current working directory on the host conypc. The name of the new file is displayed upon successful completion of the file transfer. ═══ 10.4.13. ftppwd() ═══ The ftppwd() call stores the string containing the FTP server description of the current working directory on the host to the buffer. Syntax #include int ftppwd(host, userid, passwd, acct, buf, buflen) char *host; char *userid; char *passwd; char *acct, char *buf; crt *buflen; Parameters host Host running the FTP server userid ID used for logon passwd Password of the user ID acct Account (when needed); can be NULL buf Buffer to store the string returned by the FTP server buflen Length of buf Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftppwd() call stores the string containing the FTP server description of the current working directory on the host to the buffer buf. The string describing the current working directory is truncated to fit buf if it is longer than buflen. The returned string is always null-terminated. Examples int rc; rc=ftppwd("conypc","jason","ehgr1","dirbuf", sizeof dirbuf); After the ftppwd() call, the buffer dirbuf contains the following: "C:\" is current directory. The server reply describing the current working directory on host conypc using user ID jason with password eghr1 is stored to dirbuf. ═══ 10.4.14. ftpquote() ═══ The ftpquote() call sends a string to the server verbatim. Syntax #include int ftpquote(host, userid, passwd, acct, quotestr) char *host; char *userid; char *passwd; char *acct; char *quotestr; Parameters host Host running the FTP server userid ID used for logon passwd Password of the user ID acct Account (when needed); can be NULL quotestr Quote string to be passed to the FTP server verbatim Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpquote() call sends a string to the server verbatim. Examples int rc; rc=ftpquote("conypc","jason","ehgr1",NULL,"site idle 2000"); The idle is set to time out in 2000 seconds. Your server might not support that amount of idle time. ═══ 10.4.15. ftprename() ═══ The ftprename() call renames a file on a remote host. Syntax #include int ftprename(host, userid, passwd, acct, namefrom, nameto) char *host; char *userid; char *passwd; char *acct; char *namefrom; char *nameto; Parameters host Host running the FTP server userid ID used for logon passwd Password of the user ID acct Account (when needed); can be NULL namefrom Original file name nameto New file name Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftprename() call renames a file on a host. Examples int rc; rc=ftprename("conypc","jason","ehgr1",NULL,"abc.1","cd.fg"); The file abc.1 is renamed to cd.fg on host conypc, using user ID jason, with password ehgr1. ═══ 10.4.16. ftprmd() ═══ The ftprmd() call removes a directory on a target machine. Syntax #include int ftprmd(host, userid, passwd, acct, dir) char *host; char *userid; char *passwd; char *acct; char *dir; Parameters host Host running the FTP server userid ID used for logon passwd Password of the user ID acct Account (when needed); can be NULL dir Directory to be removed Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftprmd() call removes a directory on a host. Examples int rc; rc=ftprmd("conypc","jason","ehgr1",NULL,"mydir"); The directory, mydir, is removed on the host, conypc, using the user ID, jason, and the password, ehgr1. ═══ 10.4.17. ftpsite() ═══ The ftpsite() call executes the site command. (For more information about the site command, see the TCP/IP Command Reference.) Syntax #include int ftpsite(host, userid, passwd, acct, sitestr) char *host; char *userid; char *passwd; char *acct; char *sitestr; Parameters host Host running the FTP server userid ID used for logon passwd Password of the user ID acct Account (when needed); can be NULL sitestr Site string to be executed Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpsite() call executes the site command. Note: ftpsite() does not support connections through a firewall. Examples int rc; rc=ftpsite("conypc","jason","ehgr1",NULL,"idle 2000"); The idle is set to time out in 2000 seconds. Your server might not support that amount of idle time. ═══ 10.4.18. ftpsys() ═══ The ftpsys() call stores the string containing the FTP server description of the operating system running on the host in a buffer. Syntax #include int ftpsys(host, userid, passwd, acct, buf, buflen) char *host; char *userid; char *passwd; char *acct, char *buf; int *buflen; Parameters host Host running the FTP server userid ID used for logon passwd Password of the user ID acct Account (when needed); can be NULL buf Buffer to store the string returned by the FTP server buflen Length of buf Return Values The value 0 indicates success; the value -1 indicates an error. The value of ftperrno indicates the specific error. See Return Values for a description of the return codes. Description The ftpsys() call stores the string containing the FTP server description of the operating system running on the host in the buffer buf. The string describing the operating system of the host is truncated to fit buf if it is longer than buflen. The returned string is always null-terminated. Examples int rc; rc=ftpsys("ralvmm","jason","ehgr1",hostsysbuf, sizeof hostsysbuf); After the ftpsys() call the buffer hostsysbuf contains the following: VM is the operating system of this server. The FTP server reply describing the operating system of host ralvmm using user ID jason with password eghr1 is stored to hostsysbuf. ═══ 10.4.19. ftptrcoff() ═══ The ftptrcoff() closes the trace file and stops tracing. Syntax #include int ftptrcoff(void) Parameters None. Return Values The ftptrcoff() always return a value of 0. Description The ftptrcoff() closes the trace file and stops tracing of the command and reply sequences sent over the control connection between the local and remote hosts. Examples int rc; rc = ftptrcoff(); ═══ 10.4.20. ftptrcon() ═══ The ftptrcon() call opens the trace file specified and starts tracing. Syntax #include int ftptrcon(fileSpec, mode) char *fileSpec; int mode; Parameters fileSpec Identifies the name of the trace file. mode Specifies the trace mode as overwrite or append. Use M_OVERLAY for trace data which overwrites previous information. Use M_APPEND for trace data which appends to previous information. Return Values There are three possible return values for ftptrcon(): 0 when successful TRCMODE indicates the value set into mode was invalid TRCOPEN indicates the trace file could not be opened Description The ftptrcon() call opens the trace file specified and starts tracing of the command and reply sequences sent over the control connection between the local and remote hosts. The trace file can be written over or appended to. No notification is provided if writing of trace data fails. Telnet command and reply sequences are not traced nor are command and reply sequences between the local host and a proxy host. Examples To write the trace data into a file named api.trc in the C:\WORK directory, use : int rc; rc = ftptrcon("c\\work\\api.trc", M_OVERLAY); If the file already existed, the new trace data overwrites the previous trace data (overlay mode). ═══ 10.4.21. ftpver() ═══ The ftpver() call stores the string containing the FTP API version. Syntax #include int ftpver(buf, buflen) char *buf; int buflen; Parameters buf Identifies the buffer to store the version string. buflen Specifies the length of the buffer. Return Values The value of 0 when successful. The value of -1 when the complete version string could not be copied because the buffer length was too small. Description The ftpver() call stores the string containing the FTP API version. The string is truncated to fit into the buffer if it is longer than the buffer length. The returned string is always null-terminated. Examples int rc; rc = ftpver(verBuf, bufLen); After the ftpver() call, the buffer contains the version number. ═══ 10.4.22. ping() ═══ The ping() call sends a ping to the remote host to determine if that host is responding. Syntax #include int ping(addr, len) unsigned long addr; int len; Parameters addr Internet address of the host in network byte order len Length of the ping packets Return Values If the return value is positive, the return value is the number of milliseconds it took for the echo to return. If the return value is negative, it contains an error code. The following are ping() call return codes and their corresponding descriptions: Return Code Description PINGREPLY Host does not reply PINGSOCKET Unable to obtain socket PINGPROTO Unknown protocol ICMP PINGSEND Send failed PINGRECV Recv failed Description The ping() call sends a ping to the host with ICMP Echo Request. The ping() call is useful to determine whether the host is alive before attempting FTP transfers, because time-out on regular connections is more than a minute. The ping() call returns within 3 seconds, at most, if the host is not responding. Examples #include #include #include struct hostent *hp; /* Pointer to host info */ main(int argc, char *argv[], char *envp[]) { int i; unsigned long addr; if (argc!=2) { printf("Usage: p \n"); exit(1); } hp = gethostbyname(argv[1]); if (hp) { memcpy( (char *)&addr, hp->h_addr, hp->h_length); i = ping(addr,256); printf("ping reply in %d milliseconds\n",i); } else { printf("unknown host\n"); exit(2); } ftplogoff(); /* close all connections */ } ═══ 11. SNMP Agent Distributed Protocol Interface (DPI) ═══ This section describes the Simple Network Management Protocol (SNMP) agent distributed protocol interface (DPI). The SNMP DPI permits users to dynamically add, delete, or replace management variables in the local Management Information Base (MIB) without requiring you to recompile the SNMP agent. Topics Introduction to SNMP Distributed Protocol Interface Subagent Programming Concepts Basic DPI API Functions Transport-Related DPI API Functions DPI Structures Character Set Selection Constants, Values, Return Codes, and Include File SNMP DPI API Version 1.1 Considerations Migrating Your SNMP DPI Subagent to Version 2.0 A DPI Subagent Example ═══ 11.1. Introduction to SNMP Distributed Protocol Interface ═══ This section describes the SNMP DPI routines supported by TCP/IP for OS/2. This Application Programming Interface (API) is for the DPI subagent programmer. The reader may also want to obtain a copy of the relevant RFCs.  RFC1592 is the SNMP DPI 2.0 RFC.  RFC1440 through RFC1452 are the SNMP Version 2 RFCs. Topics SNMP Agents and Subagents DPI Agent Requests Multiple Levels of the SNMP DPI API SNMP DPI API Source Files DPI Library Compiling, Linking, and Running a DPI API Application Functions, Data Structures, and Constants ═══ 11.1.1. SNMP Agents and Subagents ═══ SNMP agents are responsible for answering SNMP requests from network management stations. Examples of management requests are GET, GETNEXT, and SET, performed on the MIB objects. A subagent extends the set of MIB objects provided by the SNMP agent. With the subagent, you define MIB variables useful in your own environment and register them with the SNMP agent. When the agent receives a request for a MIB variable, it passes the request to the subagent. The subagent then returns a response to the agent. The agent creates an SNMP response packet and sends the response to the remote network management station that initiated the request. The existence of the subagent is transparent to the network management station. To allow the subagents to perform these functions, the agent provides for two types of subagent connections: a  TCP connection  Connection via Shared Memory (SHM) For the TCP connections, the agent binds to an arbitrarily chosen TCP port and listens for connection requests. A well-known port is not used. Every invocation of the SNMP agent could potentially use a different TCP port. A subagent of the SNMP agent determines the port number by sending a GET request for an MIB variable, which represents the value of the TCP port. The subagent is not required to create and parse SNMP packets because the DPI API has a library routine query_DPI_port(). After the subagent obtains the value of the DPI TCP port, it should make a TCP connection to the appropriate port. After a successful connect(), the subagent registers the set of variables it supports with the SNMP agent. When all variable classes are registered, the subagent waits for requests from the SNMP agent. The query_DPI_port() function is implicitly executed by the DPIconnect_to_agent_TCP() function. The DPI subagent programmer would normally use the DPIconnect_to_agent_TCP() function to connect to the agent, so it does not need to obtain the value of the DPI TCP port. For a SHM connection, the subagent can use the DPIconnect_to_agent_SHM() function. ═══ 11.1.2. DPI Agent Requests ═══ The SNMP agent can initiate several DPI requests:  GET  GETNEXT  GETBULK (SNMP Version 2)  SET, COMMIT, and UNDO  UNREGISTER  CLOSE The GET, GETNEXT, GETBULK, and SET requests correspond to the SNMP requests that a network management station can make. The subagent responds to a request with a response packet. The response packet can be created using the mkDPIresponse() library routine, which is part of the DPI API library. The GETBULK requests are translated into multiple GETNEXT requests by the agent. According to RFC 1592, a subagent may request that the GETBULK be passed to it, but the OS/2 version of DPI does not yet support that request. The COMMIT, UNDO, UNREGISTER, and CLOSE are specific SNMP DPI requests. The subagent normally responds to a request with a RESPONSE packet. For the CLOSE and UNREGISTER request, the subagent does not need to send a RESPONSE. Related Information Overview of Subagent Processing Connecting to the Agent Registering a Sub-Tree with the Agent Processing Requests from the Agent Processing a GET Request Processing a GETNEXT Request Processing a SET/COMMIT/UNDO Request Processing an UNREGISTER Request Processing a CLOSE Request Generating a TRAP ═══ 11.1.3. Multiple Levels of the SNMP DPI API ═══ For the SNMP DPI 2.0 API, some functions are implemented as macros, because the older DPI Version 1.x had the same function names with different parameters. The new implementation has new function names, which are not always the most intuitive. By defining the macros with the more natural names for the functions, the non-intuitive names are hidden. This was done because the macros have the same names as the functions were named in DPI Version 1. It is thus possible to provide either the DPI 1.x or the DPI 2.x API by properly defining the macros. ═══ 11.1.3.1. SNMP DPI API Version 2.0 ═══ By default, when you include the snmp_dpi.h include file, you will be exposed to the DPI 2.0 API. For a list of the functions provided, see The snmp_dpi.h Include File. This is the recommended use of the SNMP DPI API. When you link-edit your object code into an executable file, you must use the new DPI functions as provided in the DPI20DLL.LIB and DPI20DLL.DLL files. Waiting for a DPI packet also depends on the platform and how the chosen transport protocol is exactly implemented. In addition, some subagents want to control sending of and waiting for packets themselves, because they may need to be driven by other interrupts as well. There is a set of DPI transport-related functions that are implemented on all platforms to hide the platform dependent issues for those subagents that do not need detailed control about the transport themselves. ═══ 11.1.3.2. Compatibility with DPI 1.x Base Code ═══ If you have DPI 1.x based code, you may choose to keep using the old DPI 1.x dpi\snmp_dpi.h include file and the old DPI 1.x functions as provided in DPI32DLL.LIB and DPI32DLL.DLL. You will be able to communicate with the OS/2 SNMP agent which implements DPI 2.0. In Migrating Your DPI Subagent to DPI 2.0, the changes that you must make to your DPI 1.x source are presented. If you take a few minutes to look at it, you will see that this is not too big a task. ═══ 11.1.4. SNMP DPI API Source Files ═══ The following source files are provided: snmp_dpi.h The public SNMP DPI 2.0 API as provided to the DPI subagent programmer. The DPI subagent code must include this file. dpi_samp.c A very basic example of an SNMP DPI 2.0 subagent implementation, dpiSimp.mib. dpiSimp.mib The dpiSimple MIB that goes with the dpi_samp.c source. The DPI subagent programmer can use the snmp_dpi.h include file and the dpi_samp.c file as an example of using the DPI API. ═══ 11.1.5. DPI Library ═══ To use the DPI library routines provided with TCP/IP for OS/2, you must have the header file in your TCPIP\INCLUDE directory. The DPI20DLL.LIB file in the LIB directory contains the DPI library routines. You must also have the SO32DLL.LIB and TCP32DLL.LIB files in your LIB directory. You must define the OS2 variable to the compiler by doing one of the following:  Place #define OS2 at the top of each file that includes TCP/IP header files.  Use the /DOS2 option when compiling the source for your application. See the TCPIP\SAMPLES\DPI20 directory for SNMP DPI sample programs. For more information about SNMP, see the IBM TCP/IP Version 2.0 for OS/2: Installation and Administration information or the IBM TCP/IP Version 2.0 for OS/2: User's Guide. ═══ 11.1.6. Compiling, Linking, and Running a DPI API Application ═══ The compiling and linking procedure for the DPI API using an IBM 32-bit compiler for OS/2 follows: 1. Set your environment variables to find the following:  Executable programs  Link libraries  Header files You can set the environment variables in your CONFIG.SYS file. An example of entries you could have in your CONFIG.SYS file follows: LIBPATH=E:\IBMCPP\DLL;C:\MPTN\DLL;C:\TCPIP\DLL; SET PATH=E:\IBMCPP\BIN; SET DPATH=E:\IBMCPP\LOCALE;E:\IBMCPP\HELP; SET LIB=E:\IBMCPP\LIB;C:\MPTN\LIB;C:\TCPIP\LIB; SET INCLUDE=E:\IBMCPP\INCLUDE;C:\TCPIP\INCLUDE; SET HELP=E:\IBMCPP\HELP; SET BOOKSHELF=E:\IBMCPP\HELP; SET TMP=E:\IBMCPP\TMP SET TZ=EST5EDT,0,0,0,0,0,0,0,0,0 2. To compile the program, enter: icc /Sa /j- /Gm /C myprog.c 3. To create an executable program, you can enter: For VisualAge C++ ilink /NOFREEFORMAT /De myprog,myprog.exe,NULL,so32dll.lib tcp32dll.lib os2386 dpi20dll.lib For C Set++ link386 /De myprog,myprog.exe,NULL,so32dll.lib tcp32dll.lib os2386 dpi20dll.lib 4. When you are ready to run your subagent, make sure that the DPI20DLL.DLL is in a directory listed in your LIBPATH statement. LIBPATH=E:\IBMCPP\DLL;C:\MPTN\DLL;C:\TCPIP\DLL; For more information about the compile and link options, see the User's Guide provided with your compiler. ═══ 11.1.7. Functions, Data Structures, and Constants ═══ Use these lists to locate the descriptions for the functions, data structures, and constants. Basic DPI Functions: The DPIdebug() Function The DPI_PACKET_LEN() macro The fDPIparse() Function The fDPIset() Function The mkDPIAreYouThere() Function The mkDPIclose() Function The mkDPIopen() Function The mkDPIregister() Function The mkDPIresponse() Function The mkDPIset() Function The mkDPItrap() Function The mkDPIunregister() Function The pDPIpacket() Function DPI Transport-Related Functions: The DPIawait_packet_from_agent() Function The DPIconnect_to_agent_SHM() Function The DPIconnect_to_agent_TCP() Function The DPIdisconnect_from_agent() Function The DPIget_fd_for_handle() Function The DPIsend_packet_to_agent() Function The lookup_host() Function The query_DPI_port() Function Data Structures: The snmp_dpi_bulk_packet structure The snmp_dpi_close_packet structure The snmp_dpi_get_packet structure The snmp_dpi_next_packet structure The snmp_dpi_hdr structure The snmp_dpi_resp_packet structure The snmp_dpi_set_packet structure The snmp_dpi_ureg_packet structure The snmp_dpi_u64 structure Constants and Values: DPI CLOSE Reason Codes DPI Packet Types DPI RESPONSE Error Codes DPI UNREGISTER Reason Codes DPI SNMP Value Types Value Representation Related Information: Character Set Selection The snmp_dpi.h Include File ═══ 11.2. Subagent Programming Concepts ═══ This section contains conceptual information about subagent programming. Topics Programming Recommendations DPI API GET Processing SET Processing GETNEXT Processing GETBULK Processing OPEN Request CLOSE Request REGISTER Request UNREGISTER Request TRAP Request ARE_YOU_THERE Request Multithreading Programming Considerations ═══ 11.2.1. Programming Recommendations ═══ When implementing a subagent, it is recommended that you use the DPI Version 2 approach. This includes:  Use the SNMP Version 2 error codes only, even though there are definitions for the SNMP Version 1 error codes.  Implement the SET, COMMIT, UNDO processing properly.  For GET requests, use the SNMP Version 2 approach and pass back noSuchInstance value or noSuchObject value if appropriate. Continue to process all remaining varBinds.  For GETNEXT, use the SNMP Version 2 approach and pass back endOfMibView value if appropriate. Continue to process all remaining varBinds.  When you are processing a request from the agent (GET, GETNEXT, GETBULK, SET, COMMIT, or UNDO), you are supposed to respond within the timeout period. You can specify the timeout period in the OPEN and REGISTER packets. If you fail to respond within the timeout period, the agent will probably close your DPI connection and then discard your RESPONSE packet if it comes in later. If you can detect that the response is not going to be received in the time period, then you might decide to stop the request and return an SNMP_ERROR_genErr in the RESPONSE.  You may want to issue an SNMP DPI ARE_YOU_THERE request periodically to ensure that the agent is still "connected" and still knows about you.  For OS/2, you use an ASCII based machine. However, when you are running on an EBCDIC based machine and you use the (default) native character set, then all OID strings and all variable values of type OBJECT_IDENTIFIER or DisplayString will be passed to you in EBCDIC format. OID strings include the group ID, instance ID, Enterprise ID, and subagent ID. When you return a response, you should then also use EBCDIC FORMAT.  For OS/2, you use an ASCII based machine. However, when you are running on an EBCDIC based machine and you use the ASCII character set (specified in DPI OPEN), then all OID strings and all variable values of type OBJECT_IDENTIFIER or DisplayString will be passed to you in ASCII format. OID strings include the group ID, instance ID, Enterprise ID, and subagent ID. When you return a response, you should then also use ASCII FORMAT.  If you receive an error RESPONSE on the OPEN packet, you will also receive a DPI CLOSE packet with an SNMP_CLOSE_openError code. In this situation, the agent closes the "connection". For OS/2, you use an ASCII based machine. However, when you connect to an EBCDIC based agent, you may want to specify in the DPI OPEN packet that you want to use ASCII character set on the agent. This is transparent to you and the burden of conversion is on the EBCDIC based agent.  The DisplayString is only a textual convention. In the SNMP PDU (SNMP packet), the type is just an OCTET_STRING. When the type is OCTET_STRING, it is not clear if this is a DisplayString or any arbitrary data. This means that the agent can only know about an object being a DisplayString if the object is included in some sort of a compiled MIB. If it is, the agent will use SNMP_TYPE_DisplayString in the type field of the varBind in a DPI SET packet. When you send a DisplayString in a RESPONSE packet, the agent will handle it as such. Related Information A DPI Subagent Example ═══ 11.2.2. DPI API ═══ The primary goal of RFC 1592 is to specify the SNMP DPI. This is a protocol by which subagents can exchange SNMP related information with an agent. On top of this protocol, one can imagine one or possibly many Application Programming Interfaces, but those are not addressed in RFC 1592. In order to provide an environment that is generally platform independent, RFC 1592 strongly suggests that you also define a DPI API. There is a sample DPI API available in the RFC. The document describes the same sample API as the IBM supported DPI Version 2.0 API, see A DPI Subagent Example. ═══ 11.2.3. GET Processing ═══ The DPI GET packet holds one or more varBinds that the subagent has taken responsibility for. If the subagent encounters an error while processing the request, it creates a DPI RESPONSE packet with an appropriate error indication in the error_code field and sets the error_index to the position of the varBind at which the error occurs. The first varBind is index 1, the second varBind is index 2, and so on. No name, type, length, or value information needs to be provided in the packet because, by definition, the varBind information is the same as in the request to which this is a response and the agent still has that information. If there are no errors, the subagent creates a DPI RESPONSE packet in which the error_code is set to SNMP_ERROR_noError (zero) and error_index is set to zero. The packet must also include the name, type, length, and value of each varBind requested. When you get a request for a non-existing object or a non-existing instance of an object, you must return a NULL value with a type of SNMP_TYPE_noSuchObject or SNMP_TYPE_noSuchInstance respectively. These two values are not considered errors, so the error_code and error_index should be zero. The DPI RESPONSE packet is then sent back to the agent. Related Information Processing a GET Request The mkDPIresponse() Function ═══ 11.2.4. SET Processing ═══ A DPI SET packet contains the name, type, length, and value of each varBind requested, plus the value type, value length, and value to be set. If the subagent encounters an error while processing the request, it creates a DPI RESPONSE packet with an appropriate error indication in the error_code field and an error_index listing the position of the varBind at which the error occurs. The first varBind is index 1, the second varBind is index 2, and so on. No name, type, length, or value information needs to provided in the packet because, by definition, the varBind information is the same as in the request to which this is a response and the agent still has that information. If there are no errors, the subagent creates a DPI RESPONSE packet in which the error_code is set to SNMP_ERROR_noError (zero) and error_index is set to zero. No name, type, length, or value information is needed because the RESPONSE to a SET should contain exactly the same varBind data as the data present in the request. The agent can use the values it already has. This suggests that the agent must keep state information, and that is the case. It needs to do that anyway in order to be able to later pass the data with a DPI COMMIT or DPI UNDO packet. Since there are no errors, the subagent must have allocated the required resources and prepared itself for the SET. It does not yet carry out the set, that will be done at COMMIT time. The subagent sends a DPI RESPONSE packet, indicating success or failure for the preparation phase, back to the agent. The agent will issue a SET request for all other varBinds in the same original SNMP request it received. This may be to the same subagent or to one or more different subagents. Once all SET requests have returned a "no error" condition, the agent starts sending DPI COMMIT packets to the subagent(s). If any SET request returns an error, the agent sends DPI UNDO packets to those subagents that indicated successful processing of the SET preparation phase. When the subagent receives the DPI COMMIT packet, all the varBind information will again be available in the packet. The subagent can now carry out the SET request. If the subagent encounters an error while processing the COMMIT request, it creates a DPI RESPONSE packet with value SNMP_ERROR_commitFailed in the error_code field and an error_index that lists at which varBind the error occurs. The first varBind is index 1, and so on. No name, type, length, or value information is needed. The fact that a commitFailed error exists does not mean that this error should be returned easily. A subagent should do all that is possible to make a COMMIT succeed. If there are no errors and the SET and COMMIT have been carried out with success, the subagent creates a DPI RESPONSE packet in which the error_code is set to SNMP_ERROR_noError (zero) and error_index is set to zero. No name, type, length, or value information is needed. So far we have discussed a successful SET and COMMIT sequence. However, after a successful SET, the subagent may receive a DPI UNDO packet. The subagent must now undo any preparations it made during the SET processing, such as free allocated memory. Even after a COMMIT, a subagent may still receive a DPI UNDO packet. This will occur if some other subagent could not complete a COMMIT request. Because of the SNMP requirement that all varBinds in a single SNMP SET request must be changed "as if simultaneous", all committed changes must be undone if any of the COMMIT requests fail. In this case the subagent must try and undo the committed SET operation. If the subagent encounters an error while processing the UNDO request, it creates a DPI RESPONSE packet with value SNMP_ERROR_undoFailed in the error_code field and an error_index that lists at which varBind the error occurs. The first varBind is index 1, and so on. No name, type, length, or value information is needed. The fact that an undoFailed error exists does not mean that this error should be returned easily. A subagent should do all that is possible to make an UNDO succeed. If there are no errors and the UNDO has been successful, the subagent creates a DPI RESPONSE packet in which the error_code is set to SNMP_ERROR_noError (zero) and error_index is set to zero. No name, type, length, or value information is needed. Related Information Processing a SET/COMMIT/UNDO Request ═══ 11.2.5. GETNEXT Processing ═══ The DPI GETNEXT packet contains the object(s) on which the GETNEXT operation must be performed. For this operation, the subagent is to return the name, type, length, and value of the next variable it supports whose (ASN.1) name lexicographically follows the one passed in the group ID (sub-tree) and instance ID. In this case, the instance ID may not be present (NULL) in the incoming DPI packet implying that the NEXT object must be the first instance of the first object in the sub-tree that was registered. It is important to realize that a given subagent may support several discontiguous sections of the MIB tree. In that situation, it would be incorrect to jump from one section to another. This problem is correctly handled by examining the group ID in the DPI packet. This group ID represents the "reason" why the subagent is being called. It holds the prefix of the tree that the subagent had indicated it supported (registered). If the next variable supported by the subagent does not begin with that prefix, the subagent must return the same object instance as in the request, for example the group ID and instance ID with a value of SNMP_TYPE_endOfMibView (implied NULL value). This endOfMibView is not considered an error, so the error_code and error_index should be zero. If required, the SNMP agent will call upon the subagent again, but pass it a different group ID (prefix). This is illustrated in the discussion below. Assume there are two subagents. The first subagent registers two distinct sections of the tree: A and C. In reality, the subagent supports variables A.1 and A.2, but it correctly registers the minimal prefix required to uniquely identify the variable class it supports. The second subagent registers section B, which appears between the two sections registered by the first agent. If a management station begins browsing the MIB, starting from A, the following sequence of queries of the form get-next(group ID,instance ID) would be performed: Subagent 1 gets called: get-next(A,none) = A.1 get-next(A,1) = A.2 get-next(A,2) = endOfMibView Subagent 2 is then called: get-next(B,none) = B.1 get-next(B,1) = endOfMibView Subagent 1 gets called again: get-next(C,none) = C.1 Related Information Processing a GETNEXT Request ═══ 11.2.6. GETBULK Processing ═══ You can ask the agent to translate GETBULK requests into multiple GETNEXT requests. This is basically the default and is specified in the DPI REGISTER packet. In principle, we expect the majority of DPI subagents to run on the same machine as the agent, or on the same physical network. Therefore, repetitive GETNEXT requests remain local and, in general, should not be a problem. Otherwise, the subagent can tell the agent to pass on a DPI GETBULK packet. When a GETBULK request is received, the subagent must process the request and send a RESPONSE that sends back as many varBinds as requested by the request, as long as they fit within the buffers. The GETBULK requires similar processing as a GETNEXT with regard to endOfMibView handling. Note: Currently a subagent cannot select GETBULK on OS/2. It will always be translated into multiple GETNEXT requests. Related Information Processing a GETNEXT Request ═══ 11.2.7. OPEN Request ═══ As the first step, a DPI subagent must open a "connection" with the agent. To do so, it must send a DPI OPEN packet in which these parameters must be specified:  The maximum timeout value in seconds. The agent is requested to wait this long for a response to any request for an object being handled by this subagent. The agent may have an absolute maximum timeout value which will be used if the subagent asks for too large a timeout value. A value of zero can be used to indicate that the agent's own default timeout value should be used. A subagent is advised to use a reasonably short interval of a few seconds or so. If a specific sub-tree needs a (much) longer time, a specific REGISTER can be done for that sub-tree with a longer timeout value.  The maximum number of varBinds that the subagent is prepared to handle per DPI packet. Specifying 1 would result in DPI Version 1 behavior of one varBind per DPI packet that the agent sends to the subagent. A value of zero means the agent will try to combine up to as many varBinds as are present in the SNMP packet that belongs to the same sub-tree.  The character set you want to use. By default, a 0 value, which is the native character set of the machine platform where the agent runs. Since the subagent and agent normally run on the same system or platform, you want to use the native character set, which is ASCII on many platforms. If your platform is EBCDIC based, using the native character set of EBCDIC makes it easy to recognize the string representations of the fields, such as the group ID and instance ID. At the same time, the agent will translate the value from ASCII NVT to EBCDIC and vice versa for objects that it knows from a compiled MIB to have a textual convention of DisplayString. This fact cannot be determined from the SNMP PDU encoding because in the PDU the object is only known to be an OCTET_STRING. If your subagent runs on an ASCII based platform and the agent runs on an EBCDIC based platform (or the other way around), you can specify that you want to use the ASCII character set. The agent and subagent programmer knows how to handle the string-based data in this situation. Note: Not all agents need to support other than native character set selections. See Character Set Selection for more information on character set usage.  The subagent ID. This an ASN.1 Object Identifier that uniquely identifies the subagent. This OID is represented as a null terminated string using the selected character set. For example: "1.3.5.1.2.3.4.5"  The subagent description. This is a DisplayString describing the subagent. This is a character string using the selected character set. For example: "DPI sample subagent Version 2.0" Once a subagent has sent a DPI OPEN packet to an agent, it should expect a DPI RESPONSE packet that informs the subagent about the result of the request. The packet ID of the RESPONSE packet should be the same as that of the OPEN request to which the RESPONSE packet is the response. See DPI RESPONSE Error Codes for a list of valid codes that may be expected. If you receive an error RESPONSE on the OPEN packet, you will also receive a DPI CLOSE packet with an SNMP_CLOSE_openError code. In this situation, the agent closes the "connection". If the OPEN is accepted, the next step is to REGISTER one or more MIB sub-trees. Related Information Connecting to the Agent ═══ 11.2.8. CLOSE Request ═══ When a subagent is finished and wants to end processing, it should first UNREGISTER its sub-trees and then close the "connection" with the agent. To do so, it must send a DPI CLOSE packet, which specifies a reason for the closing. See DPI CLOSE Reason Codes for a list of valid codes. You should not expect a response to the CLOSE request. A subagent should also be prepared to handle an incoming DPI CLOSE packet from the agent. In this case, the packet will contain a reason code for the CLOSE request. A subagent does not have to send a response to a CLOSE request. The agent just assumes that the subagent will handle it appropriately. The close takes place regardless of what the subagent does with it. Related Information Processing an CLOSE Request ═══ 11.2.9. REGISTER Request ═══ Before a subagent will receive any requests for MIB variables, it must first register the variables or sub-tree it supports with the SNMP agent. The subagent must specify a number of parameters in the REGISTER request:  The sub-tree to be registered. This is a null terminated string in the selected character set. The sub-tree must have a trailing dot. For example: "1.3.6.1.2.3.4.5."  The requested priority for the registration. The values are: -1 Request for the best available priority. 0 Request for the next best available priority than the highest (best) priority currently registered for this sub-tree. NNN Any other positive value requests that specific priority if available or the next worse priority that is available.  The maximum timeout value in seconds. The agent is requested to wait this long for a response to any request for an object in this sub-tree. The agent may have an absolute maximum timeout value which will be used if the subagents asks for too large a timeout value. A value of zero can be used to indicate that the DPI OPEN value should be used for timeout.  A specification if the subagent wants to do view selection. If it does, the community name from SNMP Version 1 packets will be passed in the DPI GET, GETNEXT, and SET packets. This is not supported on OS/2.  A specification if the subagent wants to receive GETBULK packets or if it just prefers that the agent converts a GETBULK into multiple GETNEXT requests. This is not supported on OS/2. Once a subagent has sent a DPI REGISTER packet to the agent, it should expect a DPI RESPONSE packet that informs the subagent about the result of the request. The packet ID of the RESPONSE packet should be the same as that of the REGISTER packet to which the RESPONSE packet is the response. If the response is successful, the error_index field in the RESPONSE packet contains the priority that the agent assigned to the sub-tree registration. See DPI RESPONSE Error Codes for a list of valid codes that may be expected. Error Code: higherPriorityRegistered The response to a REGISTER request may return the error code "higherPriorityRegistered". This may be caused by:  Another subagent already registered the same sub-tree at a better priority than what you are requesting.  Another subagent already registered a sub-tree at a higher level (at any priority). For instance, if a registration already exists for sub-tree 1.2.3.4.5.6 and you try to register for sub-tree 1.2.3.4.5.6. then you will get "higherPriorityRegistered" error code. If you receive this error code, your sub-tree will be registered, but you will not see any requests for the sub-tree. They will be passed to the sub-agent which registered with a better priority. If you stay connected, and the other sub-agent goes away, then you will get control over the sub-tree at that point in time. Related Information Registering a Sub-Tree with the Agent ═══ 11.2.10. UNREGISTER Request ═══ A subagent may unregister a previously registered sub-tree. The subagent must specify a few parameters in the UNREGISTER request:  The sub-tree to be unregistered. This is a null terminated string in the selected character set. The sub-tree must have a trailing dot. For example: "1.3.6.1.2.3.4.5."  The reason for the unregister. See DPI UNREGISTER Reason Codes for a list of valid reason codes. Once a subagent has sent a DPI UNREGISTER packet to the agent, it should expect a DPI RESPONSE packet that informs the subagent about the result of the request. The packet ID of the RESPONSE packet should be the same as that of the REGISTER packet to which the RESPONSE packet is the response. See DPI RESPONSE Error Codes for a list of valid codes that may be expected. A subagent should also be prepared to handle incoming DPI UNREGISTER packets from the agent. In this situation, the DPI packet will contain a reason code for the UNREGISTER. A subagent does not have to send a response to an UNREGISTER request. The agent just assumes that the subagent will handle it appropriately. The registration is removed regardless of what the subagent returns. Related Information Processing an UNREGISTER Request ═══ 11.2.11. TRAP Request ═══ A subagent can request that the SNMP agent generates a trap for it. The subagent must provide the desired values for the generic and specific parameters of the trap. It may optionally provide a set of one or more name, type, length, or value parameters that will be included in the trap packet. It may optionally specify an Enterprise ID (Object Identifier) for the trap to be generated. If a NULL value is specified for the Enterprise ID, the agent will use the subagent Identifier from the DPI OPEN packet as the Enterprise ID to be sent with the trap. Related Information Generating a TRAP ═══ 11.2.12. ARE_YOU_THERE Request ═══ A subagent can send an ARE_YOU_THERE packet to the agent. This may be useful to do if you have a DPI "connection" over an unreliable transport protocol, such as UDP. If the "connection" is in a healthy state, the agent responds with a RESPONSE packet with SNMP_ERROR_DPI_noError. If the "connection" is not in a healthy state, the agent may respond with a RESPONSE packet with an error indication, but the agent might not react at all. In this situation, you would timeout while waiting for a response. ═══ 11.2.13. Multithreading Programming Considerations ═══ The DPI Version 2.0 DLL for OS/2 (DPI20DLL.DLL file) has been compiled with the /Gm+ compiler flag. This enables it to be used by both single and multithreaded subagents. However, even a single threaded subagent must be compiled with the /Gm+ compiler flag. No Support for Multithreaded Environment Even though the DPI20DLL.DLL has been created with multithreading compile flag, they do not contain any support for a multithreaded environment. There are several static buffers in the DPI code. For compatibility reasons, that cannot be changed. Real multithread support will probably mean several potentially incompatible changes to the DPI 2.0 API. Use a Locking Mechanism If your subagent will be a multithreaded process, then you must always use some locking mechanism of your own around the use of the static buffers. Otherwise, one thread maybe writing into the static buffer while another is writing into the same buffer at the same time. There are two static buffers. One buffer is for building the serialized DPI packet before sending it out and the other buffer is for receiving incoming DPI packets. Basically, all DPI functions that return a pointer to an unsigned char are the DPI functions that write into the static buffer to create a serialized DPI packet: mkDPIAreYouThere() mkDPIopen() mkDPIregister() mkDPIunregister() mkDPItrap() mkDPIresponse() mkDPIpacket() mkDPIclose () After you have called the DPIsend_packet_to_agent() function for the buffer, which is pointed to by the pointer returned by one of the above functions, it is free to use again. There is one function that reads the static input buffer: pDPIpacket() The input buffer gets filled by the DPIawait_packet_from_agent() function. You get a pointer to the static input buffer upon return from the await. The pDPIpacket() function parses the static input buffer and returns a pointer to dynamically allocated memory. Therefore, after the pDPIparse() call, the buffer is available for use again. The current situation is such that if multiple threads are waiting at the same time and for different handles, there is the risk that two incoming DPI packets will overlay each other. If multiple threads are waiting for the same handle, when data arrives both threads come out of the wait. If one of them issues another wait before the other one is finished parsing the input buffer, the buffer may get overlaid by a new packet before the second one gets a chance to parse the packet. The DPI internal handle structures and control blocks used by the underlying code to send and receive data to and from the agent are also static data areas. You must make sure that you use your own locking mechanism around the functions that add, change, or delete data in those static structures. The functions that change those internal static structures are: DPIconnect_to_agentTCP() /* everyone has this one */ DPIconnect_to_agentSHM() /* so-far only for */ /* NetView for OS/2 */ DPIconnect_to_agentUDP() /* not supported at all yet */ DPIdisconnect_from_agent() /* everyone has this one */ The other functions that access those static structures which must be assured that the structure is not being changed while they are referencing it during their execution are: DPIawait_packet_from_agent() DPIsend_packet_to_agent() DPIget_fd_for_handle() While the last 3 functions can be executed concurrently in different threads, you must ensure that no other thread is adding or deleting handles during this process. ═══ 11.3. Basic DPI API Functions ═══ This section describes each of the basic DPI functions that are available to the DPI subagent programmer. Topics The DPIdebug() Function The DPI_PACKET_LEN() Macro The fDPIparse() Function The fDPIset() Function The mkDPIAreYouThere() Function The mkDPIclose() Function The mkDPIopen() Function The mkDPIregister() Function The mkDPIresponse() Function The mkDPIset() Function The mkDPItrap() Function The mkDPIunregister() Function The pDPIpacket() Function ═══ 11.3.1. The DPIdebug() Function ═══ Syntax #include void DPIdebug(int level); Parameters level If this value is zero, tracing is turned off. If it has any other value, tracing is turned on at the specified level. The higher the value, the more detail. A higher level includes all lower levels of tracing. Currently there are two levels of detail: 1 Display packet creation and parsing. 2 Display hex dump of incoming and outgoing DPI packets. Description The DPIdebug() function turns DPI internal debugging/tracing on or off. Examples #include DPIdebug(2); Related Information The snmp_dpi.h Include File ═══ 11.3.2. The DPI_PACKET_LEN() Macro ═══ Syntax #include int DPI_PACKET_LEN(unsigned char *packet_p) Parameters packet_p A pointer to a serialized DPI packet. Return Values An integer representing the total DPI packet length. Description The DPI_PACKET_LEN macro generates C-code that returns an integer representing the length of a DPI packet. It uses the first two octets in network byte order of the packet to calculate the length. Examples #include unsigned char *pack_p; int length; pack_p = mkDPIclose(SNMP_CLOSE_goingDown); if (pack_p) { length = DPI_PACKET_LEN(pack_p); /* send packet to agent or subagent */ } /* endif */ ═══ 11.3.3. The fDPIparse() Function ═══ Syntax #include void fDPIparse(snmp_dpi_hdr *hdr_p); Parameters hdr_p A pointer to the parse tree. The parse tree is represented by an snmp_dpi_hdr structure. Description The fDPIparse() function frees a parse tree that was previously created by a call to pDPIpacket(). The parse tree may have been created in other ways too. After calling fDPIparse(), no further references to the parse tree can be made. A complete or partial DPI parse tree is also implicitly freed by call to a DPI function that serializes a parse tree into a DPI packet. The section that describes each function tells you if this is the case. An example of such a function is mkDPIresponse(). Examples #include snmp_dpi_hdr *hdr_p; unsigned char *pack_p; /* assume pack_p points to */ /* incoming DPI packet */ hdr_p = pDPIpacket(pack_p); /* handle the packet and when done do the following */ if (hdr_p) fDPIparse(hdr_p); Related Information The snmp_dpi_hdr Structure The pDPIpacket() Function The snmp_dpi.h Include File ═══ 11.3.4. The fDPIset() Function ═══ Syntax #include void fDPIset(snmp_dpi_set_packet *packet_p); Parameters packet_p A pointer to the first snmp_dpi_set_packet structure in a chain of such structures. Description The fDPIset() function is typically used if you must free a chain of one or more snmp_dpi_set_packet structures. This may be the case if you are in the middle of preparing a chain of such structures for a DPI RESPONSE packet, but then run into an error before you can actually make the response. If you get to the point where you make a DPI response packet to which you pass the chain of snmp_dpi_set_packet structures, then the mkDPIresponse() function will free the chain of snmp_dpi_set_packet structures. Examples #include unsigned char *pack_p; snmp_dpi_hdr *hdr_p; snmp_dpi_set_packet *set_p, *first_p; long int num1 = 0, num2 = 0; hdr_p = pDPIpacket(pack_p); /* assume pack_p */ /* analyze packet and assume all OK */ /* points to the */ /* now prepare response; 2 varBinds */ /* incoming packet */ set_p = mkDPIset(snmp_dpi_NULL_p, /* create first one */ "1.3.6.1.2.3.4.5.","1.0", /* OID=1, instance=0 */ SNMP_TYPE_Integer32, sizeof(num1), &num1); if (set_p) { /* if success, then */ first_p = set_p; /* save ptr to first */ set_p = mkDPIset(set_p, /* chain next one */ "1.3.6.1.2.3.4.5.","1.1", /* OID=1, instance=1 */ SNMP_TYPE_Integer32, sizeof(num2), &num2); if (set_p) { /* success 2nd one */ pack_p = mkDPIresponse(hdr_p, /* make response */ SNMP_ERROR_noError, /* It will also free */ 0L, first_p); /* the set_p tree */ /* send DPI response to agent */ } else { /* 2nd mkDPIset fail */ fDPIset(first_p); /* must free chain */ } /* endif */ } /* endif */ Related Information The fDPIparse() Function The snmp_dpi_set_packet Structure The mkDPIresponse() Function ═══ 11.3.5. The mkDPIAreYouThere() Function ═══ Syntax #include unsigned char *mkDPIAreYouThere(void); Parameters None. Return Values If successful, a pointer to a static DPI packet buffer is returned. The first two bytes of the buffer in network byte order contain the length of the remaining packet. The macro DPI_PACKET_LEN can be used to calculate the total length of the DPI packet. If failure, a NULL pointer is returned. Note: The static buffer for the DPI packet is shared by other mkDPIxxxx() functions that create a serialized DPI packet. Description The mkDPIAreYouThere() function creates a serialized DPI ARE_YOU_THERE packet that can be sent to the DPI peer, which is normally the agent. A subagent connected via TCP probably does not need this function because, normally when the agent breaks the "connection", you will receive an EOF on the file descriptor. For unreliable "connections", like over UDP, this function may be useful to periodically poll the agent and verify that it still knows about the subagent. If your "connection" to the agent is still healthy, the agent will send a DPI RESPONSE with SNMP_ERROR_DPI_noError in the error code field and zero in the error index field. The RESPONSE will have no varBind data. If your "connection" is not healthy, the agent may send a response with an error indication, or may just not send a response at all. Examples #include unsigned char *pack_p; pack_p = mkDPIAreYouThere(); if (pack_p) { /* send the packet to the agent */ } /* endif */ /* wait for response with DPIawait_packet_from_agent() */ /* normally the response should come back pretty quickly, */ /* but it depends on the load of the agent */ Related Information The snmp_dpi_resp_packet Structure The DPIawait_packet_from_agent() Function ═══ 11.3.6. The mkDPIclose() Function ═══ Syntax #include unsigned char *mkDPIclose(char reason_code); Parameters reason_code The reason for closing the DPI connection. See DPI CLOSE Reason Codes for a list of valid reason codes. Return Values If successful, a pointer to a static DPI packet buffer is returned. The first two bytes of the buffer in network byte order contain the length of the remaining packet. The macro DPI_PACKET_LEN can be used to calculate the total length of the DPI packet. If failure, a NULL pointer is returned. Note: The static buffer for the DPI packet is shared by other mkDPIxxxx() functions that create a serialized DPI packet. Description The mkDPIclose() function creates a serialized DPI CLOSE packet that can be sent to the DPI peer. As a result of sending the packet, the DPI connection will be closed. Sending a DPI CLOSE packet to the agent implies an automatic DPI UNREGISTER for all registered sub-trees on the connection being closed. Examples #include unsigned char *pack_p; pack_p = mkDPIclose(SNMP_CLOSE_goingDown); if (pack_p) { /* send the packet to the agent */ } /* endif */ Related Information The snmp_dpi_close_packet Structure DPI CLOSE Reason Codes ═══ 11.3.7. The mkDPIopen() Function ═══ Syntax #include unsigned char *mkDPIopen( /* Make a DPI open packet */ char *oid_p, /* subagent Identifier (OID) */ char *description_p, /* subagent descriptive name */ unsigned long timeout, /* requested default timeout */ unsigned long max_varBinds, /* max varBinds per DPI ndle */ char character_set, /* selected character set */ #define DPI_NATIVE_CSET 0 /* 0 = native character set */ #define DPI_ASCII_CSET 1 /* 1 = ASCII character set */ unsigned long password_len, /* length of password (if any)*/ unsigned char *password_p); /* ptr to password (if any) */ Parameters oid_p A pointer to a NULL terminated character string representing the OBJECT IDENTIFIER which uniquely identifies the subagent. description_p A pointer to a NULL terminated character string, which is a descriptive name for the subagent. This can be any DisplayString, which basically is an octet string containing only characters from the ASCII NVT set. timeout The requested timeout for this subagent. An agent often has a limit for this value and it will use that limit if this value is larger. A timeout of zero has a special meaning in the sense that the agent will use its own default timeout value. max_varBinds The maximum number of varBinds per DPI packet that the subagent is prepared to handle. It must be a positive number or zero. If a value greater than 1 is specified, the agent will try to combine as many varBinds which belong to the same sub-tree per DPI packet as possible up to this value. If a value of zero is specified, the agent will try to combine up to as many varBinds as are present in the SNMP packet and belong to the same sub-tree. For example, a value of zero means no limit. character_set The character set that you want to use for string-based data fields in the DPI packets and structures. The choices are: DPI_NATIVE_CSET Specifies that you want to use the native character set of the platform on which the agent that you connect to is running. DPI_ASCII_CSET Specifies that you want to use the ASCII character set. The agent will translate between ASCII and the native character set as required. See Character Set Selection for more information. password_len The length in octets of an optional password. It depends on the agent implementation if a password is needed. If not, a zero length may be specified. password_p A pointer to an octet string representing the password for this subagent. A password may include any character value, including the NULL character. If the password_len is zero, this can be a NULL pointer. Return Values If successful, a pointer to a static DPI packet buffer is returned. The first two bytes of the buffer in network byte order contain the length of the remaining packet. The macro DPI_PACKET_LEN can be used to calculate the total length of the DPI packet. If failure, a NULL pointer is returned. Note: The static buffer for the DPI packet is shared by other mkDPIxxxx() functions that create a serialized DPI packet. Description The mkDPIopen() function creates a serialized DPI OPEN packet that can then be sent to the DPI peer which is a DPI capable SNMP agent. Normally you will want to use the native character set, which is the easiest for the subagent programmer. However, if the agent and subagent each run on their own platform and those platforms use different native character sets, you must select the ASCII character set, so that you both know exactly how to represent string-based data that is being send back and forth. Currently you do not need to specify a password to connect to the OS/2 SNMP agent. Therefore, you can pass a length of zero and a NULL pointer for the password. Examples #include unsigned char *pack_p; pack_p = mkDPIopen("1.3.6.1.2.3.4.5", "Sample DPI subagent" 0L,2L, DPI_NATIVE_CSET, /* max 2 varBinds */ 0,(char *)0); if (pack_p) { /* send packet to the agent */ } /* endif */ Related Information Character Set Selection ═══ 11.3.8. The mkDPIregister() Function ═══ Syntax #include unsigned char *mkDPIregister( /* Make a DPI register packet */ unsigned short timeout, /* in seconds (16-bit) */ long int priority, /* requested priority */ char *group_p, /* ptr to group ID (sub-tree) */ char bulk_select);/* Bulk selection (GETBULK) */ #define DPI_BULK_NO 0 /* map GETBULK into GETNEXTs */ #define DPI_BULK_YES 1 /* pass GETBULK to subagent */ Parameters timeout The requested timeout in seconds. An agent often has a limit for this value and it will use that limit if this value is larger. The value zero has special meaning in the sense that it tells the agent to use the timeout value that was specified in the DPI OPEN packet. priority The requested priority. This field may contain any of these values: -1 Requests the best available priority. 0 Requests a better priority than the highest priority currently registered. Use this value to obtain the SNMP DPI Version 1 behavior. nnn Any positive value. You will receive that priority if available, otherwise the next best priority that is available. group_p A pointer to a NULL terminated character string that represents the sub-tree to be registered. This group ID must have a trailing dot. bulk_select Specifies if you want the agent to pass GETBULK on to the subagent or to map them into multiple GETNEXT requests. The choices are: DPI_BULK_NO Do not pass any GETBULK requests, but instead map a GETBULK request into multiple GETNEXT requests. DPI_BULK_YES Do pass a GETBULK request to the subagent. Return Values If successful, a pointer to a static DPI packet buffer is returned. The first two bytes of the buffer in network byte order contain the length of the remaining packet. The macro DPI_PACKET_LEN can be used to calculate the total length of the DPI packet. If failure, a NULL pointer is returned. Note: The static buffer for the DPI packet is shared by other mkDPIxxxx() functions that create a serialized DPI packet. Description The mkDPIregister() function creates a serialized DPI REGISTER packet that can then be sent to the DPI peer which is a DPI capable SNMP agent. The bulk_select can be used to ask the agent to map an SNMP GETBULK request into multiple GETNEXT requests. This makes it easier for the DPI subagent programmer because GETBULK processing doesn't need implementing. However, if one expects that a single GETBULK might improve the performance a lot, one can tell the agent to pass such requests. This might be the case if one expects a GETBULK to arrive often for a table for which one needs to do a kernel dive. Using GETBULK, one might be able to do just one dive instead of many. Although one could anticipate the dive with a GETNEXT also, and therefore obtain and cache the table upon the first GETNEXT request. According to the DPI 2.0 RFC, not all agents need to support DPI_BULK_YES. These agents will return an appropriate error code in the DPI RESPONSE though if such is the case. Normally the SNMP agent sends a DPI RESPONSE packet back. This packet identifies if the register was successful or not. Examples #include unsigned char *pack_p; pack_p = mkDPIregister(0,0L,"1.3.6.1.2.3.4.5." DPI_BULK_NO); if (pack_p) { /* send packet to agent and await response */ } /* endif */ Related Information The snmp_dpi_resp_packet Structure ═══ 11.3.9. The mkDPIresponse() Function ═══ Syntax #include unsigned char *mkDPIresponse( /* Make a DPI response packet*/ snmp_dpi_hdr *hdr_p, /* ptr to packet to respnd to*/ long int error_code, /* error code: SNMP_ERROR_xxx*/ long int error_index, /* index to varBind in error */ snmp_dpi_set_packet *packet_p);/* ptr to varBinds, a chain */ /* of dpi_set_packets */ Parameters hdr_p A pointer to the parse tree of the DPI request to which this DPI packet will be the response. The function uses this parse tree to copy the packet_id and the DPI version and release, so that the DPI packet is correctly formatted as a response. error_code The error code. See DPI RESPONSE Error Codes for a list of valid codes. error_index Specifies the first varBind in error. Counting starts at 1 for the first varBind. This field should be zero if there is no error. packet_p A pointer to a chain of snmp_dpi_set_packet structures. This partial parse tree will be freed by the mkDPIresponse() function. So upon return you cannot reference it anymore. Pass a NULL pointer if there are no varBinds to be returned. Return Values If successful, a pointer to a static DPI packet buffer is returned. The first two bytes of the buffer in network byte order contain the length of the remaining packet. The macro DPI_PACKET_LEN can be used to calculate the total length of the DPI packet. If failure, a NULL pointer is returned. Note: The static buffer for the DPI packet is shared by other mkDPIxxxx() functions that create a serialized DPI packet. Description The mkDPIresponse() function is used at the subagent side to prepare a DPI RESPONSE packet to a GET, GETNEXT, GETBULK, SET, COMMIT or UNDO request. The resulting packet can be sent to the DPI peer, which is normally a DPI capable SNMP agent. Examples #include unsigned char *pack_p; snmp_dpi_hdr *hdr_p; snmp_dpi_set_packet *set_p; long int num; hdr_p = pDPIpacket(pack_p); /* parse incoming packet */ /* assume it's in pack_p */ if (hdr_p) { /* analyze packet, assume GET, no error */ set_p = mkDPIset(snmp_dpi_set_packet_NULL_p, "1.3.6.1.2.3.4.5.", "1.0", SNMP_TYPE_Integer32, sizeof(num), &num); if (set_p) { pack_p = mkDPIresponse(hdr_p, SNMP_ERROR_noError, 0L, set_p); if (pack_p) { /* send packet to subagent */ } /* endif */ } /* endif */ } /* endif */ The mkDPIresponse() function is used at the agent side to prepare a DPI RESPONSE packet to an OPEN, REGISTER or UNREGISTER request. In the case of a RESPONSE to a REGISTER request and if there is no error, the actually assigned priority must be passed in the error_index parameter. The resulting packet can be sent to the DPI peer, which is normally a subagent. Examples #include unsigned char *pack_p; snmp_dpi_hdr *hdr_p; long int priority; hdr_p = pDPIpacket(pack_p); /* parse incoming packet */ /* assume it's in pack_p */ if (hdr_p) { /* analyze packet, assume REGISTER and OK */ pack_p = mkDPIresponse(hdr_p, SNMP_ERROR_DPI_noError, priority, snmp_dpi_set_packet_NULL_p); if (pack_p) { /* send packet to subagent */ } /* endif */ } /* endif */ Related Information The pDPIpacket() Function The snmp_dpi_hdr Structure The snmp_dpi_next_packet Structure ═══ 11.3.10. The mkDPIset() Function ═══ Syntax #include snmp_dpi_set_packet *mkDPIset( /* Make DPI set packet tree */ snmp_dpi_set_packet *packet_p, /* ptr to SET structure */ char *group_p, /* ptr to group ID(sub-tree)*/ char *instance_p,/* ptr to instance OIDstring*/ int value_type,/* value type: SNMP_TYPE_xxx*/ int value_len, /* length of value */ void *value_p); /* ptr to value */ Parameters packet_p A pointer to a chain of snmp_dpi_set_packet structures. Pass a NULL pointer if this is the first structure to be created. group_p A pointer to a NULL terminated character string that represents the registered sub-tree that caused this GET request to be passed to this DPI subagent. The sub-tree must have a trailing dot. instance_p A pointer to a NULL terminated character string that represents the rest, which is the piece following the sub-tree part, of the OBJECT IDENTIFIER of the variable instance being accessed. Use of the term instance_p here should not be confused with an OBJECT instance because this string may consist of a piece of the OBJECT IDENTIFIER plus the INSTANCE IDENTIFIER. value_type The type of the value. See DPI SNMP Value Types for a list of currently defined value types. value_len This is the value that specifies the length in octets of the value pointed to by the value field. The length may be zero if the value is of type SNMP_TYPE_NULL. The maximum value is 64K -1. However, the implementation often makes the length significantly less. The OS/2 implementation limit is 4K. The SNMP_DPI_BUFFSIZE in the snmp_dpi.h include file defines the limit for OS/2. value_p A pointer to the actual value. This field may contain a NULL pointer if the value is of implicit or explicit type SNMP_TYPE_NULL. Return Values If successful and a chain of one or more packets was passed in the packet_p parameter, the same pointer that was passed in packet_p is returned. A new dynamically allocated structure has then been added to the end of that chain of snmp_dpi_get_packet structures. If successful and a NULL pointer was passed in the packet_p parameter, a pointer to a new dynamically allocated structure is returned. If failure, a NULL pointer is returned. Description The mkDPIset() function is used at the subagent side to prepare a chain of one or more snmp_dpi_set_packet structures. This chain is used to create a DPI RESPONSE packet by a call to mkDPIresponse() which can be sent to the DPI peer, which is normally a DPI capable SNMP agent. The chain of snmp_dpi_set_packet structures can also be used to create a DPI TRAP packet that includes varBinds as explained in The mkDPItrap() Function. For the value_len, the maximum value is 64K -1. However, the implementation often makes the length significantly less. For example the SNMP PDU size may be limited to 484 bytes at the SNMP manager or agent side. In this case, the total response packet cannot exceed 484 bytes, so a value_len is limited by that. You can send the DPI packet to the agent, but the manager will never see it. Examples #include unsigned char *pack_p; snmp_dpi_hdr *hdr_p; snmp_dpi_set_packet *set_p; long int num; hdr_p = pDPIpacket(pack_p) /* parse incoming packet */ /* assume it's in pack_p */ if (hdr_p) { /* analyze packet, assume GET, no error */ set_p = mkDPIset(snmp_dpi_set_packet_NULL_p, "1.3.6.1.2.3.4.5.", "1.0", SNMP_TYPE_Integer32, sizeof(num), &num); if (set_p) { pack_p = mkDPIresponse(hdr_p, SNMP_ERROR_noError, 0L, set_p); if (pack_p) /* send packet to subagent */ } /* endif */ } /* endif */ } /* endif */ The mkDPIset() function is used at the agent side to prepare a chain of one or more snmp_dpi_set_packet structures. This chain is normally anchored in an snmp_dpi_hdr structure that has its packet_type field set to SNMP_DPI_SET, SNMP_DPI_COMMIT or SNMP_DPI_UNDO. When all varBinds have been prepared into snmp_dpi_set_packet structures, a call can be made to mkDPIpacket() which will serialize the DPI parse tree into a DPI packet that can be sent to the DPI peer, which is normally a subagent. Examples #include unsigned char *pack_p; snmp_dpi_hdr *hdr_p; long int num; hdr_p = mkDPIhdr(SNMP_DPI_SET); if (hdr_p) { hdr_p->data_u.set_p = mkDPIset(snmp_dpi_set_packet_NULL_p, "1.3.6.1.2.3.4.5.", "1.0", SNMP_TYPE_Integer32, sizeof(num), &num); if (hdr_p->data_u.set_p) { pack_p = mkDPIpacket(hdr_p); if (pack_p) /* send packet to subagent */ } /* endif */ } /* endif */ } /* endif */ If you must chain many snmp_dpi_set_packet structures, be sure to note that the packets are chained only by forward pointers. It is recommended that you use the last structure in the existing chain as the packet_p parameter. Then, the underlying code does not have to scan through a possibly long chain of structures in order to chain the new structure at the end. In the next example let's assume that we want to chain 20 snmp_dpi_set_packet structures as a response to a GETBULK. Examples #include unsigned char *pack_p; snmp_dpi_hdr *hdr_p; snmp_dpi_set_packet *first_p; snmp_dpi_set_packet *set_p; long int num[20]; int i; hdr_p = pDPIpacket(pack_p); /* parse incoming packet */ /* assume it's in pack_p */ if (hdr_p) { /* analyze packet, assume GETBULK, no error. In this */ /* example we do not check max_repetitions as we should */ set_p = snmp_dpi_set_packet_NULL_p; first_p = snmp_dpi_set_packet_NULL_p; for (i=0; i<20; i++) { char instance[5]; sprintf(instance, "%1.%d", i+1); set_p = mkDPIset(set_p, "1.3.6.1.2.3.4.5.", instance, SNMP_TYPE_Integer32, sizeof(num), &num[i]); if (set_p) { if (first_p) continue; /* OK, iterate for loop */ first_p = set_p; /* remember first one */ } else if (first_p) { /* failed to mkDPIset */ fDPIset(first_p) /* free allocated memory */ first_p = snmp_dpi_set_packet_NULL_p; /* reset */ } /* endif */ } /* endfor */ if (first_p) { pack_p = mkDPIresponse(hdr_p, SNMP_ERROR_noError, 0L, first_p); if (pack_p) /* send packet to subagent */ } /* endif */ } /* endif */ } /* endif */ Related Information The pDPIpacket() Function The mkDPIresponse() Function The mkDPItrap() Function The snmp_dpi_hdr Structure The snmp_dpi_set_packet Structure DPI SNMP Value Types Value Representation ═══ 11.3.11. The mkDPItrap() Function ═══ Syntax #include unsigned char *mkDPItrap( /* Make a DPI trap packet */ long int generic, /* generic traptype (32 bit)*/ long int specific, /* specific traptype (32 bit)*/ snmp_dpi_set_packet *packet_p, /* ptr to varBinds, a chain */ /* of dpi_set_packets */ char *enterprise_p); /* ptr to enterprise OID */ Parameters generic The generic trap type. The range of this value is 0-6, where 6, which is enterprise specific, is the type that is probably used most by DPI subagent programmers. The values 0-5 are well defined standard SNMP traps. specific The enterprise specific trap type. This can be any value that is valid for the MIB sub-trees that the subagent implements. packet_p A pointer to a chain of snmp_dpi_set_structures, representing the varBinds to be passed with the trap. This partial parse tree will be freed by the mkDPItrap() function so you cannot reference it anymore upon completion of the call. A NULL pointer means that there are no varBinds to be included in the trap. enterprise_p A pointer to a NULL terminated character string representing the enterprise ID (OBJECT IDENTIFIER) for which this trap is defined. A NULL pointer can be used. In this case, the subagent Identifier, as passed in the DPI OPEN packet, will be used when the agent receives the DPI TRAP packet. Return Values If successful, a pointer to a static DPI packet buffer is returned. The first two bytes of the buffer in network byte order contain the length of the remaining packet. The macro DPI_PACKET_LEN can be used to calculate the total length of the DPI packet. If failure, a NULL pointer is returned. Note: The static buffer for the DPI packet is shared by other mkDPIxxxx() functions that create a serialized DPI packet. Description The mkDPItrap() function is used at the subagent side to prepare a DPI TRAP packet. The resulting packet can be sent to the DPI peer, which is normally a DPI capable SNMP agent. Examples #include unsigned char *pack_p; snmp_dpi_set_packet *set_p; long int num; set_p = mkDPIset(snmp_dpi_set_packet_NULL_p, "1.3.6.1.2.3.4.5.", "1.0", SNMP_TYPE_Integer32, sizeof(num), &num); if (set_p) { pack_p = mkDPItrap(6,1,set_p, (char *)0); if (pack_p) { /* send packet to subagent */ } /* endif */ } /* endif */ Related Information The fDPIset() Function ═══ 11.3.12. The mkDPIunregister() Function ═══ Syntax #include unsigned char *mkDPIunregister( /* Make DPI unregister packet */ char reason_code; /* unregister reason code */ char *group_p); /* ptr to group ID (sub-tree) */ Parameters reason_code The reason for the unregister. See DPI UNREGISTER Reason Codes for a list of the currently defined reason codes. group_p A pointer to a NULL terminated character string that represents the sub-tree to be unregistered. The sub-tree must have a trailing dot. Return Values If successful, a pointer to a static DPI packet buffer is returned. The first two bytes of the buffer in network byte order contain the length of the remaining packet. The macro DPI_PACKET_LEN can be used to calculate the total length of the DPI packet. If failure, a NULL pointer is returned. Note: The static buffer for the DPI packet is shared by other mkDPIxxxx() functions that create a serialized DPI packet. Description The mkDPIunregister() function creates a serialized DPI UNREGISTER packet that can be sent to the DPI peer, which is a DPI capable SNMP agent or subagent. Normally, the SNMP peer then sends a DPI RESPONSE packet back. This packet identifies if the unregister was successful or not. Examples #include unsigned char *pack_p; pack_p = mkDPIunregister( SNMP_UNREGISTER_goingDown, "1.3.6.1.2.3.4.5."); if (pack_p) { /* send packet to agent or subagent and await response */ } /* endif */ Related Information The snmp_dpi_ureg_packet Structure ═══ 11.3.13. The pDPIpacket() Function ═══ Syntax #include snmp_dpi_hdr *pDPIpacket(unsigned char *packet_p); Parameters packet_p A pointer to a serialized DPI packet. Return Values If successful, a pointer to a DPI parse tree (snmp_dpi_hdr) is returned. Memory for the parse tree has been dynamically allocated, and it is the callers responsibility to free it when no longer needed. You can use the fDPIparse() function to free the parse tree. If failure, a NULL pointer is returned. Description The pDPIpacket() function parses the buffer pointed to by the packet_p parameter. It ensures that the buffer contains a valid DPI packet and that the packet is for a DPI version and release that is supported by the DPI functions in use. Examples #include unsigned char *pack_p; snmp_dpi_hdr *hdr_p; hdr_p = pDPIpacket(pack_p); /* parse incoming packet */ /* assume it's in pack_p */ if (hdr_p) { /* analyze packet, and handle it */ } Related Information The snmp_dpi_hdr Structure The snmp_dpi.h Include File The fDPIparse() Function ═══ 11.4. Transport-Related DPI API Functions ═══ This section describes each of the DPI transport-related functions that are available to the DPI subagent programmer. These functions try to hide any platform specific issues for the DPI subagent programmer so that the subagent can be made as portable as possible. If you need detailed control for sending and awaiting DPI packets, you may have to do some of the transport-related code yourself. The transport-related functions are basically the same for any platform, except for the initial call to setup a connection. OS/2 currently supports the TCP/IP transport type. Topics The DPIawait_packet_from_agent() Function The DPIconnect_to_agent_SHM() Function The DPIconnect_to_agent_TCP() Function The DPIdisconnect_from_agent() Function The DPIget_fd_for_handle() Function The DPIsend_packet_to_agent() Function The lookup_host() Function The query_DPI_port() Function ═══ 11.4.1. The DPIawait_packet_from_agent() Function ═══ Syntax #include int DPIawait_packet_from_agent( /* await a DPI packet */ int handle, /* on this connection */ int timeout, /* timeout in seconds */ unsigned char **message_p, /* receives ptr to data */ unsigned long *length); /* receives length of data */ Parameters handle A handle as obtained with a DPIconnect_to_agent_xxxx() call. timeout A timeout value in seconds. There are two special values: -1 Causes the function to wait forever until a packet arrives. 0 Means that the function will only check if a packet is waiting. If not, an immediate return is made. If there is a packet, it will be returned. message_p The address of a pointer that will receive the address of a static DPI packet buffer or, if there is no packet, a NULL pointer. length The address of an unsigned long integer that will receive the length of the received DPI packet or, if there is no packet, a zero value. Return Values If successful, a zero (DPI_RC_noError) is returned. The buffer pointer and length of the caller will be set to point to the received DPI packet and to the length of that packet. If failure, a negative integer is returned. It indicates the kind of error that occurred. See Return Codes from DPI Transport-Related Functions for a list of possible error codes. Description The DPIawait_packet_from_agent() function is used at the subagent side to await a DPI packet from the DPI capable SNMP agent. The programmer can specify how long to wait. Examples #include int handle; unsigned char *pack_p; unsigned long length; handle = DPIconnect_to_agent_TCP("localhost", "public"); if (handle < 0) { printf("Error %d from connect\n",handle); exit(1); } /* endif */ /* do useful stuff */ rc = DPIawait_packet_from_agent(handle, -1, &pack_p, &length); if (rc) { printf("Error %d from await packet\n"); exit(1); } /* endif */ /* handle the packet */ Related Information The DPIconnect_to_agent_TCP() Function ═══ 11.4.2. The DPIconnect_to_agent_SHM() Function ═══ Syntax #inlcude int DPIconnect_to_agent_SHM( /* Connect to DPI Shared Mem */ int queue_id);/* target (agent) queue id */ Parameters queu_id A queue_id known by the agent. The value is a fixed queueid. It must always be 1. Return Values If successful, a positive integer that represents the connection is returned. It is to be used as a handle in subsequent calls to DPI transport-related functions. If failure, a negative integer is returned. It indicates the kind of error that occurred. See Return Codes from DPI Transport-Related Functions for a list of possible error codes. Description The DPIconnect_to_agent_SHM() function is used at the subagent side to setup a connection (via SHared Memory) to the DPI capable SNMP agent. Examples #include int handle; handle = DPIconnect_to_agent_SHM(1); if (handle < 0) { printf("Error %d from connect\n",handle); exit(1); } /* endif */ Related Information Return Codes from DPI Transport-Related Functions The DPIconnect_to_agent_TCP() Function ═══ 11.4.3. The DPIconnect_to_agent_TCP() Function ═══ Syntax #include int DPIconnect_to_agent_TCP( /* Connect to DPI TCP port */ char *hostname_p, /* target hostname/IP address */ char *community_p); /* community name */ Parameters hostname_p A pointer to a NULL terminated character string representing the host name or IP address in dot notation of the host where the DPI capable SNMP agent is running. community_p A pointer to a NULL terminated character string representing the community name that is required to obtain the dpiPort from the SNMP agent via an SNMP GET request. Return Values If successful, a positive integer that represents the connection is returned. It is to be used as a handle in subsequent calls to DPI transport-related functions. If failure, a negative integer is returned. It indicates the kind of error that occurred. See Return Codes from DPI Transport-Related Functions for a list of possible error codes. Description The DPIconnect_to_agent_TCP() function is used at the subagent side to setup a TCP connection to the DPI capable SNMP agent. Examples #include int handle; handle = DPIconnect_to_agent_TCP("localhost", "loopback"); if (handle < 0) { printf("Error %d from connect\n",handle); exit(1); } /* endif */ Related Information Return Codes from DPI Transport-Related Functions The DPIconnect_to_agent_SHM() Function ═══ 11.4.4. The DPIdisconnect_from_agent() Function ═══ Syntax #include void DPIdisconnect_from_agent( /* disconnect from DPI (agent)*/ int handle); /* close this connection */ Parameters handle A handle as obtained with a DPIconnect_to_agent_xxxx() call. Return Values If successful, a positive integer that represents the connection is returned. It is to be used as a handle in subsequent calls to DPI transport-related functions. If failure, a negative integer is returned. It indicates the kind of error that occurred. See Return Codes from DPI Transport-Related Functions for a list of possible error codes. Description The DPIdisconnect_from_agent() function is used at the subagent side to terminate a connection to the DPI capable SNMP agent. Examples #include int handle; handle = DPIconnect_to_agent_TCP("localhost", "loopback"); if (handle < 0) { printf("Error %d from connect\n",handle); exit(1); } /* endif */ /* do useful stuff */ DPIdisconnect_from_agent(handle); Related Information The DPIconnect_to_agent_TCP() Function ═══ 11.4.5. The DPIget_fd_for_handle() Function ═══ Syntax #include int DPIget_fd_for_handle( /* get the file descriptor */ int handle); /* for this handle */ Parameters handle A handle that was obtained with a DPIconnect_to_agent_TCP() call. Return Values If successful, a positive integer representing the file descriptor associated with the specified handle. If failure, a negative integer is returned. It indicates the error that occurred. See Return Codes from DPI Transport-Related Functions for a list of possible error codes. Description The DPIget_fd_for_handle function is used to obtain the file descriptor for the handle, which was obtained with a DPIconnect_to_agent_TCP() call. The DPI subagent programmer would use this function to not only wait for DPI requests, but possibly for other TCP/IP events. The programmer may want to do their own select and include for the file descriptor of the DPI connections. Examples #include #include /* other include files for BSD sockets and such */ int handle; int fd; handle = DPIconnect_to_agentTCP("localhost","public"); if (handle < 0) { printf("Error %d from connect\n",handle); exit(1); } fd = DPIget_fd_for_handle(handle); if (fd <0) { printf("Error %d from get_fd\n",fd); exit(1); } Related Information The DPIconnect_to_agent_TCP() Function ═══ 11.4.6. The DPIsend_packet_to_agent() Function ═══ Syntax #include int DPIsend_packet_to_agent( /* send a DPI packet */ int handle, /* on this connection */ unsigned char *message_p, /* ptr to the packet data */ unsigned long length); /* length of the packet */ Parameters handle A handle as obtained with a DPIconnect_to_agent_xxxx() call. message_p A pointer to the buffer containing the DPI packet to be sent. length The length of the DPI packet to be sent. The DPI_PACKET_LEN macro is a useful macro to calculate the length. Return Values If successful, a zero (DPI_RC_noError) is returned. If failure, a negative integer is returned. It indicates the kind of error that occurred. See Return Codes from DPI Transport-Related Functions for a list of possible error codes. Description The DPIsend_packet_to_agent() function is used at the subagent side to send a DPI packet to the DPI capable SNMP agent. Examples #include int handle; unsigned char *pack_p; handle = DPIconnect_to_agent_TCP("localhost", "public"); if (handle < 0) { printf("Error %d from connect\n",handle); exit(1); } /* endif */ pack_p = mkDPIopen("1.3.6.1.2.3.4.5", "Sample DPI subagent" 0L,2L,,DPI_NATIVE_CSET, 0,(char *)0); if (pack_p) { rc = DPIsend_packet_to_agent(handle,pack_p, DPI_PACKET_LEN(pack_p)); if (rc) { printf("Error %d from await packet\n"); exit(1); } /* endif */ } else { printf("Can't make DPI OPEN packet\n"); exit(1); } /* endif */ /* await the response */ Related Information The DPIconnect_to_agent_TCP() Function The DPI_PACKET_LEN() Macro The mkDPIopen() Function ═══ 11.4.7. The lookup_host() Function ═══ Syntax #include unsigned long lookup_host( /* find IP address in network */ char *hostname_p); /* byte order for this host */ Parameters hostname_p A pointer to a NULL terminated character string representing the host name or IP address in dot notation of the host where the DPI capable SNMP agent is running. Return Values If successful, the IP address is returned in network byte order, so it is ready to be used in a sockaddr_in structure. If failure, a value of 0 is returned. Description The lookup_host() function is used to obtain the IP address in network byte order of a host or IP address in dot notation. The DPI subagent programmer only needs to use this function to code the connection setup and send or await the packet. The programmer then obtains the DPI port number, finds the IP address of the agent with the lookup_host() function and then sets up a socket for communication. This function is implicitly executed by the DPIconnect_to_agent_TCP() function, which is the function that the DPI subagent programmer would normally use. So the lookup_host() function is normally not used by the DPI subagent programmer. Examples #include #include /* other include files for BSD sockets and such */ int handle; unsigned char *pack_p; long int dpi_port; int fd; struct sockaddr_in s,t; /* source and target */ dpi_port = query_DPI_port("localhost", /* get DPI port number */ "public", /* for TCP, local host */ dpiPortForTCP); if (dpi_port < 0) exit(1); /* error if negative */ host_addr = lookup_host("localhost"); /* find target IP addr */ if (host_addr == 0) exit(1); /* unknown, that's it */ fd = socket(AF_INET,SOCK_STREAM,0); /* create a TCP socket */ if (fd < 0) exit(1); /* failure to do so */ memset(&s,0,sizeof(s)); s.sin_family = AF_INET; /* set AF_INET family */ s.sin_port = 0; /* give us any port, */ s.sin_addr.s_addr = htonl(INADDR_ANY); /* any local IPaddress */ rc = bind(fd,(struct sockaddr *)s, /* bind our socket(fd) */ sizeof(s_sock)); /* defined in s socket */ if (rc < 0) exit(1); /* failure, so exit */ memset(&d,0,sizeof(d)); d.sin_family = AF_INET; /* set AF_INET family */ d.sin_port = htons(dpi_port); /* set requested port */ d.sin_addr.s_addr = host_addr; /* destination IP addr */ /* network byte order */ rc = connect(fd,(struct sockaddr *)d, /* connect to target */ sizeof(d)); /* based on d sock */ if (rc < 0) exit(1); /* failed, exit */ /* now we have a socket on which to send/receive DPI packets */ Related Information The query_DPI_port() Function The DPIconnect_to_agent_TCP() Function ═══ 11.4.8. The query_DPI_port() Function ═══ Syntax #include long int query_DPI_port( /* Query (GET) SNMP_DPI port */ char *hostname_p, /* target hostname/IPaddress */ char *community_p, /* communityname for GET */ int porttype); /* port type, one of: */ /* dpiPortForTCP */ /* dpiPortForUDP */ Parameters hostname_p A pointer to a NULL terminated character string representing the host name or IP address in dot notation of the host where the DPI capable SNMP agent is running. community_p A pointer to a NULL terminated character string representing the community name that is required to obtain the dpiPort from the SNMP agent via an SNMP GET request. porttype The dpiPort object for a specific port type that you want to obtain. Currently there are two types: one for a TCP port and one for a UDP port. The snmp_dpi.h include file has two #define statements for these DPI port types: #define dpiPortForTCP 1 #define dpiPortForUDP 2 At this time, the dpiPORTForUDP port type is not supported. If you use it, the return value is set to -1, which indicates a failure. Return Values If successful, the DPI port number for the specified protocol, TCP or UDP, is returned. If failure, a value of -1 is returned. Description The query_DPI_port function is used to obtain the port number on which the DPI capable SNMP agent at the specified host is listening for connections (TCP) or packets (UDP). The DPI subagent programmer only needs to use this function to code the connection setup and send or await the packet. The programmer then obtains the DPI port number, finds the IP address of the agent with the lookup_host() function and then sets up a socket for communication. This function is implicitly executed by the DPIconnect_to_agent_TCP() function, which is the function that the DPI subagent programmer would normally use. So the query_DPI_port() function is normally not used by the DPI subagent programmer. Examples #include #include /* other include files for BSD sockets and such */ int handle; unsigned char *pack_p; long int dpi_port; int fd; struct sockaddr_in s,t; /* source and target */ dpi_port = query_DPI_port("localhost", /* get DPI port number */ "public", /* for TCP, local host */ dpiPortForTCP); if (dpi_port < 0) exit(1); /* error if negative */ host_addr = lookup_host("localhost"); /* find target IP addr */ if (host_addr == 0) exit(1); /* unknown, that's it */ fd = socket(AF_INET,SOCK_STREAM,0); /* create a TCP socket */ if (fd < 0) exit(1); /* failure to do so */ memset(&s,0,sizeof(s)); s.sin_family = AF_INET; /* set AF_INET family */ s.sin_port = 0; /* give us any port, */ s.sin_addr.s_addr = htonl(INADDR_ANY); /* any local IPaddress */ rc = bind(fd,(struct sockaddr *)s, /* bind our socket(fd) */ sizeof(s_sock)); /* defined in s socket */ if (rc < 0) exit(1); /* failure, so exit */ memset(&d,0,sizeof(d)); d.sin_family = AF_INET; /* set AF_INET family */ d.sin_port = htons(dpi_port); /* set requested port */ d.sin_addr.s_addr = host_addr; /* destination IP addr */ /* network byte order */ rc = connect(fd,(struct sockaddr *)d, /* connect to target */ sizeof(d)); /* based on d sock */ if (rc < 0) exit(1); /* failed, exit */ /* now we have a socket on which to send/receive DPI packets */ Related Information The lookup_host() Function The DPIconnect_to_agent_TCP() Function ═══ 11.5. DPI Structures ═══ This section describes each data structure that is used in the SNMP DPI API. Topics The snmp_dpi_bulk_packet Structure The snmp_dpi_close_packet Structure The snmp_dpi_get_packet Structure The snmp_dpi_next_packet Structure The snmp_dpi_hdr Structure The snmp_dpi_resp_packet Structure The snmp_dpi_set_packet Structure The snmp_dpi_ureg_packet Structure The snmp_dpi_u64 Structure ═══ 11.5.1. The snmp_dpi_bulk_packet Structure ═══ Structure Definition struct dpi_bulk_packet { long int non_repeaters; /* count of non-repeaters*/ long int max_repetitions; /* max repeaters */ struct dpi_next_packet *varBind_p; /* ptr to varBinds, chain*/ /* of dpi_next_packets */ }; typedef struct dpi_bulk_packet snmp_dpi_bulk_packet; #define snmp_dpi_bulk_packet_NULL_p ((snmp_dpi_bulk_packet *)0) Note: This structure is supported only in SNMP Version 2. Structure Members non_repeaters The number of varBinds in the chain of dpi_next_packet structures that are to be treated as a single GETNEXT. max_repetitions The maximum number of repetitions for the remaining set of varBinds in dpi_next_packet structures treated as a single GETNEXT. varBind_p The pointer to the first varBind in the chain of dpi_next_packet structures. Description The snmp_dpi_bulk_packet structure represents a parse tree for a DPI GETBULK packet. At the subagent side, the snmp_dpi_bulk_packet structure is normally created as a result of a call to pDPIpacket(). This is the case if the DPI packet is of type SNMP_DPI_GETBULK. The snmp_dpi_hdr structure then contains a pointer to an snmp_dpi_bulk_packet structure, which in turn has a pointer to a chain of one or more snmp_dpi_next_packet structures. The DPI subagent programmer uses this structure to find out which variables instances are to be returned in a DPI RESPONSE. Related Information The pDPIpacket() Function The snmp_dpi_hdr Structure The snmp_dpi_next_packet Structure ═══ 11.5.2. The snmp_dpi_close_packet Structure ═══ Structure Definition struct dpi_close_packet { char reason_code; /* reason for closing */ }; typedef struct dpi_close_packet snmp_dpi_close_packet; #define snmp_dpi_close_packet_NULL_p ((snmp_dpi_close_packet*)0) Structure Members reason_code The reason for the close. See DPI CLOSE Reason Codes for a list of valid reason codes. Description The snmp_dpi_close_packet structure represents a parse tree for a DPI CLOSE packet. The snmp_dpi_close_packet structure may be created as a result of a call to pDPIpacket(). This is the case if the DPI packet is of type SNMP_DPI_CLOSE. The snmp_dpi_hdr structure then contains a pointer to a snmp_dpi_close_packet structure. An snmp_dpi_close_packet_structure is also created as a result of a mkDPIclose() call, but the programmer never sees the structure since mkDPIclose() immediately creates a serialized DPI packet from it and then frees the structure. It is recommended that DPI subagent programmer uses mkDPIclose() to create a DPI CLOSE packet. Related Information The pDPIpacket() Function The mkDPIclose() Function The snmp_dpi_hdr Structure ═══ 11.5.3. The snmp_dpi_get_packet Structure ═══ Structure Definition struct dpi_get_packet { char *object_p; /* ptr to OID string */ char *group_p; /* ptr to sub-tree(group)*/ char *instance_p; /* ptr to rest of OID */ struct dpi_get_packet *next_p; /* ptr to next in chain */ }; typedef struct dpi_get_packet snmp_dpi_get_packet; #define snmp_dpi_get_packet_NULL_p ((snmp_dpi_get_packet *)0) Structure Members object_p A pointer to a NULL terminated character string that represents the full OBJECT IDENTIFIER of the variable instance that is being accessed. It basically is a concatenation of the fields group_p and instance_p. Using this field is not recommended because it is only included for DPI Version 1 compatibility and it maybe withdrawn in a later version. group_p A pointer to a NULL terminated character string that represents the registered sub-tree that caused this GET request to be passed to this DPI subagent. The sub-tree must have a trailing dot. instance_p A pointer to a NULL terminated character string that represents the rest which is the piece following the sub-tree part of the OBJECT IDENTIFIER of the variable instance being accessed. Use of the term instance_p here should not be confused with an OBJECT instance because this string may consist of a piece of the OBJECT IDENTIFIER plus the INSTANCE IDENTIFIER. next_p A pointer to a possible next snmp_dpi_get_packet structure. If this next field contains the NULL pointer, this is the end of the chain. Description The snmp_dpi_get_packet structure represents a parse tree for a DPI GET packet. At the subagent side, the snmp_dpi_get_packet structure is normally created as a result of a call to pDPIpacket(). This is the case if the DPI packet is of type SNMP_DPI_GET. The snmp_dpi_hdr structure then contains a pointer to a chain of one or more snmp_dpi_get_packet structures. The DPI subagent programmer uses this structure to find out which variables instances are to be returned in a DPI RESPONSE. Related Information The pDPIpacket() Function The snmp_dpi_hdr Structure ═══ 11.5.4. The snmp_dpi_hdr Structure ═══ Structure Definition struct snmp_dpi_hdr { unsigned char proto_major; /* always 2: SNMP_DPI_PROTOCOL*/ unsigned char proto_version; /* DPI version */ unsigned char proto_release; /* DPI release */ unsigned short packet_id; /* 16-bit, DPI packet ID */ unsigned char packet_type; /* DPI packet type */ union { snmp_dpi_reg_packet *reg_p; snmp_dpi_ureg_packet *ureg_p; snmp_dpi_get_packet *get_p; snmp_dpi_next_packet *next_p; snmp_dpi_next_packet *bulk_p; snmp_dpi_set_packet *set_p; snmp_dpi_resp_packet *resp_p; snmp_dpi_trap_packet *trap_p; snmp_dpi_open_packet *open_p; snmp_dpi_close_packet *close_p; unsigned char *any_p; } data_u; }; typedef struct snmp_dpi_hdr snmp_dpi_hdr; #define snmp_dpi_hdr_NULL_p ((snmp_dpi_hdr *)0) Structure Members proto_major The major protocol. For SNMP DPI, it is always 2. proto_version The DPI version. proto_release The DPI release. packet_id This field contains the packet ID of the DPI packet. When you create a response to a request, the packet ID must be the same as that of the request. This is taken care of if you use the mkDPIresponse() function. packet_type The type of DPI packet (parse tree) which you are dealing with. See DPI Packet Types for a list of currently defined DPI packet types data_u A union of pointers to the different types of data structures that are created based on the packet_type field. The pointers themselves have names that are self-explanatory. The fields proto_major, proto_version, proto_release, and packet_id are basically for DPI internal use. So the DPI programmer normally does not need to be concerned about them. If you work with an unreliable DPI "connection", such as UDP, you may want to use the packet_id field to ensure you are handling the correct packet. Description The snmp_dpi_hdr structure is the anchor of a DPI parse tree. At the subagent side, the snmp_dpi_hdr structure is normally created as a result of a call to pDPIpacket(). The DPI subagent programmer uses this structure to interrogate packets. Depending on the packet_type, the pointer to the chain of one or more packet_type specific structures that contain the actual packet data can be picked. The storage for a DPI parse tree is always dynamically allocated. It is the responsibility of the caller to free this parse tree when it is no longer needed. You can use the fDPIparse() function to do that. Note: Some mkDPIxxxx functions do free the parse tree that is passed to them. An example is the mkDPIpacket() function. Related Information The fDPIparse() Function The pDPIpacket() Function The snmp_dpi_close_packet Structure The snmp_dpi_get_packet Structure The snmp_dpi_next_packet Structure The snmp_dpi_bulk_packet Structure The snmp_dpi_resp_packet Structure The snmp_dpi_set_packet Structure The snmp_dpi_ureg_packet Structure ═══ 11.5.5. The snmp_dpi_next_packet Structure ═══ Structure Definition struct dpi_next_packet { char *object_p; /* ptr to OID (string) */ char *group_p; /* ptr to sub-tree(group)*/ char *instance_p;/* ptr to rest of OID */ struct dpi_next_packet *next_p; /* ptr to next in chain */ }; typedef struct dpi_next_packet snmp_dpi_next_packet; #define snmp_dpi_next_packet_NULL_p ((snmp_dpi_next_packet *)0) Structure Members object_p A pointer to a NULL terminated character string that represents the full OBJECT IDENTIFIER of the variable instance that is being accessed. It basically is a concatenation of the fields group_p and instance_p. Using this field is not recommended because it is only included for DPI Version 1 compatibility and it maybe withdrawn in a later version. group_p A pointer to a NULL terminated character string that represents the registered sub-tree that caused this GETNEXT request to be passed to this DPI subagent. This sub-tree must have a trailing dot. instance_p A pointer to a NULL terminated character string that represents the rest which is the piece following the sub-tree part of the OBJECT IDENTIFIER of the variable instance being accessed. Use of the term instance_p here should not be confused with an OBJECT instance because this string may consist of a piece of the OBJECT IDENTIFIER plus the INSTANCE IDENTIFIER. next_p A pointer to a possible next snmp_dpi_get_packet structure. If this next field contains the NULL pointer, this is the end of the chain. Description The snmp_dpi_next_packet structure represents a parse tree for a DPI GETNEXT packet. At the subagent side, the snmp_dpi_next_packet structure is normally created as a result of a call to pDPIpacket(). This is the case if the DPI packet is of type SNMP_DPI_GETNEXT. The snmp_dpi_hdr structure then contains a pointer to a chain of one or more snmp_dpi_next_packet structures. The DPI subagent programmer uses this structure to find out which variables instances are to be returned in a DPI RESPONSE. Related Information The pDPIpacket() Function The snmp_dpi_hdr Structure ═══ 11.5.6. The snmp_dpi_resp_packet Structure ═══ Structure Definition struct dpi_resp_packet { char error_code; /* like: SNMP_ERROR_xxx */ unsigned long int error_index;/* 1st varBind in error */ #define resp_priority error_index /* if respons to register*/ struct dpi_set_packet *varBind_p; /* ptr to varBind, chain */ /* of dpi_set_packets */ }; typedef struct dpi_resp_packet snmp_dpi_resp_packet; #define snmp_dpi_resp_packet_NULL_p ((snmp_dpi_resp_packet *)0) Structure Members error_code The return code or the error code. See DPI RESPONSE Error Codes for a list of valid codes. error_index Specifies the first varBind is in error. Counting starts at 1 for the first varBind. This field should be zero (SNMP_ERROR_noError) if there is no error. resp_priority This is a redefinition of the error_index field. If the response is a response to a DPI REGISTER request and the error_code is equal to SNMP_ERROR_DPI_noError or SNMP_ERROR_DPI_higherPriorityRegistered, then this field contains the priority that was actually assigned. Otherwise, this field is set to zero for responses to a DPI REGISTER.. varBind_p A pointer to the chain of one or more snmp_dpi_set_structures, representing varBinds of the response. This field contains a NULL pointer if there are no varBinds in the response. Description The snmp_dpi_resp_packet structure represents a parse tree for a DPI RESPONSE packet. The snmp_dpi_resp_packet structure is normally created as a result of a call to pDPIpacket(). This is the case if the DPI packet is of type SNMP_DPI_RESPONSE. The snmp_dpi_hdr structure then contains a pointer to a snmp_dpi_resp_packet structure. At the DPI subagent side, a DPI RESPONSE should only be expected at initialization and termination time when the subagent has issued a DPI OPEN, DPI REGISTER or DPI UNREGISTER request. The DPI programmer is advised to use the mkDPIresponse() function to prepare a DPI RESPONSE packet. Related Information The pDPIpacket() Function The mkDPIresponse() Function The snmp_dpi_set_packet Structure The snmp_dpi_hdr Structure ═══ 11.5.7. The snmp_dpi_set_packet Structure ═══ Structure Definition struct dpi_set_packet { char *object_p; /* ptr to Object ID (string) */ char *group_p; /* ptr to sub-tree (group) */ char *instance_p; /* ptr to rest of OID */ unsigned char value_type; /* value type: SNMP_TYPE_xxx */ unsigned short value_len; /* value length */ char *value_p; /* ptr to the value itself */ struct dpi_set_packet *next_p; /* ptr to next in chain */ }; typedef struct dpi_set_packet snmp_dpi_set_packet; #define snmp_dpi_set_packet_NULL_p ((snmp_dpi_set_packet *)0) Structure Members object_p A pointer to a NULL terminated character string that represents the full OBJECT IDENTIFIER of the variable instance that is being accessed. It basically is a concatenation of the fields group_p and instance_p. Using this field is not recommended because it is only included for DPI Version 1 compatibility and it maybe withdrawn in a later version. group_p A pointer to a NULL terminated character string that represents the registered sub-tree that caused this SET, COMMIT, or UNDO request to be passed to this DPI subagent. The sub-tree must have a trailing dot. instance_p A pointer to a NULL terminated character string that represents the rest, which is the piece following the sub-tree part, of the OBJECT IDENTIFIER of the variable instance being accessed. Use of the term instance_p here should not be confused with an OBJECT instance because this string may consist of a piece of the OBJECT IDENTIFIER plus the INSTANCE IDENTIFIER. value_type The type of the value. See DPI SNMP Value Types for a list of currently defined value types. value_len This is an unsigned 16-bit integer that specifies the length in octets of the value pointed to by the value field. The length may be zero if the value if of type SNMP_TYPE_NULL. value_p A pointer to the actual value. This field may contain a NULL pointer if the value if of type SNMP_TYPE_NULL. See Value Representation for information on how the data is represented for the various value types. next_p A pointer to a possible next snmp_dpi_set_packet structure. If this next field contains the NULL pointer, this is the end of the chain. Description The snmp_dpi_set_packet structure represents a parse tree for a DPI SET request. The snmp_dpi_set_packet structure may be created as a result of a call to pDPIpacket(). This is the case if the DPI packet is of type SNMP_DPI_SET, SNMP_DPI_COMMIT or SNMP_DPI_UNDO. The snmp_dpi_hdr structure then contains a pointer to a chain of one or more snmp_dpi_set_packet structures. This structure can also be created with a mkDPIset() call, which is typically used when preparing varBinds for a DPI RESPONSE packet. Related Information The pDPIpacket() Function The mkDPIset() Function DPI SNMP Value Types Value Representation The snmp_dpi_hdr Structure ═══ 11.5.8. The snmp_dpi_ureg_packet Structure ═══ Structure Definition struct dpi_ureg_packet { char reason_code;/* reason for unregister */ char *group_p; /* ptr to sub-tree(group)*/ struct dpi_reg_packet *next_p; /* ptr to next in chain */ }; typedef struct dpi_ureg_packet snmp_dpi_ureg_packet; #define snmp_dpi_ureg_packet_NULL_p ((snmp_dpi_ureg_packet *)0) Structure Members reason_code The reason for the unregister. See DPI UNREGISTER Reason Codes for a list of the currently defined reason codes. group_p A pointer to a NULL terminated character string that represents the sub-tree to be unregistered. This sub-tree must have a trailing dot. next_p A pointer to a possible next snmp_dpi_ureg_packet structure. If this next field contains the NULL pointer, this is the end of the chain. Currently we do not support multiple unregister requests in one DPI packet, so this field should always be zero. Description The snmp_dpi_ureg_packet structure represents a parse tree for a DPI UNREGISTER request. The snmp_dpi_ureg_packet structure is normally created as a result of a call to pDPIpacket(). This is the case if the DPI packet is of type SNMP_DPI_UNREGISTER. The snmp_dpi_hdr structure then contains a pointer to a snmp_dpi_ureg_packet structure. The DPI programmer is advised to use the mkDPIunregister() function to create a DPI UNREGISTER packet. Related Information The pDPIpacket() Function The mkDPIunregister() Function The snmp_dpi_hdr Structure ═══ 11.5.9. The snmp_dpi_u64 Structure ═══ Structure Definition struct snmp_dpi_u64 { /* for unsigned 64-bit int */ unsigned long high; /* - high order 32 bits */ unsigned long low; /* - low order 32 bits */ }; typedef struct snmp_dpi_u64 snmp_dpi_u64; Note: This structure is supported only in SNMP Version 2. Structure Members high The high order, most significant, 32 bits low The low order, least significant, 32 bits Description The snmp_dpi_u64 structure represents an unsigned 64-bit integer as need for values with a type of SNMP_TYPE_Counter64. The snmp_dpi_u64 structure may be created as a result of a call to pDPIpacket(). This is the case if the DPI packet is of type SNMP_DPI_SET and one of the values has a type of SNMP_TYPE_Counter64. The value_p pointer of the snmp_dpi_set_packet structure will then point to an snmp_dpi_u64 structure. The DPI programmer must also use an snmp_dpi_u64 structure as the parameter to a mkDPIset() call if you want to create a value of type SNMP_TYPE_Counter64. Related Information The pDPIpacket() Function The snmp_dpi_set_packet Structure DPI SNMP Value Types Value Representation ═══ 11.6. Character Set Selection ═══ Based on the character set used on the platform where the agent and subagent are running, you will encounter one of the following three scenarios:  Both run on an ASCII based platform. In reality a lot of platforms use the ASCII character set. For those platforms, just use the native character set. In that case, the native character set is ASCII.  Both run on the same non-ASCII based platform. It is expected that the agent and the subagent normally run on the same machine or at least on the same platform. In that case, it is easiest to use the native character set for data that is represented as strings. If such native character set is not the ASCII character set, the agent must translate from ASCII to the native character set (and vice versa) as needed.  One runs on ASCII based platform, the other on a non-ASCII based platform. If the agent and subagent each run on their own platform and those platforms use different native character sets; for example, IBM OS/2 uses ASCII and IBM MVS uses EBCDIC, you must select the ASCII character set, so that you both know exactly how to represent string-based data that is being send back and forth. The entity that is not ASCII based must do the translation from ASCII to the native character set (and vice versa) as needed. When the DPI subagent sends a DPI OPEN packet, it must specify the character set that it wants to use. The subagent here needs to know or determine in an implementation dependent manner if the agent is running on a system with the same character set as the subagent. If you connect to the agent at loopback, localhost, or your own machine, you might assume that you are using the same character set. As long as you are just using OS/2 on an Intel based processor, it does not matter. Always use the native character set, which is ASCII. The subagent has two choices: DPI_NATIVE_CSET Specifies that you want to use the native character set of the platform on which the agent that you connect to is running. DPI_ASCII_CSET Specifies that you want to use the ASCII character set. The agent will translate between ASCII and the native character set as required. If the subagent is on a non-ASCII based platform, it may have to translate also. The DPI packets have a number of fields that are represented as strings. The fields that must be represented in the selected character set are:  The null terminated string pointed to by the description_p, enterprise_p, group_p, instance_p, and oid_p parameters in the various mkDPIxxxx(...) functions.  The string pointed to by the value_p parameter in the mkDPIset(...) function, that is if the value_type parameter specifies that the value is an SNMP_TYPE_DisplayString or an SNMP_TYPE_OBJECT_IDENTIFIER.  The null terminated string pointed to by the description_p, enterprise_p, group_p, instance_p, and oid_p pointers in the various snmp_dpi_xxxx_packet structures.  The string pointed to by the value_p pointer in the snmp_dpi_set_packet structure, that is if the value_type field specifies that the value is an SNMP_TYPE_DisplayString or an SNMP_TYPE_OBJECT_IDENTIFIER. Related Information The mkDPIopen() Function ═══ 11.7. Constants, Values, Return Codes, and Include File ═══ This section describes all the constants and names for values as they are defined in the snmp_dpi.h include file. Topics DPI CLOSE Reason Codes DPI Packet Types DPI RESPONSE Error Codes DPI UNREGISTER Reason Codes DPI SNMP Value Types Value Representation Value Ranges and Limits Return Codes from DPI Transport-Related Functions ═══ 11.7.1. DPI CLOSE Reason Codes ═══ The currently defined DPI CLOSE reason codes as defined in the snmp_dpi.h include file are: #define SNMP_CLOSE_otherReason 1 #define SNMP_CLOSE_goingDown 2 #define SNMP_CLOSE_unsupportedVersion 3 #define SNMP_CLOSE_protocolError 4 #define SNMP_CLOSE_authenticationFailure 5 #define SNMP_CLOSE_byManager 6 #define SNMP_CLOSE_timeout 7 #define SNMP_CLOSE_openError 8 These codes are used in the reason_code parameter for the mkDPIclose() function and in the reason_code field in the snmp_dpi_close_packet structure. Related Information The snmp_dpi_close_packet Structure The mkDPIclose() Function ═══ 11.7.2. DPI Packet Types ═══ The currently defined DPI packet types as defined in the snmp_dpi.h include file are: #define SNMP_DPI_GET 1 #define SNMP_DPI_GET_NEXT 2 /* old DPI 1.x style */ #define SNMP_DPI_GETNEXT 2 #define SNMP_DPI_SET 3 #define SNMP_DPI_TRAP 4 #define SNMP_DPI_RESPONSE 5 #define SNMP_DPI_REGISTER 6 #define SNMP_DPI_UNREGISTER 7 #define SNMP_DPI_OPEN 8 #define SNMP_DPI_CLOSE 9 #define SNMP_DPI_COMMIT 10 #define SNMP_DPI_UNDO 11 #define SNMP_DPI_GETBULK 12 #define SNMP_DPI_TRAPV2 13 /* reserved, not .... */ #define SNMP_DPI_INFORM 14 /* reserved, implemented */ #define SNMP_DPI_ARE_YOU_THERE 15 These packet types are used in the type parameter for the packet_type field in the snmp_dpi_hdr structure. Related Information The snmp_dpi_hdr Structure ═══ 11.7.3. DPI RESPONSE Error Codes ═══ In case of an error on an SNMP request like GET, GETNEXT, GETBULK, SET, COMMIT, or UNDO, the RESPONSE can have one of these currently defined error codes. They are defined in the snmp_dpi.h include file: #define SNMP_ERROR_noError 0 #define SNMP_ERROR_tooBig 1 #define SNMP_ERROR_noSuchName 2 #define SNMP_ERROR_badValue 3 #define SNMP_ERROR_readOnly 4 #define SNMP_ERROR_genErr 5 #define SNMP_ERROR_noAccess 6 #define SNMP_ERROR_wrongType 7 #define SNMP_ERROR_wrongLength 8 #define SNMP_ERROR_wrongEncoding 9 #define SNMP_ERROR_wrongValue 10 #define SNMP_ERROR_noCreation 11 #define SNMP_ERROR_inconsistentValue 12 #define SNMP_ERROR_resourceUnavailable 13 #define SNMP_ERROR_commitFailed 14 #define SNMP_ERROR_undoFailed 15 #define SNMP_ERROR_authorizationError 16 #define SNMP_ERROR_notWritable 17 #define SNMP_ERROR_inconsistentName 18 In case of an error on a DPI only request (OPEN, REGISTER, UNREGISTER, ARE_YOU_THERE), the RESPONSE can have one of these currently defined error codes. They are defined in the snmp_dpi.h include file: #define SNMP_ERROR_DPI_noError 0 #define SNMP_ERROR_DPI_otherError 101 #define SNMP_ERROR_DPI_notFound 102 #define SNMP_ERROR_DPI_alreadyRegistered 103 #define SNMP_ERROR_DPI_higherPriorityRegistered 104 #define SNMP_ERROR_DPI_mustOpenFirst 105 #define SNMP_ERROR_DPI_notAuthorized 106 #define SNMP_ERROR_DPI_viewSelectionNotSupported 107 #define SNMP_ERROR_DPI_getBulkSelectionNotSupported 108 #define SNMP_ERROR_DPI_duplicateSubAgentIdentifier 109 #define SNMP_ERROR_DPI_invalidDisplayString 110 #define SNMP_ERROR_DPI_characterSetSelectionNotSupported 111 These codes are used in the error_code parameter for the mkDPIresponse() function and in the error_code field in the snmp_dpi_resp_packet structure. Related Information The snmp_dpi_resp_packet Structure The mkDPIresponse() Function ═══ 11.7.4. DPI UNREGISTER Reason Codes ═══ These are the currently defined DPI UNREGISTER reason codes. They are define in the snmp_dpi.h include file: #define SNMP_UNREGISTER_otherReason 1 #define SNMP_UNREGISTER_goingDown 2 #define SNMP_UNREGISTER_justUnregister 3 #define SNMP_UNREGISTER_newRegistration 4 #define SNMP_UNREGISTER_higherPriorityRegistered 5 #define SNMP_UNREGISTER_byManager 6 #define SNMP_UNREGISTER_timeout 7 These codes are used in the reason_code parameter for the mkDPIunregister() function and in the reason_code field in the snmp_dpi_ureg_packet structure. Related Information The snmp_dpi_ureg_packet Structure The mkDPIunregister() Function ═══ 11.7.5. DPI SNMP Value Types ═══ These are the currently defined value types as defined in the snmp_dpi.h include file: #define SNMP_TYPE_MASK 0x7f /* mask to isolate type*/ #define SNMP_TYPE_Integer32 (128|1) /* 32-bit INTEGER */ #define SNMP_TYPE_OCTET_STRING 2 /* OCTET STRING */ #define SNMP_TYPE_OBJECT_IDENTIFIER 3 /* OBJECT IDENTIFIER */ #define SNMP_TYPE_NULL 4 /* NULL, no value */ #define SNMP_TYPE_IpAddress 5 /* IMPLICIT OCTETSTRING*/ #define SNMP_TYPE_Counter32 (128|6) /* 32-bit Counter */ #define SNMP_TYPE_Gauge32 (128|7) /* 32-bit Gauge */ #define SNMP_TYPE_TimeTicks (128|8) /* 32-bit TimeTicks in */ /* hundredths of a sec */ #define SNMP_TYPE_DisplayString 9 /* DisplayString (TC) */ #define SNMP_TYPE_BIT_STRING 10 /* BIT STRING */ #define SNMP_TYPE_NsapAddress 11 /* IMPLICIT OCTETSTRING*/ #define SNMP_TYPE_UInteger32 (128|12) /* 32-bit INTEGER */ #define SNMP_TYPE_Counter64 13 /* 64-bit Counter */ #define SNMP_TYPE_Opaque 14 /* IMPLICIT OCTETSTRING*/ #define SNMP_TYPE_noSuchObject 15 /* IMPLICIT NULL */ #define SNMP_TYPE_noSuchInstance 16 /* IMPLICIT NULL */ #define SNMP_TYPE_endOfMibView 17 /* IMPLICIT NULL */ These value types are used in the value_type parameter for the mkDPIset() function and in the value_type field in the snmp_dpi_set_packet structure. Related Information The snmp_dpi_set_packet Structure The mkDPIset() Function Value Representation Value Ranges and Limits ═══ 11.7.6. Value Representation ═══ Values in the snmp_dpi_set_packet structure are represented as follows:  32-bit integers are defined as long int or unsigned long int. We assume that a long int is 4 bytes.  64-bit integers are represented as an snmp_dpi_u64. We only deal with unsigned 64 bit integers in SNMP. In a structure that has two fields, the high order piece and the low order piece, each is of type unsigned long int. We assume these are 4-bytes.  Object Identifiers are NULL terminated strings in the selected character set, representing the OID in ASN.1 dotted notation. The length includes the terminating NULL. An ASCII example: '312e332e362e312e322e312e312e312e3000'h represents "1.3.6.1.2.1.1.1.0" which is sysDescr.0. An EBCDIC example: 'f14bf34bf64bf14bf24bf14bf14bf14bf000'h represents "1.3.6.1.2.1.1.1.0" which is sysDescr.0.  DisplayStrings are in the selected character set. The length specifies the length of the string. An ASCII example: '6162630d0a'h represents "abc\r\n", no NULL. An EBCDIC example: '8182830d25'h represents "abc\r\n", no NULL.  IpAddress, NsapAddress, and Opaque are implicit OCTET_STRING, so they are a sequence of octets/bytes. This means, for instance, that the IP address is in network byte order.  NULL has a zero length for the value, no value data, so a NULL pointer in the value_p field.  noSuchObject, noSuchInstance, and endOfMibView are implicit NULL and represented as such.  BIT_STRING is an OCTET_STRING of the form uubbbb...bb, where the first octet (uu) is 0x00-0x07 and indicates the number of unused bits in the last octet (bb). The bb octets represent the bit string itself, where bit zero (0) comes first and so on. Related Information Value Ranges and Limits ═══ 11.7.7. Value Ranges and Limits ═══ The following rules apply to object IDs in ASN.1 notation:  The object ID consists of 1 to 128 subIDs, which are separated by dots.  Each subID is a positive number. No negative numbers are allowed.  The value of each number cannot exceed 4294967295 (4,294,967,295). This value is 2 to the power of 32 minus 1.  The valid values of the first subID are: 0, 1, or 2.  If the first subID has a value of 0 or 1, the second subID can only have a value of 0 through 39. The following rules apply to DisplayString:  A DisplayString (Textual Convention) is basically an OCTET STRING in SNMP terms.  The maximum size of a DisplayString is 255 octets/bytes.  The octets of a DisplayString must belong to the ASCII NVT character set. This character set is not precisely defined, but commonly accepted to consist of all ASCII characters with a value in the range of 0-127 inclusive. A further limitation is that the CR (hex 13) must always be followed by either a LF (hex 0A) or a NUL (hex 00). More information on the DPI SNMP value types can be found in the SNMP SMI (Structure of Management Information) and SNMP TC (Textual Conventions) RFCs. At the time of this publication, these two RFCs are RFC1442 and RFC1443. ═══ 11.7.8. Return Codes from DPI Transport-Related Functions ═══ These are the currently defined values for the return codes from DPI transport-related functions. They are defined in the snmp_dpi.h include file: #define DPI_RC_OK 0 /* all OK, no error */ #define DPI_RC_NOK -1 /* some other error */ #define DPI_RC_NO_PORT -2 /* can't determine DPIport */ #define DPI_RC_NO_CONNECTION -3 /* no connection to DPIagent*/ #define DPI_RC_EOF -4 /* EOF receivd on connection*/ #define DPI_RC_IO_ERROR -5 /* Some I/O error on connect*/ #define DPI_RC_INVALID_HANDLE -6 /* unknown/invalid handle */ #define DPI_RC_TIMEOUT -7 /* timeout occurred */ #define DPI_RC_PACKET_TOO_LARGE -8 /* packed too large, dropped*/ These values are used as return codes for the transport-related DPI functions. Related Information The DPIconnect_to_agent_TCP() Function The DPIconnect_to_agent_SHM() Function The DPIawait_packet_from_agent() Function The DPIsend_packet_to_agent() Function ═══ 11.7.9. The snmp_dpi.h Include File ═══ Syntax #include Parameters None. Description The snmp_dpi.h include file defines the SNMP DPI API to the DPI subagent programmer. It has all the function proto-type statements, and it also has the definitions for the snmp_dpi structures. The same include file is used at the agent side, so you will see some definitions which are unique to the agent side. Also there may be other functions or prototypes of functions not implemented on OS/2. Therefore, you should only use the API as far as it is documented in this information. Related Information Macros, functions, structures, constants and values defined in the snmp_dpi.h include file are: The DPIawait_packet_from_agent() Function The DPIconnect_to_agent_TCP() Function The DPIdebug() Function The DPIdisconnect_from_agent() Function The DPI_PACKET_LEN() Macro The DPIsend_packet_to_agent() Function The fDPIparse() Function The fDPIset() Function The mkDPIAreYouThere() Function The mkDPIclose() Function The mkDPIopen() Function The mkDPIregister() Function The mkDPIresponse() Function The mkDPIset() Function The mkDPItrap() Function The mkDPIunregister() Function The pDPIpacket() Function The snmp_dpi_close_packet Structure The snmp_dpi_get_packet Structure The snmp_dpi_next_packet Structure The snmp_dpi_bulk_packet Structure The snmp_dpi_hdr Structure The lookup_host() Function The query_DPI_port() Function The snmp_dpi_resp_packet Structure The snmp_dpi_set_packet Structure The snmp_dpi_ureg_packet Structure DPI CLOSE Reason Codes DPI Packet Types DPI RESPONSE Error Codes DPI UNREGISTER Reason Codes DPI SNMP Value Types Character Set Selection ═══ 11.8. SNMP DPI API Version 1.1 Considerations ═══ The information presented in this section must be taken as guidelines and not exact procedures. Your specific implementation will vary from the guidelines presented. You can keep your existing DPI 1.1 subagent and communicate with a DPI capable agent that supports DPI 1.1 in addition to DPI 2.0. For example, the OS/2 agent for TCP/IP provides support for multiple versions of DPI, namely Version 1.0, Version 1.1 and Version 2.0. Normally you would compile your DPI 1.1 subagent with the DPI 1.1 include file and link-edit it with the provided DPI 1.1 level DPI32DLL.LIB. At run time, you then need access to the DPI32DLL.DLL. You can continue to do this until you are ready to migrate to DPI Version 2.0. In the snmp_dpi.h include file for DPI 2.0, you may find references to DPI 1.1 compatibility mode under control of compiler flags, such as: /DSNMP_DPI_VERSION=1 /DSNMP_DPI_RELEASE=0 /DSNMP_DPI_VERSION=1 /DSNMP_DPI_RELEASE=1 However, this compatibility mode is not provided with the TCP/IP product for OS/2. If you want to convert to DPI 2.0, which prepares you also for SNMP Version 2, you must make changes to your code. Name Changes A number of field names in the snmp_dpi_xxxx_packet structures have changed so that the names are now more consistent throughout the DPI code. The new names indicate if the value is a pointer (_p) or a union (_u). The names that have changed and that affect the subagent code are listed in the table below. ┌──────────────┬──────────────┬───────────────────────┐ │Old Name │New Name │Data Structure(XXXX) │ │ │ │ │ │group_id │group_p │getnext │ │ │ │ │ │object_id │object_p │get, getnext, set │ │ │ │ │ │value │value_p │set │ │ │ │ │ │type │value_type │set │ │ │ │ │ │next │next_p │set │ │ │ │ │ │enterprise │enterprise_p │trap │ │ │ │ │ │packet_body │data_u │dpi_hdr │ │ │ │ │ │dpi_get │get_p │hdr (packet_body) │ │ │ │ │ │dpi_getnext │next_p │hdr (packet_body) │ │ │ │ │ │dpi_set │set_p │hdr (packet_body) │ │ │ │ │ │dpi_trap │trap_p │hdr (packet_body) │ │ │ │ │ │ │ │ │ └──────────────┴──────────────┴───────────────────────┘ There is no clean approach to make this change transparent. You probably will have to change the names in your code. You may want to try a simple set of defines like: #define packet_body data_u #define dpi_get get_p #define dpi_set set_p #define dpi_next next_p #define dpi_response resp_p #define dpi_trap trap_p #define group_id group_p #define object_id object_p #define value value_p #define type value_type #define next next_p #define enterprise enterprise_p However, the names may conflict with other definitions that you have, in which case you must change your code. Related Information Migrating Your SNMP DPI Subagent to Version 2.0 ═══ 11.9. Migrating Your SNMP DPI Subagent to Version 2.0 ═══ The information presented in this section must be taken as guidelines and not exact procedures. Your specific implementation will vary from the guidelines presented. When you want to change your DPI 1.x based subagent code to the DPI Version 2.0 level use these guidelines for the required actions and the recommended actions. Required Actions  Add a mkDPIopen() call and send the created packet to the agent. This opens your "DPI connection" with the agent. Wait for the response and ensure that the open is accepted. You need to pass a subagent ID (Object Identifier) which must be a unique ASN.1 OID. See The mkDPIopen() Function for more information.  Change your mkDPIregister() calls and pass the parameters according to the new function prototype. You must also expect a RESPONSE to the REGISTER request. See The mkDPIregister() Function for more information.  Change mkDPIset() and/or mkDPIlist() calls to the new mkDPIset() call. Basically all mkDPIset() calls are now of the DPI 1.1 mkDPIlist() form. See The mkDPIset() Function for more information.  Change mkDPItrap() and mkDPItrape() calls to the new mkDPItrap() call. Basically all mkDPItrap() calls are now of the DPI 1.1 mkDPItrape() form. See The mkDPItrap() Function for more information.  Add code to recognize DPI RESPONSE packets, which should be expected as a result of OPEN, REGISTER, UNREGISTER requests.  Add code to expect and handle the DPI UNREGISTER packet from the agent. It may send such packets if an error occurs or if a higher priority subagent registers the same sub-tree as you have registered.  Add code to unregister your sub-tree(s) and close the "DPI connection" when you want to terminate the subagent. See The mkDPIunregister() Function and The mkDPIclose() Function for more information.  Change your code to use the new SNMP Version 2 error codes as defined in the snmp_dpi.h include file.  Change your code that handles a GET request. It should return a varBind with SNMP_TYPE_noSuchObject value or SNMP_TYPE_noSuchInstance value instead of an error SNMP_ERROR_noSuchName if the object or the instance do not exist. This is not considered an error any more. Therefore, you should return an SNMP_ERROR_noError with an error index of zero.  Change your code that handles a GETNEXT request. It should return a varBind with SNMP_TYPE_endOfMibView value instead of an error SNMP_ERROR_noSuchName if you reach the end of your MIB or sub-tree. This is not considered an error any more. Therefore, you should return an SNMP_ERROR_noError with an error index of zero.  Change your code that handles SET requests to follow the two phase SET/COMMIT scheme as described in SET Processing. See the sample handling of SET/COMMIT/UNDO in Processing a SET/COMMIT/UNDO Request. Recommended Actions  Do not reference the object ID pointer (object_p) in the snmp_dpi_xxxx_packet structures anymore. Instead start using the group_p and instance_p pointers. The object_p pointer may be removed in a future version of the DPI API.  Check Transport-Related DPI API Functions to see if you want to use those functions instead of using your own code for those functions.  Consider using more than 1 varBind per DPI packet. You can specify this on the REGISTER request. You must then be prepared to handle multiple varBinds per DPI packet. The varBinds are chained via the various snmp_dpi_xxxx_packet structures. See The mkDPIregister() Function for more information.  Consider specifying a time out when you issue a DPI OPEN or DPI REGISTER. See The mkDPIopen() Function and The mkDPIregister() Function for more information. ═══ 11.10. A DPI Subagent Example ═══ This is an example of a DPI subagent. The code is in the "dpi_samp.c" file. Note: The example code presented here was copied from the sample file at the time of the publication. For the most up-to-date example code, please see the \TCPIP\SAMPLES\DPI20 directory. There may be differences in the code presented and the code that is shipped with the product. Always use the code provided in the \TCPIP\SAMPLES\DPI20 directory as the authoritative sample code. Topics Overview of Subagent Processing Connecting to the Agent Registering a Sub-Tree with the Agent Processing Requests from the Agent Processing a GET Request Processing a GETNEXT Request Processing a SET/COMMIT/UNDO Request Processing an UNREGISTER Request Processing an CLOSE Request Generating a TRAP Related Information Subagent Programming Concepts ═══ 11.10.1. Overview of Subagent Processing ═══ This overview assumes that the subagent communicates with the agent over a TCP connection. Other connection implementations are possible and, in that case, the processing approach may be a bit different. We also take a simplistic approach in the sense that we will request the agent to send us at most one varBind per DPI packet, so we do not need to loop through a list of varBinds. Potentially, you may gain performance improvements if you allow for multiple varBinds per DPI packet on GET, GETNEXT, SET requests, but to do so, your code will have to loop through the varBind list and so it becomes somewhat more complicated. We assume that the DPI subagent programmer can handle that once you understand the basics of the DPI API. Here is the dpiSimple MIB definition as it is implemented by the sample code, which follows: DPISimple-MIB DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, OBJECT-TYPE, snmpModules, enterprises FROM SNMPv2-SMI DisplayString FROM SNMPv2-TC ibm OBJECT IDENTIFIER ::= { enterprises 2 } ibmDPI OBJECT IDENTIFIER ::= { ibm 2 } dpi20MIB OBJECT IDENTIFIER ::= { ibmDPI 1 } dpiSimpleMIB OBJECT IDENTIFIER ::= { dpi20MIB 5 } dpiSimpleInteger OBJECT-TYPE SYNTAX INTEGER ACCESS read-only STATUS mandatory DESCRIPTION "A sample integer32 value" ::= { dpiSimpleMIB 1 } dpiSimpleString OBJECT-TYPE SYNTAX DisplayString ACCESS read-write STATUS mandatory DESCRIPTION "A sample Display String" ::= { dpiSimpleMIB 2 } dpiSimpleCounter32 OBJECT-TYPE SYNTAX Counter -- Counter32 is SNMPv2 ACCESS read-only STATUS mandatory DESCRIPTION "A sample 32-bit counter" ::= { dpiSimpleMIB 3 } dpiSimpleCounter64 OBJECT-TYPE SYNTAX Counter64 -- Counter64 is SNMPv2, -- Not supported by SNMPv1 agents ACCESS read-only STATUS mandatory DESCRIPTION "A sample 64-bit counter" ::= { dpiSimpleMIB 4 } END To make the code more readable, we have defined the following names in our dpi_samp.c source file. #define DPI_SIMPLE_SUBAGENT "1.3.6.1.4.1.2.2.1.5" #define DPI_SIMPLE_MIB "1.3.6.1.4.1.2.2.1.5." #define DPI_SIMPLE_INTEGER "1.0" /* dpiSimpleInteger.0 */ #define DPI_SIMPLE_STRING "2.0" /* dpiSimpleString.0 */ #define DPI_SIMPLE_COUNTER32 "3.0" /* dpiSimpleCounter32.0 */ #define DPI_SIMPLE_COUNTER64 "4.0" /* dpiSimpleCounter64.0 */ In addition, we have defined the following variables as global variable in our dpi_samp.c source file. static int handle; /* handle has global scope */ static long int value1 = 5; #define value2_p cur_val_p /* writable object */ #define value2_len cur_val_len /* writable object */ static char *cur_val_p = (char *)0; static char *new_val_p = (char *)0; static char *old_val_p = (char *)0; static unsigned long cur_val_len = 0; static unsigned long new_val_len = 0; static unsigned long old_val_len = 0; static unsigned long value3 = 1; static snmp_dpi_u64 value4 = {0x80000000,1L}; ═══ 11.10.2. Connecting to the Agent ═══ Before a subagent can receive or send any DPI packets from/to the SNMP DPI capable agent, it must "connect" to the agent and identify itself to the agent. The following example code returns a response. We assume that there are no errors in the request, but proper code should do the checking for that. We do proper checking for lexicographic next object, but we do no checking for ULONG_MAX, or making sure that the instance ID is indeed valid (digits and dots). If we get to the end of our dpiSimpleMIB, we must return an endOfMibView as defined by the SNMP Version 2 rules.  A host name or IP address in dot notation that specifies where the agent is running. Often the name "loopback" or "localhost" can be used if the subagent runs on the same system as the agent.  A community name which is used to obtain the DPI TCP port from the agent. Internally that is done by sending a regular SNMP GET request to the agent. In an open environment, we probably can use the well know community name "public". The function returns a negative error code if an error occurs. If the connection setup is successful, it returns a handle which represents the connection and which we must use on subsequent calls to send or await DPI packets. The second step is to identify the subagent to the agent. This is done by making a DPI-OPEN packet, sending it to the agent, and then awaiting the response from the agent. The agent may accept or deny the OPEN request. Making a DPI-OPEN packet is done by calling mkDPIopen() which expects the following parameters:  A unique subagent identification (an Object Identifier).  A description which can be the NULL string ("").  Overall subagent timeout in seconds. The agent uses this value as a timeout value for a response when it sends a request to the subagent. The agent may have a maximum value for this timeout that will be used if you exceed it.  The maximum number of varBinds per DPI packet that the subagent is willing or is able to handle.  The character set we want to use. In most cases you want to use the native character set.  Length of a password. A zero means no password.  Pointer to the password or NULL if no password. It depends on the agent if subagents must specify a password to open up a connection. The function returns a pointer to a static buffer holding the DPI packet if successful. If it fails, it returns a NULL pointer. Once the DPI-OPEN packet has been created, you must send it to the agent. You can use the DPIsend_packet_to_agent() function which expects the following parameters:  The handle of a connection from DPIconnect_to_agent_TCP.  A pointer to the DPI packet from mkDPIopen.  The length of the packet. The snmp_dpi.h include file provides a macro DPI_PACKET_LEN that calculates the packet length of a DPI packet. This function returns DPI_RC_OK (value zero) if successful. Otherwise, an appropriate DPI_RC_xxxx error code as defined in snmp_dpi.h is returned. Now we must wait for a response to the DPI-OPEN. To await such a response, you call the DPIawait_packet_from_agent() function which expects the following parameters:  The handle of a connection from DPIconnect_to_agent_TCP.  A timeout in seconds, which is the maximum time to wait for response.  A pointer to a pointer, which will receive a pointer to a static buffer containing the awaited DPI packet. If the system fails to receive a packet, a NULL pointer is stored.  A pointer to a long integer (32-bit), which will receive the length of the awaited packet. If it fails, it will be set to zero. This function returns DPI_RC_OK (value zero) if successful. Otherwise, an appropriate DPI_RC_xxxx error code as defined in snmp_dpi.h is returned. The last step is to ensure that we received a DPI-RESPONSE back from the agent. If we did, then we must ensure that the agent accepted us as a valid subagent. This will be shown by the error_code field in the DPI response packet. The following example code establishes a connection and "opens" it by identifying yourself to the agent. #include /* DPI 2.0 API definitions */ static int handle; /* handle has global scope */ static void do_connect_and_open(char *hostname_p, char *community_p) { unsigned char *packet_p; int rc; unsigned long length; snmp_dpi_hdr *hdr_p; if (shared_mem) { /* if shared memory wanted */ handle = /* then (SHM) connect to */ DPIconnect_to_agent_SHM(1); /* always use 1 as queueID */ } else { handle = DPIconnect_to_agent_TCP( /* (TCP) connect to agent */ hostname_p, /* on this host */ community_p); /* snmp community name */ } /* endif */ if (handle < 0) exit(1); /* If it failed, exit */ packet_p = mkDPIopen( /* Make DPI-OPEN packet */ DPI_SIMPLE_SUBAGENT, /* Our identification */ "Simple DPI subAgent", /* description */ 10L, /* Our overall timeout */ 1L, /* max varBinds/packet */ DPI_NATIVE_CSET, /* native character set */ 0L, /* password length */ (unsigned char *)0); /* ptr to password */ if (!packet_p) exit(1); /* If it failed, exit */ rc = DPIsend_packet_to_agent( /* send OPEN packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p)); /* and this is its length */ if (rc != DPI_RC_OK) exit(1); /* If it failed, exit */ rc = DPIawait_packet_from_agent( /* wait for response */ handle, /* on this connection */ 10, /* timeout in seconds */ &packet_p, /* receives ptr to packet */ &length); /* receives packet length */ if (rc != DPI_RC_OK) exit(1); /* If it failed, exit */ hdr_p = pDPIpacket(packet_p); /* parse DPI packet */ if (hdr_p == snmp_dpi_hdr_NULL_p) /* If we fail to parse it */ exit(1); /* then exit */ if (hdr_p->packet_type != SNMP_DPI_RESPONSE) exit(1); rc = hdr_p->data_u. resp_p->error_code; if (rc != SNMP_ERROR_DPI_noError) exit(1); } /* end of do_connect_and_open() */ ═══ 11.10.3. Registering a Sub-Tree with the Agent ═══ After we have set up a connection to the agent and after we have identified ourselves, we must register one or more MIB sub-trees for which we want to be responsible to handle all SNMP requests. To do so, the subagent must create a DPI-REGISTER packet and send it to the agent. The agent will then send a response to indicate success or failure of the register request. To create a DPI-REGISTER packet, the subagent uses a call to the mkDPIregister() function, which expects these parameters:  A timeout value in seconds for this sub-tree. If you specify zero, your overall timeout value that was specified in DPI-OPEN is used. You can specify a different value if you expect longer processing time for a specific sub-tree.  A requested priority. Multiple subagents may register the same sub-tree at different priorities. For example, 0 is better than 1 and so on. The agent considers the subagent with the best priority to be the active subagent for the sub-tree. If you specify -1, you are asking for the best priority available. If you specify 0, you are asking for a better priority than any existing subagent may already have.  The MIB sub-tree which you want to control. You must specify this parameter with a trailing dot.  Your choice of GETBULK processing. You can ask the agent to map a GETBULK into multiple GETNEXT packets or to pass the GETBULK to you. The function returns a pointer to a static buffer holding the DPI packet if successful. If it fails, it returns a NULL pointer. Now we must send this DPI-REGISTER packet to the agent with the DPIsend_packet_to_agent() function. This is similar to sending the DPI_OPEN packet. We then wait for a response from the agent. Again, we use the DPIawait_packet_from_agent() function in the same way as we awaited a response on the DPI-OPEN request. Once we have received the response, we must check the return code to ensure that registration was successful. The following code example demonstrates how to register one MIB sub-tree with the agent. #include /* DPI 2.0 API definitions */ static int handle; /* handle has global scope */ static void do_register(void) { unsigned char *packet_p; int rc; unsigned long length; snmp_dpi_hdr *hdr_p; packet_p = mkDPIregister( /* Make DPI register */ 3, /* timeout in seconds */ 0, /* requested priority */ DPI_SIMPLE_MIB, /* ptr to the sub-tree */ DPI_BULK_NO); /* GetBulk into GetNext */ if (!packet_p) exit(1); /* If it failed, exit */ rc = DPIsend_packet_to_agent( /* send REGISTER packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* this is its length */ if (rc != DPI_RC_OK) exit(1); /* If it failed, exit */ rc = DPIawait_packet_from_agent( /* wait for response */ handle, /* on this connection */ 3, /* timeout in seconds */ &packet_p, /* gets ptr to packet */ &length); /* gets packet length */ if (rc != DPI_RC_OK) exit(1); /* If it failed, exit */ hdr_p = pDPIpacket(packet_p); /* parse DPI packet */ if (hdr_p == snmp_dpi_hdr_NULL_p) /* Failed to parse it */ exit(1); /* so exit */ if (hdr_p->packet_type != SNMP_DPI_RESPONSE) exit(1); rc = hdr_p->data_u.resp_p->error_code; if (rc != SNMP_ERROR_DPI_noError) exit(1); } /* end of do_register() */ ═══ 11.10.4. Processing Requests from the Agent ═══ After we have registered our sample MIB sub-tree with the agent, we must expect that SNMP requests for that sub-tree will be passed for processing by us. Since the requests will arrive in the form of DPI packets on the connection that the we have established, we go into a while loop to await DPI packets from the agent. Since the subagent cannot know in advance which kind of packet arrives from the agent, we await a DPI packet (forever), then we parse the packet, check the packet type, and process the request based on the DPI packet type. A call to pDPIpacket, which expects as parameter a pointer to the encoded/serialized DPI packet, returns a pointer to a DPI parse tree. The pointer points to a snmp_dpi_hdr structure which looks as follows: struct snmp_dpi_hdr { unsigned char proto_major; unsigned char proto_version; unsigned char proto_release; unsigned short packet_id; unsigned char packet_type; union { snmp_dpi_reg_packet *reg_p; snmp_dpi_ureg_packet *ureg_p; snmp_dpi_get_packet *get_p; snmp_dpi_next_packet *next_p; snmp_dpi_next_packet *bulk_p; snmp_dpi_set_packet *set_p; snmp_dpi_resp_packet *resp_p; snmp_dpi_trap_packet *trap_p; snmp_dpi_open_packet *open_p; snmp_dpi_close_packet *close_p; unsigned char *any_p; } data_u; }; typedef struct snmp_dpi_hdr snmp_dpi_hdr; #define snmp_dpi_hdr_NULL_p ((snmp_dpi_hdr *)0) With the DPI parse tree, we decide how to process the DPI packet. The following code example demonstrates the high level process of a DPI subagent. #include /* DPI 2.0 API definitions */ static int handle; /* handle has global scope */ main(int argc, char *argvэ∙, char *envpэ∙) { unsigned char *packet_p; int rc = 0; unsigned long length; snmp_dpi_hdr *hdr_p; if (argc>1) { /* if use passed one parm */ if (strcmp(argvэ1∙,"-d")==0) /* being -d, then we */ DPIdebug(2); /* turn on DPI debugging */ } /* endif */ /* which shows us things */ do_connect_and_open(); /* connect and DPI-OPEN */ do_register(); /* register our sub-tree */ while (rc == 0) { /* do forever */ rc = DPIawait_packet_from_agent( /* wait for a DPI packet */ handle, /* on this connection */ -1, /* wait forever */ &packet_p, /* receives ptr to packet */ &length); /* receives packet length */ if (rc != DPI_RC_OK) exit(1); /* If it failed, exit */ hdr_p = pDPIpacket(packet_p); /* parse DPI packet */ if (hdr_p == snmp_dpi_hdr_NULL_p)/* If we fail to parse it */ exit(1); /* then exit */ switch(hdr_p->packet_type) { /* handle by DPI type */ case SNMP_DPI_GET: rc = do_get(hdr_p, hdr_p->data_u.get_p); break; case SNMP_DPI_GETNEXT: rc = do_next(hdr_p, hdr_p->data_u.next_p); break; case SNMP_DPI_SET: case SNMP_DPI_COMMIT: case SNMP_DPI_UNDO: rc = do_set(hdr_p, hdr_p->data_u.set_p); break; case SNMP_DPI_CLOSE: rc = do_close(hdr_p, hdr_p->data_u.close_p); break; case SNMP_DPI_UNREGISTER: rc = do_unreg(hdr_p, hdr_p->data_u.ureg_p); break; default: printf("Unexpected DPI packet type %d\n", hdr_p->packet_type); rc = -1; } /* endswitch */ if (rc) exit(1); } /* endwhile */ return(0); } /* end of main() */ ═══ 11.10.5. Processing a GET Request ═══ When the DPI packet is parsed, the snmp_dpi_hdr structure will show in the packet_type that this is a SNMP_DPI_GET packet. In that case, the packet_body contains a pointer to a GET-varBind, which is represented in an snmp_dpi_get_packet structure: struct dpi_get_packet { char *object_p; /* ptr to OIDstring */ char *group_p; /* ptr to sub-tree */ char *instance_p; /* ptr to rest of OID */ struct dpi_get_packet *next_p; /* ptr to next in chain */ }; typedef struct dpi_get_packet snmp_dpi_get_packet; #define snmp_dpi_get_packet_NULL_p ((snmp_dpi_get_packet *)0) Assuming we have registered example sub-tree 1.3.6.1.4.1.2.2.1.5 and a GET request comes in for one variable 1.3.6.1.4.1.2.2.1.5.1.0 so that it is object 1 instance 0 in our sub-tree, the fields in the snmp_dpi_get_packet would have pointers to: object_p -> "1.3.6.1.4.1.2.2.1.5.1.0" group_p -> "1.3.6.1.4.1.2.2.1.5." instance_p -> "1.0" next_p -> snmp_dpi_get_packet_NULL_p If there are multiple varBinds in a GET request, each one is represented in a snmp_dpi_get_packet structure and all the snmp_dpi_get_packet structures are chained via the next pointer. As long as the next pointer is not the snmp_dpi_get_packet_NULL_p pointer, there are more varBinds in the list. Now we can analyze the varBind structure for whatever checking we want to do. Once we are ready to make a response that contains the value of the variable, we prepare a SET-varBind which is represented in an snmp_dpi_set_packet structure: struct dpi_set_packet { char *object_p; /* ptr to OIDstring */ char *group_p; /* ptr to sub-tree */ char *instance_p; /* ptr to rest of OID */ unsigned char value_type; /* SNMP_TYPE_xxxx */ unsigned short value_len; /* value length */ char *value_p; /* ptr to value itself */ struct dpi_set_packet *next_p; /* ptr to next in chain */ }; typedef struct dpi_set_packet snmp_dpi_set_packet; #define snmp_dpi_set_packet_NULL_p ((snmp_dpi_set_packet *)0) We can use the mkDPIset() function to prepare such a structure. This function expects the following parameters:  A pointer to an existing snmp_dpi_set_packet structure if the new varBind must be added to an existing chain of varBinds. If this is the first or the only varBind in the chain, pass the snmp_dpi_set_packet_NULL_p pointer to indicate this.  A pointer to the sub-tree that we registered.  A pointer to the rest of the OID; in other words, the piece that follows the sub-tree.  The value type of the value to be bound to the variable name. This is must be one of the SNMP_TYPE_xxxx values as defined in the snmp_dpi.h include file.  The length of the value for integer type values. This must be a length of 4. So we always work with 32-bit signed or unsigned integers except for the Counter64 type. For the Counter64 type, we must point to a snmp_dpi_u64 structure and pass the length of that structure.  A pointer to the value. Memory for the varBind is dynamically allocated and the data itself is copied. So upon return we can dispose of our own pointers and allocated memory as we please. If the call is successful, a pointer is returned as follows:  To a new snmp_dpi_set_packet if it is the first or only varBind.  To the existing snmp_dpi_set_packet that we passed on the call. In this case, the new packed has been chained to the end of the varBind list. If the mkDPIset() call fails, a NULL pointer is returned. Once we have prepared the SET-varBind data, we can create a DPI RESPONSE packet using the mkDPIresponse() function which expects these parameters:  A pointer to an snmp_dpi_hdr. We should use the header of the parsed incoming packet. It is used to copy the packet_id from the request into the response, such that the agent can correlate the response to a request.  A return code which is an SNMP error code. If successful, this should be SNMP_ERROR_noError (value zero). If failure, it must be one of the SNMP_ERROR_xxxx values as defined in the snmp_dpi.h include file. A request for a non-existing object or instance is not considered an error. Instead, we must pass a value type of SNMP_TYPE_noSuchObject or SNMP_TYPE_noSuchInstance respectively. These two value types have an implicit value of NULL, so we can pass a zero length and a NULL pointer for the value in this case.  The index of the varBind in error starts counting at 1. Pass zero if no error occurred, or pass the proper index of the first varBind for which an error was detected.  A pointer to a chain of snmp_dpi_set_packets (varBinds) to be returned as response to the GET request. If an error was detected, an snmp_dpi_set_packet_NULL_p pointer may be passed. The following code example returns a response. We assume that there are no errors in the request, but proper code should do the checking for that. For instance, we return a noSuchInstance if the instance is not exactly what we expect and a noSuchObject if the object instance_ID is greater than 3, for example 4.0. However, there might be no instance_ID at all and we should check for that too. static int do_get(snmp_dpi_hdr *hdr_p, snmp_dpi_get_packet *pack_p) { unsigned char *packet_p; int rc; snmp_dpi_set_packet *varBind_p; varBind_p = /* init the varBind chain*/ snmp_dpi_set_packet_NULL_p; /* to a NULL pointer */ if (pack_p->instance_p && (strcmp(pack_p->instance_p,"1.0") == 0)) { varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to sub-tree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_Integer32, /* value type Integer 32 */ sizeof(value1), /* length of value */ &value1); /* ptr to value */ } else if (pack_p->instance_p && (strcmp(pack_p->instance_p,"2.0") == 0)) { varBind_p = mkDPIset( /* Make DPI set packet*/ varBind_p, /* ptr to varBindchain*/ pack_p->group_p, /* ptr to sub-tree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_DisplayString,/* value type */ strlen(value2_p), /* length of value */ value2_p); /* ptr to value */ } else if (pack_p->instance_p && (strcmp(pack_p->instance_p,"3.0") == 0)) { varBind_p = mkDPIset( /* Make DPI set packet*/ varBind_p, /* ptr to varBindchain*/ pack_p->group_p, /* ptr to sub-tree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_Counter32, /* value type */ sizeof(value3), /* length of value */ &value3); /* ptr to value */ } else if (pack_p->instance_p && (strcmp(pack_p->instance_p,"3")>0)) { varBind_p = mkDPIset( /* Make DPI set packet*/ varBind_p, /* ptr to varBindchain*/ pack_p->group_p, /* ptr to sub-tree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_noSuchObject, /* value type */ 0L, /* length of value */ (unsigned char *)0); /* ptr to value */ } else { varBind_p = mkDPIset( /* Make DPI set packet*/ varBind_p, /* ptr to varBindchain*/ pack_p->group_p, /* ptr to sub-tree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_noSuchInstance,/* value type */ 0L, /* length of value */ (unsigned char *)0); /* ptr to value */ } /* endif */ if (!varBind_p) return(-1); /* If it failed, return */ packet_p = mkDPIresponse( /* Make DPIresponse pack */ hdr_p, /* ptr parsed request */ SNMP_ERROR_noError, /* all is OK, no error */ 0L, /* index zero, no error */ varBind_p); /* varBind response data */ if (!packet_p) return(-1); /* If it failed, return */ rc = DPIsend_packet_to_agent( /* send RESPONSE packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* and this is its length*/ return(rc); /* return retcode */ } /* end of do_get() */ ═══ 11.10.6. Processing a GETNEXT Request ═══ When a DPI packet is parsed, the snmp_dpi_hdr structure shows in the packet_type that this is a SNMP_DPI_GETNEXT packet, and so the packet_body contains a pointer to a GETNEXT-varBind, which is represented in an snmp_dpi_next_packet structure: struct dpi_next_packet { char *object_p; /* ptr to OIDstring */ char *group_p; /* ptr to sub-tree */ char *instance_p; /* ptr to rest of OID */ struct dpi_next_packet *next_p; /* ptr to next in chain*/ }; typedef struct dpi_next_packet snmp_dpi_next_packet; #define snmp_dpi_next_packet_NULL_p ((snmp_dpi_next_packet *)0) In the interest of simplicity and easier understanding we will discuss the GETNEXT for a scalar object, which only has one instance. For columnar objects, which may have multiple instances, the process is more complex. However, the DPI subagent programmer should be able to handle that once the basics of GETNEXT processing in a DPI subagent is understood. Assuming we have registered example sub-tree dpiSimpleMIB and a GETNEXT arrives for one variable, dpiSimpleInteger.0, so that is object 1 instance 0 in our sub-tree, the fields in the snmp_dpi_get_packet structure would have pointers to: object_p -> "1.3.6.1.4.1.2.2.1.5.1.0" group_p -> "1.3.6.1.4.1.2.2.1.5." instance_p -> "1.0" next_p -> snmp_dpi_next_packet_NULL_p If there are multiple varBinds in a GETNEXT request, each one is represented in a snmp_dpi_get_packet structure and all the snmp_dpi_get_packet structures are chained via the next pointer. As long as the next pointer is not the snmp_dpi_next_packet_NULL_p pointer, there are more varBinds in the list. Now we can analyze the varBind structure for whatever checking we want to do. We must find out which OID is the one that lexicographically follows the one in the request. It is that OID with its value that we must return as a response. Therefore, we must now also set the proper OID in the response. Once we are ready to make a response that contains the new OID and the value of that variable, we must prepare a SET-varBind which is represented in an snmp_dpi_set_packet: struct dpi_set_packet { char *object_p; /* ptr to OIDstring */ char *group_p; /* ptr to sub-tree */ char *instance_p; /* ptr to rest of OID */ unsigned char value_type; /* SNMP_TYPE_xxxx */ unsigned short value_len; /* value length */ char *value_p; /* ptr to value itself */ struct dpi_set_packet *next_p; /* ptr to next in chain */ }; typedef struct dpi_set_packet snmp_dpi_set_packet; #define snmp_dpi_set_packet_NULL_p ((snmp_dpi_set_packet *)0) We can use the mkDPIset() function to prepare such a structure. This function expects the following parameters:  A pointer to an existing snmp_dpi_set_packet structure if the new varBind must be added to an existing chain of varBinds. If this is the first or only varBind in the chain, we pass the snmp_dpi_set_packet_NULL_p pointer to indicate this.  A pointer to the sub-tree that we registered.  A pointer to the rest of the OID, in other words the piece that follows the sub-tree.  The value type of the value to be bound to the variable name. This is must be one of the SNMP_TYPE_xxxx values as defined in the snmp_dpi.h include file.  The length of the value for integer type values. This must be a length of 4. So we always work with 32-bit signed or unsigned integers except for the Counter64 type. For Counter 64 type, we must point to a snmp_dpi_u64 structure and pass the length of that structure.  A pointer to the value. Memory for the varBind is dynamically allocated and the data itself is copied. Upon return, we can dispose of our own pointers and allocated memory as we please. If the call is successful, a pointer is returned as follows:  A new snmp_dpi_set_packet if it is the first or only varBind.  The existing snmp_dpi_set_packet that we passed on the call. In this case, the new packed has been chained to the end of the varBind list. If the mkDPIset() call fails, a NULL pointer is returned. Once we have prepared the SET-varBind data, we can create a DPI RESPONSE packet using the mkDPIresponse() function, which expects these parameters:  A pointer to an snmp_dpi_hdr. We should use the header of the parsed incoming packet. It is used to copy the packet_id from the request into the response, such that the agent can correlate the response to a request.  A return code which is an SNMP error code. If successful, this should be SNMP_ERROR_noError (value zero). If failure, it must be one of the SNMP_ERROR_xxxx values as defined in the snmp_dpi.h include file. A request for a non-existing object or instance is not considered an error. Instead, we must pass the OID and value of the first OID that lexicographically follows the non-existing object and/or instance. Reaching the end of our sub-tree is not considered an error. For example, if there is no NEXT OID, this is not an error. In this situation we must return the original OID as received in the request and a value_type of SNMP_TYPE_endOfMibView. This value_type has an implicit value of NULL, so we can pass a zero length and a NULL pointer for the value.  The index of the first varBind in error starts counting at 1. Pass zero if no error occurred, or pass the proper index of the first varBind for which an error was detected.  A pointer to a chain of snmp_dpi_set_packet(s) (varBinds) to be returned as response to the GETNEXT request. If an error was detected, an snmp_dpi_set_packet_NULL_p pointer may be passed. The following code example returns a response. We assume that there are no errors in the request, but proper code should do the checking for that. We do proper checking for lexicographic next object, but we do no checking for ULONG_MAX, or making sure that the instance ID is indeed valid (digits and dots). If we get to the end of our dpiSimpleMIB, we must return an endOfMibView as defined by the SNMP Version 2 rules. static int do_next(snmp_dpi_hdr *hdr_p, snmp_dpi_next_packet *pack_p) { unsigned char *packet_p; int rc; unsigned long subid; /* subid is unsigned */ unsigned long instance; /* same with instance */ char *cp; snmp_dpi_set_packet *varBind_p; varBind_p = /* init the varBind chain*/ snmp_dpi_set_packet_NULL_p; /* to a NULL pointer */ if (pack_p->instance_p) { /* we have an instance ID*/ cp = pack_p->instance_p; /* pick up ptr */ subid = strtoul(cp, &cp, 10); /* convert subid (object)*/ if (*cp == '.') { /* followed by a dot ? */ cp++; /* point after it if yes */ instance=strtoul(cp,&cp,10); /* convert real instance */ /* not that we need it,we*/ subid++; /* only have instance 0, */ /* so NEXT is next object*/ instance = 0; /* and always instance 0 */ } else { /* no real instance */ instance = 0; /* passed, so we use 0 */ if (subid == 0) subid++; /* if object 0, subid 1 */ } /* endif */ } else { /* no instance ID passed */ subid = 1; /* so do first object */ instance = 0; /* 0 is all we have */ } /* endif */ /* we have set subid and instance such that we can basically*/ /* process the request as a GET now. Actually, we don't even*/ /* need instance, because all out object instances are zero.*/ if (instance != 0) printf("Strange instance: %lu\n",instance); switch (subid) { case 1: varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to sub-tree */ DPI_SIMPLE_INTEGER, /* ptr to rest of OID */ SNMP_TYPE_Integer32, /* value type Integer 32 */ sizeof(value1), /* length of value */ &value1); /* ptr to value */ break; case 2: varBind_p = mkDPIset( /* Make DPI set packet*/ varBind_p, /* ptr to varBindchain*/ pack_p->group_p, /* ptr to sub-tree */ DPI_SIMPLE_STRING, /* ptr to rest of OID */ SNMP_TYPE_DisplayString,/* value type */ strlen(value2_p), /* length of value */ value2_p); /* ptr to value */ break; case 3: varBind_p = mkDPIset( /* Make DPI set packet*/ varBind_p, /* ptr to varBindchain*/ pack_p->group_p, /* ptr to sub-tree */ DPI_SIMPLE_COUNTER32, /* ptr to rest of OID */ SNMP_TYPE_Counter32, /* value type */ sizeof(value3), /* length of value */ &value3); /* ptr to value */ break; case 4: /* *Apr23*/ varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ pack_p->group_p, /* ptr to sub-tree */ DPI_SIMPLE_COUNTER64, /* ptr to rest of OID */ SNMP_TYPE_Counter64, /* value type */ sizeof(value4), /* length of value */ &value4); /* ptr to value */ break; /* *Apr23*/ default: varBind_p = mkDPIset( /* Make DPI set packet*/ varBind_p, /* ptr to varBindchain*/ pack_p->group_p, /* ptr to sub-tree */ pack_p->instance_p, /* ptr to rest of OID */ SNMP_TYPE_endOfMibView, /* value type */ 0L, /* length of value */ (unsigned char *)0); /* ptr to value */ break; } /* endswitch */ if (!varBind_p) return(-1); /* If it failed, return */ packet_p = mkDPIresponse( /* Make DPIresponse pack */ hdr_p, /* ptr parsed request */ SNMP_ERROR_noError, /* all is OK, no error */ 0L, /* index zero, no error */ varBind_p); /* varBind response data */ if (!packet_p) return(-1); /* If it failed, return */ rc = DPIsend_packet_to_agent( /* send RESPONSE packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* and this is its length*/ return(rc); /* return retcode */ } /* end of do_next() */ ═══ 11.10.7. Processing a SET/COMMIT/UNDO Request ═══ These three requests can come in one of these sequences:  SET, COMMIT  SET, UNDO  SET, COMMIT, UNDO The normal sequence is SET and then COMMIT. When we receive a SET request, we must make preparations to accept the new value. For example, check that it is for an existing object and instance, check the value type and contents to be valid, allocate memory, but we must not yet make the change. If there are no SET errors, the next request we receive will be a COMMIT request. It is then that we must make the change, but we must also keep enough information such that we can UNDO the change later if we get a subsequent UNDO request. The latter may happen if the agent discovers any errors with other subagents while processing requests that belong to the same original SNMP SET packet. All the varBinds in the same SNMP request PDU must be processed "as if atomic". When the DPI packet is parsed, the snmp_dpi_hdr structure shows in the packet_type that this is an SNMP_DPI_SET, SNMP_DPI_COMMIT, or SNMP_DPI_UNDO packet. In that case, the packet_body contains a pointer to a SET-varBind, represented in an snmp_dpi_get_packet structure. COMMIT and UNDO have same varBind data as SET upon which they follow: struct dpi_set_packet { char *object_p; /* ptr to OIDstring */ char *group_p; /* ptr to sub-tree */ char *instance_p; /* ptr to rest of OID */ unsigned char value_type; /* SNMP_TYPE_xxxx */ unsigned short value_len; /* value length */ char *value_p; /* ptr to value itself */ struct dpi_set_packet *next_p; /* ptr to next in chain */ }; typedef struct dpi_set_packet snmp_dpi_set_packet; #define snmp_dpi_set_packet_NULL_p ((snmp_dpi_set_packet *)0) Assuming we have registered example sub-tree dpiSimpleMIB and a GET request comes in for one variable dpiSimpleString.0 so that is object 1 instance 0 in our sub-tree, and also assuming that the agent knows about our compiled dpiSimpleMIB so that it knows this is a DisplayString as opposed to just an arbitrary OCTET_STRING, the pointers in the snmp_dpi_set_packet structure would have pointers and values like: object_p -> "1.3.6.1.4.1.2.2.1.5.2.0" group_p -> "1.3.6.1.4.1.2.2.1.5." instance_p -> "2.0" value_type -> SNMP_TYPE_DisplayString value_len -> 8 value_p -> pointer to the value to be set next_p -> snmp_dpi_get_packet_NULL_p If there are multiple varBinds in a SET request, each one is represented in a snmp_dpi_set_packet structure and all the snmp_dpi_set_packet structures are chained via the next pointer. As long as the next pointer is not the snmp_dpi_set_packet_NULL_p pointer, there are more varBinds in the list. Now we can analyze the varBind structure for whatever checking we want to do. Once we are ready to make a response that contains the value of the variable, we may prepare a new SET-varBind. However, by definition, the response to a successful SET is exactly the same as the SET request. So there is no need to return any varBinds. A response with SNMP_ERROR_noError and an index of zero will do. If there is an error, a response with the SNMP_ERROR_xxxx error code and an index pointing to the varBind in error (counting starts at 1) will do. The following code example returns a response. We assume that there are no errors in the request, but proper code should do the checking for that. We also do not check if the varBind in the COMMIT and/or UNDO is the same as that in the SET request. A proper agent would make sure that is the case, but a proper subagent may want to verify that for itself. We only do one check that this is dpiSimpleString.0, and if it is not, we return a noCreation. This may not be correct, the mainline does not even return a response. static int do_set(snmp_dpi_hdr *hdr_p, snmp_dpi_set_packet *pack_p) { unsigned char *packet_p; int rc; int index = 0; int error = SNMP_ERROR_noError; snmp_dpi_set_packet *varBind_p; varBind_p = /* init the varBind chain */ snmp_dpi_set_packet_NULL_p; /* to a NULL pointer */ if (!pack_p->instance_p || (strcmp(pack_p->instance_p,"2.0") != 0)) { if (pack_p->instance_p && (strncmp(pack_p->instance_p,"1.",2) == 0)) { error = SNMP_ERROR_notWritable; } else if (pack_p->instance_p && (strncmp(pack_p->instance_p,"2.",2) == 0)) { error = SNMP_ERROR_noCreation; } else if (pack_p->instance_p && (strncmp(pack_p->instance_p,"3.",2) == 0)) { error = SNMP_ERROR_notWritable; } else { error = SNMP_ERROR_noCreation; } /* endif */ packet_p = mkDPIresponse( /* Make DPIresponse packet */ hdr_p, /* ptr parsed request */ error, /* all is OK, no error */ 1, /* index is 1, 1st varBind */ varBind_p); /* varBind response data */ if (!packet_p) return(-1); /* If it failed, return */ rc = DPIsend_packet_to_agent( /* send RESPONSE packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* and this is its length */ return(rc); /* return retcode */ } switch (hdr_p->packet_type) { case SNMP_DPI_SET: if ((pack_p->value_type != SNMP_TYPE_DisplayString) && (pack_p->value_type != SNMP_TYPE_OCTET_STRING)) { /* check octet string in case agent has no compiled MIB */ error = SNMP_ERROR_wrongType; break; /* from switch */ } /* endif */ if (new_val_p) free(new_val_p); /* free these memory areas */ if (old_val_p) free(old_val_p); /* if we allocated any */ new_val_p = (char *)0; old_val_p = (char *)0; new_val_len = 0; old_val_len = 0; new_val_p = /* allocate memory for */ malloc(pack_p->value_len); /* new value to set */ if (new_val_p) { /* If success, then also */ memcpy(new_val_p, /* copy new value to our */ pack_p->value_p, /* own and newly allocated */ pack_p->value_len); /* memory area. */ new_val_len = pack_p->value_len; } else { /* Else failed to malloc, */ error = SNMP_ERROR_genErr; /* so that is a genErr */ index = 1; /* at first varBind */ } /* endif */ break; case SNMP_DPI_COMMIT: old_val_p = cur_val_p; /* save old value for undo */ cur_val_p = new_val_p; /* make new value current */ new_val_p = (char *)0; /* keep only 1 ptr around */ old_val_len = cur_val_len; /* and keep lengths correct*/ cur_val_len = new_val_len; new_val_len = 0; /* may need to convert from ASCII to native if OCTET_STRING */ break; case SNMP_DPI_UNDO: if (new_val_p) { /* free allocated memory */ free(new_val_p); new_val_p = (char *)0; new_val_len = 0; } /* endif */ if (old_val_p) { if (cur_val_p) free(cur_val_p); cur_val_p = old_val_p; /* reset to old value */ cur_val_len = old_val_len; old_val_p = (char *)0; old_val_len = 0; } /* endif */ break; } /* endswitch */ packet_p = mkDPIresponse( /* Make DPIresponse packet */ hdr_p, /* ptr parsed request */ error, /* all is OK, no error */ index, /* index is zero, no error */ varBind_p); /* varBind response data */ if (!packet_p) return(-1); /* If it failed, return */ rc = DPIsend_packet_to_agent( /* send RESPONSE packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* and this is its length */ return(rc); /* return retcode */ } /* end of do_set() */ ═══ 11.10.8. Processing an UNREGISTER Request ═══ An agent can send an UNREGISTER packet if some other subagent does a register for the same sub-tree at a higher priority. An agent can also send an UNREGISTER if, for example, an SNMP manager tells it to "invalidate" the subagent connection or the registered sub-tree. Here is an example of how to handle such a packet. #include /* DPI 2.0 API definitions */ static int do_unreg(snmp_dpi_hdr *hdr_p, snmp_dpi_ureg_packet *pack_p) { printf("DPI UNREGISTER received from agent, reason=%d\n", pack_p->reason_code); printf(" sub-tree=%s\n",pack_p->group_p); DPIdisconnect_from_agent(handle); return(-1); /* causes exit in main loop */ } /* end of do_unreg() */ ═══ 11.10.9. Processing a CLOSE Request ═══ An agent can send a CLOSE packet if it encounters an error or for some other reason. It can also do so if an SNMP MANAGER tells it to "invalidate" the subagent connection. Here is an example of how to handle such a packet. #include /* DPI 2.0 API definitions */ static int do_close(snmp_dpi_hdr *hdr_p, snmp_dpi_close_packet *pack_p) { printf("DPI CLOSE received from agent, reason=%d\n", pack_p->reason_code); DPIdisconnect_from_agent(handle); return(-1); /* causes exit in main loop */ } /* end of do_close() */ ═══ 11.10.10. Generating a TRAP ═══ A trap can be issued at any time after a DPI OPEN was successful. To do so, you must create a trap packet and send it to the agent. With the TRAP, you can pass all sorts of varBinds if you want. In this example, we pass two varBinds one with integer data and one with an octet string. You can also pass an Enterprise ID, but with DPI 2.0, the agent will use your subagent ID as the enterprise ID if you do not pass one with the trap. In most cases that will probably be fine. We must first prepare a varBind list chain that contains the two variables that we want to pass along with the trap. To do so we must prepare a chain of two snmp_dpi_set_packet structures, which looks like: struct dpi_set_packet { char *object_p; /* ptr to OIDstring */ char *group_p; /* ptr to sub-tree */ char *instance_p; /* ptr to rest of OID */ unsigned char value_type; /* SNMP_TYPE_xxxx */ unsigned short value_len; /* value length */ char *value_p; /* ptr to value itself */ struct dpi_set_packet *next_p; /* ptr to next in chain */ }; typedef struct dpi_set_packet snmp_dpi_set_packet; #define snmp_dpi_set_packet_NULL_p ((snmp_dpi_set_packet *)0) We can use the mkDPIset() function to prepare such a structure. This function expects the following parameters:  A pointer to an existing snmp_dpi_set_packet structure if the new varBind must be added to an existing chain of varBinds. If this is the first or the only varBind in the chain, pass the snmp_dpi_set_packet_NULL_p pointer to indicate this.  A pointer to the sub-tree that we registered.  A pointer to the rest of the OID, in other words, the piece that follows the sub-tree.  The value type of the value to be bound to the variable name. This is must be one of the SNMP_TYPE_xxxx values as defined in the snmp_dpi.h include file.  The length of the value. For integer type values, this must be a length of 4. We always work with 32-bit signed or unsigned integers except for the Counter64 type. For the Counter64 type, we must point to a snmp_dpi_u64 structure and pass the length of that structure.  A pointer to the value. Memory for the varBind is dynamically allocated and the data itself is copied. Upon return, we can dispose of our own pointers and allocated memory as we please. If the call is successful, a pointer is returned as follows:  To a new snmp_dpi_set_packet if it is the first or only varBind.  To the existing snmp_dpi_set_packet that we passed on the call. In this case, the new packed has been chained to the end of the varBind list. If the mkDPIset() call fails, a NULL pointer is returned. Once we have prepared the SET-varBind data, we can create a DPI TRAP packet. To do so we can use the mkDPItrap() function which expects these parameters:  The generic trap code. Use 6 for enterprise specific trap type.  The specific trap type. This is a type that is defined by the MIB which we are implementing. In our example we just use a 1.  A pointer to a chain of varBinds or the NULL pointer if no varBinds need to be passed with the trap.  A pointer to the enterprise OID if we want to use a different enterprise ID than the OID we used to identify ourselves as a subagent at DPI-OPEN time. The following code creates an enterprise specific trap with specific type 1 and passes two varBinds. The first varBind with our object 1, instance 0, Integer32 value; the second varBind with our object 2, instance 0, Octet String. We pass no enterprise ID. static int do_trap(void) { unsigned char *packet_p; int rc; snmp_dpi_set_packet *varBind_p; varBind_p = /* init the varBindchain */ snmp_dpi_set_packet_NULL_p, /* to a NULL pointer */ varBind_p = mkDPIset( /* Make DPI set packet */ varBind_p, /* ptr to varBind chain */ DPI_SIMPLE_MIB, /* ptr to sub-tree */ DPI_SIMPLE_INTEGER, /* ptr to rest of OID */ SNMP_TYPE_Integer32, /* value type Integer 32 */ sizeof(value1), /* length of value */ &value1); /* ptr to value */ if (!varBind_p) return(-1); /* If it failed, return */ varBind_p = mkDPIset( /* Make DPI set packet*/ varBind_p, /* ptr to varBindchain*/ DPI_SIMPLE_MIB, /* ptr to sub-tree */ DPI_SIMPLE_STRING, /* ptr to rest of OID */ SNMP_TYPE_DisplayString,/* value type */ strlen(value2_p), /* length of value */ value2_p); /* ptr to value */ if (!varBind_p) return(-1); /* If it failed, return */ varBind_p = mkDPIset( /* Make DPI set packet*/ varBind_p, /* ptr to varBindchain*/ DPI_SIMPLE_MIB, /* ptr to sub-tree */ DPI_SIMPLE_COUNTER32, /* ptr to rest of OID */ SNMP_TYPE_Counter32, /* value type */ sizeof(value3), /* length of value */ &value3); /* ptr to value */ if (!varBind_p) return(-1); /* If it failed, return */ packet_p = mkDPItrap( /* Make DPItrap packet */ 6, /* enterpriseSpecific */ 1, /* specific type = 1 */ varBind_p, /* varBind data, and use */ (char *)0); /* default enterpriseID */ if (!packet_p) return(-1); /* If it failed, return */ rc = DPIsend_packet_to_agent( /* send TRAP packet */ handle, /* on this connection */ packet_p, /* this is the packet */ DPI_PACKET_LEN(packet_p));/* and this is its length*/ return(rc); /* return retcode */ } /* end of do_trap() */ ═══ 12. NETWORKS File Structure ═══ The NETWORKS file contains the network name, number, and alias or aliases of known networks. The NETWORKS file must reside in the directory specified by the ETC environment variable. The NETWORKS file is used only by the following socket calls:  endnetent()  getnetbyaddr()  getnetbyname()  getnetent()  setnetent() Name Structures of Known Networks lists examples of network names contained in the NETWORKS file. ┌────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 18. Name Structures of Known Networks │ ├──────────┬────────────────────────────────────────────────┬────────────────────────────┤ │ NAME │ │ │ │ OF FILE │ CONTENTS OF FILE │ SAMPLE FILE ENTRIES │ ├──────────┼────────────────────────────────────────────────┼────────────────────────────┤ │ NETWORKS │ official_network_name network_number alias │ ne-region 128.1 classb.net1│ │ │ │ at1-region 128.2 classb.net│ │ │ │ lab-net 192.5.1 classc.net5│ └──────────┴────────────────────────────────────────────────┴────────────────────────────┘ ═══ 13. Socket Error Constants ═══ The following table provides the error constants set by socket calls. This table can be found in the header file. /* * The redefinition of error constants is necessary to avoid conflict with * standard compiler error constants. * * All OS/2 SOCKETS API error constants are biased by SOCBASEERR from the "normal" * */ #define SOCBASEERR 10000 /* * OS/2 SOCKETS API definitions of regular Microsoft C 6.0 error constants */ #define SOCEPERM (SOCBASEERR+1) /*Not owner*/ #define SOCESRCH (SOCBASEERR+3) /*No such process*/ #define SOCEINTR (SOCBASEERR+4) /*Interrupted system call*/ #define SOCENXIO (SOCBASEERR+6) /*No such device or address*/ #define SOCEBADF (SOCBASEERR+9) /*Bad file number*/ #define SOCEACCES (SOCBASEERR+13) /*Permission denied*/ #define SOCEFAULT (SOCBASEERR+14) /*Bad address*/ #define SOCEINVAL (SOCBASEERR+22) /*Invalid argument*/ #define SOCEMFILE (SOCBASEERR+24) /*Too many open files*/ #define SOCEPIPE (SOCBASEERR+32) /*Broken pipe*/ #define SOCEOS2ERR (SOCBASEERR+100) /*OS/2 Error*/ /* * OS/2 SOCKETS API definitions of regular BSD error constants */ #define SOCEWOULDBLOCK (SOCBASEERR+35) /*Operation would block*/ #define SOCEINPROGRESS (SOCBASEERR+36) /*Operation now in progress*/ #define SOCEALREADY (SOCBASEERR+37) /*Operation already in progress*/ #define SOCENOTSOCK (SOCBASEERR+38) /*Socket operation on non-socket*/ #define SOCEDESTADDRREQ (SOCBASEERR+39) /*Destination address required*/ #define SOCEMSGSIZE (SOCBASEERR+40) /*Message too long*/ #define SOCEPROTOTYPE (SOCBASEERR+41) /*Protocol wrong type for socket*/ #define SOCENOPROTOOPT (SOCBASEERR+42) /*Protocol not available*/ #define SOCEPROTONOSUPPORT (SOCBASEERR+43) /*Protocol not supported*/ #define SOCESOCKTNOSUPPORT (SOCBASEERR+44) /*Socket type not supported*/ #define SOCEOPNOTSUPP (SOCBASEERR+45) /*Operation not supported on socket*/ #define SOCEPFNOSUPPORT (SOCBASEERR+46) /*Protocol family not supported*/ #define SOCEAFNOSUPPORT (SOCBASEERR+47) /*Address family not supported by protocol family*/ #define SOCEADDRINUSE (SOCBASEERR+48) /*Address already in use*/ #define SOCEADDRNOTAVAIL (SOCBASEERR+49) /*Can't assign requested address*/ #define SOCENETDOWN (SOCBASEERR+50) /*Network is down*/ #define SOCENETUNREACH (SOCBASEERR+51) /*Network is unreachable*/ #define SOCENETRESET (SOCBASEERR+52) /*Network dropped connection on reset*/ #define SOCECONNABORTED (SOCBASEERR+53) /*Software caused connection abort*/ #define SOCECONNRESET (SOCBASEERR+54) /*Connection reset by peer*/ #define SOCENOBUFS (SOCBASEERR+55) /*No buffer space available*/ #define SOCEISCONN (SOCBASEERR+56) /*Socket is already connected*/ #define SOCENOTCONN (SOCBASEERR+57) /*Socket is not connected*/ #define SOCESHUTDOWN (SOCBASEERR+58) /*Can't send after socket shutdown*/ #define SOCETOOMANYREFS (SOCBASEERR+59) /*Too many references: can't splice*/ #define SOCETIMEDOUT (SOCBASEERR+60) /*Connection timed out*/ #define SOCECONNREFUSED (SOCBASEERR+61) /*Connection refused*/ #define SOCELOOP (SOCBASEERR+62) /*Too many levels of symbolic links*/ #define SOCENAMETOOLONG (SOCBASEERR+63) /*File name too long*/ #define SOCEHOSTDOWN (SOCBASEERR+64) /*Host is down*/ #define SOCEHOSTUNREACH (SOCBASEERR+65) /*No route to host*/ #define SOCENOTEMPTY (SOCBASEERR+66) /*Directory not empty*/ /* * OS/2 SOCKETS API errors redefined as regular BSD error constants */ #define EWOULDBLOCK SOCEWOULDBLOCK #define EINPROGRESS SOCEINPROGRESS #define EALREADY SOCEALREADY #define ENOTSOCK SOCENOTSOCK #define EDESTADDRREQ SOCEDESTADDRREQ #define EMSGSIZE SOCEMSGSIZE #define EPROTOTYPE SOCEPROTOTYPE #define ENOPROTOOPT SOCENOPROTOOPT #define EPROTONOSUPPORT SOCEPROTONOSUPPORT #define ESOCKTNOSUPPORT SOCESOCKTNOSUPPORT #define EOPNOTSUPP SOCEOPNOTSUPP #define EPFNOSUPPORT SOCEPFNOSUPPORT #define EAFNOSUPPORT SOCEAFNOSUPPORT #define EADDRINUSE SOCEADDRINUSE #define EADDRNOTAVAIL SOCEADDRNOTAVAIL #define ENETDOWN SOCENETDOWN #define ENETUNREACH SOCENETUNREACH #define ENETRESET SOCENETRESET #define ECONNABORTED SOCECONNABORTED #define ECONNRESET SOCECONNRESET #define ENOBUFS SOCENOBUFS #define EISCONN SOCEISCONN #define ENOTCONN SOCENOTCONN #define ESHUTDOWN SOCESHUTDOWN #define ETOOMANYREFS SOCETOOMANYREFS #define ETIMEDOUT SOCETIMEDOUT #define ECONNREFUSED SOCECONNREFUSED #define ELOOP SOCELOOP #define ENAMETOOLONG SOCENAMETOOLONG #define EHOSTDOWN SOCEHOSTDOWN #define EHOSTUNREACH SOCEHOSTUNREACH #define ENOTEMPTY SOCENOTEMPTY ═══ 14. Well-Known Port Assignments ═══ The following table is a list of the common well-known ports supported by TCP/IP. It provides the port number, keyword, and a description of the reserved port assignment. Port numbers of less than 1024 are reserved for system applications. You can also find a complete list of well-known port numbers in the ETC\SERVICES file. TCP Well-Known Port Assignments ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 19. TCP Well-Known Port Assignments │ ├─────────────────┬────────────┬──────────────────────────────────┬────────────────────────────────────────────────────┤ │ PORT NUMBER │ KEYWORD │ RESERVED FOR │ SERVICES DESCRIPTION │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 0 │ │ reserved │ │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 5 │ RJE │ remote job entry │ remote job entry │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 7 │ ECHO │ echo │ echo │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 9 │ DISCARD │ discard │ sink null │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 11 │ SYSTAT │ active users │ active users │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 13 │ DAYTIME │ daytime │ daytime │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 15 │ NETSTAT │ Netstat │ who is up or Netstat │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 19 │ CHARGEN │ ttytst source │ character generator │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 21 │ FTP │ FTP │ File Transfer Protocol │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 23 │ TELNET │ Telnet │ Telnet │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 25 │ SMTP │ mail │ Simple Mail Transfer Protocol │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 37 │ TIME │ timeserver │ timeserver │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 39 │ RLP │ resource │ Resource Location Protocol │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 42 │ NAMESERVER │ name │ host name server │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 43 │ NICNAME │ who is │ who is │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 53 │ DOMAIN │ name server │ domain name server │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 57 │ MTP │ private terminal access │ private terminal access │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 67 │ BOOTPS │ bootps dhcps │ bootp server │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 68 │ BOOTPC │ bootpc dhcpc │ bootp client │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 69 │ TFTP │ TFTP │ Trivial File Transfer Protocol │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 70 │ GOPHER │ gopher │ Gopher │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 77 │ │ netrjs │ any private RJE service │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 79 │ FINGER │ finger │ finger │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 80 │ WWW-HTTP │ www-http │ World Wide Web HTTP │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 87 │ LINK │ ttylink │ any private terminal link │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 95 │ SUPDUP │ supdup │ SUPDUP Protocol │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 101 │ HOSTNAME │ hostname │ nic hostname server, usually from SRI-NIC │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 109 │ POP │ postoffice │ Post Office Protocol │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 111 │ SUNRPC │ sunrpc │ Sun remote procedure call │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 113 │ AUTH │ authentication │ authentication service │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 115 │ SFTP │ sftp │ Simple File Transfer Protocol │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 117 │ UUCP-PATH │ UUCP path service │ UUCP path service │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 119 │ UNTP │ readnews untp │ USENET News Transfer Protocol │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 123 │ NTP │ NTP │ Network Time Protocol │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 160 │ │ reserved │ │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 161 │ SNMP │ SNMP Agent │ SNMP Agent receives packets │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 162 │ SNMPTRAP │ SNMP Client │ SNMP Client receives SNMP TRAPs │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 163-223 │ │ reserved │ │ └─────────────────┴────────────┴──────────────────────────────────┴────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 19. TCP Well-Known Port Assignments │ ├─────────────────┬────────────┬──────────────────────────────────┬────────────────────────────────────────────────────┤ │ PORT NUMBER │ KEYWORD │ RESERVED FOR │ SERVICES DESCRIPTION │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 449 │ AS-SVRMAP │ mapper function for AS/400 │ servers for signon, central management, network │ │ │ │ servers │ print, database, stream file, data queue, and │ │ │ │ │ remote command and distributed program calls. │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 712 │ VEXEC │ vice-exec │ Andrew File System authenticated service │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 713 │ VLOGIN │ vice-login │ Andrew File System authenticated service │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 714 │ VSHELL │ vice-shell │ Andrew File System authenticated service │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 2001 │ FILESRV │ │ Andrew File System service │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 2106 │ VENUS.ITC │ │ Andrew File System service, for the Venus process │ └─────────────────┴────────────┴──────────────────────────────────┴────────────────────────────────────────────────────┘ UDP Well-Known Port Assignments ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Table 20. UDP Well-Known Port Assignments │ ├─────────────────┬────────────┬──────────────────────────────────┬────────────────────────────────────────────────────┤ │ PORT NUMBER │ KEYWORD │ RESERVED FOR │ SERVICES DESCRIPTION │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 0 │ │ reserved │ │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 5 │ RJE │ remote job entry │ remote job entry │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 7 │ ECHO │ echo │ echo │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 9 │ DISCARD │ discard │ sink null │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 11 │ USERS │ active users │ active users │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 13 │ DAYTIME │ daytime │ daytime │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 15 │ NETSTAT │ Netstat │ Netstat │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 19 │ CHARGEN │ ttytst source │ character generator │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 37 │ TIME │ timeserver │ timeserver │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 39 │ RLP │ resource │ Resource Location Protocol │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 42 │ NAMESERVER │ name │ host name server │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 43 │ NICNAME │ who is │ who is │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 53 │ DOMAIN │ name server │ domain name server │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 67 │ BOOTPS │ bootps dhcps │ bootp server │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 68 │ BOOTPC │ bootpc dhcpc │ bootp client │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 69 │ TFTP │ TFTP │ Trivial File Transfer Protocol │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 70 │ GOPHER │ gopher │ Gopher │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 75 │ │ │ any private dial out service │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 77 │ │ netrjs │ any private RJE service │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 79 │ FINGER │ finger │ finger │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 80 │ WWW-HTTP │ www-http │ World Wide Web HTTP │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 111 │ SUNRPC │ sunrpc │ Sun remote procedure call │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 123 │ NTP │ NTP │ Network Time Protocol │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 135 │ LLBD │ NCS LLBD │ NCS local location broker daemon │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 160-223 │ │ reserved │ │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 531 │ RVD-CONTROL│ │ rvd control port │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 2001 │ RAUTH2 │ │ Andrew File System service, for the Venus process │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 2002 │ RFILEBULK │ │ Andrew File System service, for the Venus process │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 2003 │ RFILESRV │ │ Andrew File System service, for the Venus process │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 2018 │ CONSOLE │ │ Andrew File System service │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 2115 │ ROPCONS │ │ Andrew File System service, for the Venus process │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 2131 │ RUPDSRV │ │ assigned in pairs; bulk must be SRV +1 │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 2132 │ RUPDBULK │ │ assigned in pairs; bulk must be SRV +1 │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 2133 │ RUPDSRV1 │ │ assigned in pairs; bulk must be SRV +1 │ ├─────────────────┼────────────┼──────────────────────────────────┼────────────────────────────────────────────────────┤ │ 2134 │ RUPDBULK1 │ │ assigned in pairs; bulk must be SRV +1 │ └─────────────────┴────────────┴──────────────────────────────────┴────────────────────────────────────────────────────┘ ═══ 15. Notices ═══ First Edition (December 1995) The following paragraph does not apply to the United Kingdom or any country where such provisions are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer of express or implied warranties in certain transactions, therefore, this statement may not apply to you. This publication could include technical inaccuracies or typographical errors. Changes are periodically made to the information herein; these changes will be incorporated in new editions of the publication. IBM may make improvements and/or changes in the product(s) and/or the program(s) described in this publication at any time. It is possible that this publication may contain reference to, or information about, IBM products (machines and programs), programming, or services that are not announced in your country. Such references or information must not be construed to mean that IBM intends to announce such IBM products, programming, or services in your country. Requests for technical information about IBM products should be made to your IBM reseller or IBM marketing representative. ═══ 15.1. Copyright Notices ═══ COPYRIGHT LICENSE: This publication contains printed sample application programs in source language, which illustrate OS/2 programming techniques. You may copy, modify, and distribute these sample programs in any form without payment to IBM, for the purposes of developing, using, marketing or distributing application programs conforming to the OS/2 application programming interface. (C)Copyright International Business Machines Corporation 1995. All rights reserved. Note to U.S. Government Users - Documentation related to restricted rights - Use, duplication or disclosure is subject to restrictions set forth in GSA ADP Schedule Contract with IBM Corp. IBM is required to include the following statements in order to distribute portions of this document and the software described herein. Sun RPC is a product of Sun Microsystems, Inc. and is provided for unrestricted use provided that this legend is included on all tape media and as a part of the software program in whole or part. Users may copy or modify Sun RPC without charge, but are not authorized to license or distribute it to anyone else except as part of a product or program developed by the user. SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. Sun RPC is provided with no support and without any obligation on the part of Sun Microsystems, Inc. to assist in its use, correction, modification or enhancement. SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC OR ANY PART THEREOF. In no event will Sun Microsystems, Inc. be liable for any lost revenue or profits or other special, indirect and consequential damages, even if Sun has been advised of the possibility of such damages. Copyright (c) 1989 The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.  All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors.  Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Some portions of this publication relating to X Window System are Copyright (c) 1987, 1988 by the Massachusetts Institute of Technology, Cambridge, Massachusetts; by Digital Equipment Corporation, Maynard, Massachusetts; and by Hewlett-Packard Corporation. All Rights Reserved. Permission to use, copy, modify, and distribute the M.I.T., Digital Equipment Corporation, and Hewlett-Packard Corporation portions of this software and its documentation for any purpose without fee is hereby granted, provided that the above copyright notice appears in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of M.I.T., Digital, and Hewlett-Packard not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. M.I.T., Digital, and Hewlett-Packard make no representation about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ═══ 15.2. Disclaimers ═══ References in this publication to IBM products, programs, or services do not imply that IBM intends to make these available in all countries in which IBM operates. Any reference to an IBM product, program or service is not intended to state or imply that only IBM's product, program, or service may be used. Subject to IBM's valid intellectual property or other legally protectable rights, any functionally equivalent product, program, or service may be used instead of the IBM product, program, or service. Evaluation and verification of operation in conjunction with other products, programs, or services, except those expressly designated by IBM, are the user's responsibility. IBM may have patents or pending patent applications covering subject matter in this document. The furnishing of this document does not give you any license to these patents. You can send license inquiries, in writing, to the IBM Director of Licensing, IBM Corporation, 500 Columbus Avenue, Thornwood, NY 10594, U.S.A. Licensees of this program who wish to have information about it for the purpose of enabling: (i) the exchange of information between independently created programs and other programs (including this one) and (ii) the mutual use of the information which has been exchanged, should contact IBM Corporation, Department RM1A, 1000 N.W. 51st Street, Boca Raton, FL 33431, U.S.A. Such information may be available, subject to appropriate terms and conditions, including in some cases, payment of a fee. ═══ 15.3. Trademarks ═══ The following terms are trademarks of the IBM Corporation in the United States or other countries or both: AS/400 C Set++ IBM OS/2 Operating System/2 VisualAge The following terms are trademarks of other companies: UNIX is a trademark of UNIX System Laboratories, Inc. Other company, product, and service names, which may be denoted by a double asterisk (**), may be trademarks or service marks of others.