Sunday, September 5, 2010

A UNIX Domain Sockets

UNIX domain sockets are named with UNIX paths. For example, a socket might be named /tmp/foo. UNIX domain sockets communicate only between processes on a single host. Sockets in the UNIX domain are not considered part of the network protocols because they can be used to communicate only between processes on a single host.
Socket types define the communication properties visible to a user. The Internet domain sockets provide access to the TCP/IP transport protocols. The Internet domain is identified by the value AF_INET. Sockets exchange data only with sockets in the same domain.

Creating Sockets

The socket(3SOCKET) call creates a socket in the specified family and of the specified type.
s = socket(family, type, protocol);
If the protocol is unspecified (a value of 0), the system selects a protocol that supports the requested socket type. The socket handle (a file descriptor) is returned.
The family is specified by one of the constants defined in sys/socket.h. Constants named AF_suite specify the address format to use in interpreting names.
The following creates a datagram socket for intramachine use:
s = socket(AF_UNIX, SOCK_DGRAM, 0);
Set the protocol argument to 0, the default protocol, in most situations.

Local Name Binding

A socket is created with no name. A remote process has no way to refer to a socket until an address is bound to the socket. Communicating processes are connected through addresses. In the UNIX family, a connection is composed of (usually) one or two path names. UNIX family sockets need not always be bound to a name. If they are, bound, duplicate ordered sets such as local pathname or foreign pathname can never exist. The path names cannot refer to existing files.
The bind(3SOCKET) call enables a process to specify the local address of the socket. This creates the local pathname ordered set, while connect(3SOCKET) and accept(3SOCKET) complete a socket's association by fixing the remote half of the address. Use bind(3SOCKET) as follows:
bind (s, name, namelen);
The socket handle is s. The bound name is a byte string that is interpreted by the supporting protocols. UNIX family names contain a path name and a family. The example shows binding the name /tmp/foo to a UNIX family socket.
#include <sys/un.h>
 ...
struct sockaddr_un addr;
 ...
strcpy(addr.sun_path, "/tmp/foo");
addr.sun_family = AF_UNIX;
bind (s, (struct sockaddr *) &addr,
  strlen(addr.sun_path) + sizeof (addr.sun_family));
When determining the size of an AF_UNIX socket address, null bytes are not counted, which is why you can use strlen(3C).
The file name referred to in addr.sun_path is created as a socket in the system file name space. The caller must have write permission in the directory where addr.sun_path is created. The file should be deleted by the caller when it is no longer needed. Delete AF_UNIX sockets with unlink(1M).

Establishing a Connection

Connection establishment is usually asymmetric. One process acts as the client and the other as the server. The server binds a socket to a well-known address associated with the service and blocks on its socket for a connect request. An unrelated process can then connect to the server. The client requests services from the server by initiating a connection to the server's socket. On the client side, the connect(3SOCKET) call initiates a connection. In the UNIX family, this might appear as:
struct sockaddr_un server;
  server.sun.family = AF_UNIX;
   ...
  connect(s, (struct sockaddr *)&server, strlen(server.sun_path) 
         + sizeof (server.sun_family));
 

UNIX Domain Sockets

UNIX domain sockets are used to communicate with processes running on the same machine. Although Internet domain sockets can be used for this same purpose, UNIX domain sockets are more efficient. UNIX domain sockets only copy data; they have no protocol processing to perform, no network headers to add or remove, no checksums to calculate, no sequence numbers to generate, and no acknowledgements to send.
UNIX domain sockets provide both stream and datagram interfaces. The UNIX domain datagram service is reliable, however. Messages are neither lost nor delivered out of order. UNIX domain sockets are like a cross between sockets and pipes. You can use the network-oriented socket interfaces with them, or you can use the socketpair function to create a pair of unnamed, connected, UNIX domain sockets.

#include <sys/socket.h>

int socketpair(int domain, int type, int protocol,
 int sockfd[2]);

Returns: 0 if OK, 1 on error

Although the interface is sufficiently general to allow socketpair to be used with arbitrary domains, operating systems typically provide support only for the UNIX domain.

Examples_pipe Function Using UNIX Domain Sockets
Figure 17.13 shows the socket-based version of the s_pipe function previously shown in Figure 17.6. The function creates a pair of connected UNIX domain stream sockets.
Some BSD-based systems use UNIX domain sockets to implement pipes. But when pipe is called, the write end of the first descriptor and the read end of the second descriptor are both closed. To get a full-duplex pipe, we must call socketpair directly.

Figure 17.13. Socket version of the s_pipe function
#include "apue.h" #include <sys/socket.h> /* * Returns a full-duplex "stream" pipe (a UNIX domain socket) * with the two file descriptors returned in fd[0] and fd[1]. */ int s_pipe(int fd[2]) { return(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)); }

17.3.1. Naming UNIX Domain Sockets

Although the socketpair function creates sockets that are connected to each other, the individual sockets don't have names. This means that they can't be addressed by unrelated processes.
In Section 16.3.4, we learned how to bind an address to an Internet domain socket. Just as with Internet domain sockets, UNIX domain sockets can be named and used to advertise services. The address format used with UNIX domain sockets differs from Internet domain sockets, however.
Recall from Section 16.3 that socket address formats differ from one implementation to the next. An address for a UNIX domain socket is represented by a sockaddr_un structure. On Linux 2.4.22 and Solaris 9, the sockaddr_un structure is defined in the header <sys/un.h> as follows:
struct sockaddr_un { sa_family_t sun_family; /* AF_UNIX */ char sun_path[108]; /* pathname */ };
On FreeBSD 5.2.1 and Mac OS X 10.3, however, the sockaddr_un structure is defined as
struct sockaddr_un { unsigned char sun_len; /* length including null */ sa_family_t sun_family; /* AF_UNIX */ char sun_path[104]; /* pathname */ };
The sun_path member of the sockaddr_un structure contains a pathname. When we bind an address to a UNIX domain socket, the system creates a file of type S_IFSOCK with the same name.
This file exists only as a means of advertising the socket name to clients. The file can't be opened or otherwise used for communication by applications.
If the file already exists when we try to bind the same address, the bind request will fail. When we close the socket, this file is not automatically removed, so we need to make sure that we unlink it before our application exits.

Example
The program in Figure 17.14 shows an example of binding an address to a UNIX domain socket.
When we run this program, the bind request succeeds, but if we run the program a second time, we get an error, because the file already exists. The program won't succeed again until we remove the file.
$ ./a.out run the program UNIX domain socket bound $ ls -l foo.socket look at the socket file srwxrwxr-x 1 sar 0 Aug 22 12:43 foo.socket $ ./a.out try to run the program again bind failed: Address already in use $ rm foo.socket remove the socket file $ ./a.out run the program a third time UNIX domain socket bound now it succeeds
The way we determine the size of the address to bind is to determine the offset of the sun_path member in the sockaddr_un structure and add to this the length of the pathname, not including the terminating null byte. Since implementations vary in what members precede sun_path in the sockaddr_un structure, we use the offsetof macro from <stddef.h> (included by apue.h) to calculate the offset of the sun_path member from the start of the structure. If you look in <stddef.h>, you'll see a definition similar to the following:
#define offsetof(TYPE, MEMBER) ((int)&((TYPE *)0)->MEMBER)
The expression evaluates to an integer, which is the starting address of the member, assuming that the structure begins at address 0.


Unique Connections
A server can arrange for unique UNIX domain connections to clients using the standard bind, listen, and accept functions. Clients use connect to contact the server; after the connect request is accepted by the server, a unique connection exists between the client and the server..
First, we create a single UNIX domain socket by calling socket. We then fill in a sockaddr_un structure with the well-known pathname to be assigned to the socket. This structure is the argument to bind. Note that we don't need to set the sun_len field present on some platforms, because the operating system sets this for us using the address length we pass to the bind function.
Finally, we call listen (Section 16.4) to tell the kernel that the process will be acting as a server awaiting connections from clients. When a connect request from a client arrives, the server calls the serv_accept function.
The server blocks in the call to accept, waiting for a client to call cli_conn. When accept returns, its return value is a brand new descriptor that is connected to the client. (This is somewhat similar to what the connld module does with the STREAMS subsystem.) Additionally, the pathname that the client assigned to its socket (the name that contained the client's process ID) is also returned by accept, through the second argument (the pointer to the sockaddr_un structure). We null terminate this pathname and call stat. This lets us verify that the pathname is indeed a socket and that the permissions allow only user-read, user-write, and user-execute. We also verify that the three times associated with the socket are no older than 30 seconds. (Recall from Section 6.10 that the time function returns the current time and date in seconds past the Epoch.)
If all these checks are OK, we assume that the identity of the client (its effective user ID) is the owner of the socket. Although this check isn't perfect, it's the best we can do with current systems. (It would be better if the kernel returned the effective user ID to accept as the I_RECVFD ioctl command does.)
The client initiates the connection to the server by calling the cli_conn function.

No comments:

Post a Comment