A


 
Network Programming with Perl
By Lincoln  D.  Stein
Slots : 1
Table of Contents
Chapter  22.   UNIX-Domain Sockets

    Content

Using UNIX-Domain Sockets

Like TCP/IP sockets, two applications that wish to communicate must rendezvous at an agreed-on name . Instead of using the combination of IP address and port number for rendezvous, UNIX-domain sockets use a path on the local file system, such as /dev/log . They are created automatically when the socket is bound and appear in UNIX directory listings with an "s" at the beginning of the permission string. For example:

 %  ls -l /dev/log  srw-rw-rw-   1 root     root          0 Jun 17 16:21 /dev/log 

The socket files are not automatically removed after the socket is closed, and must be unlinked manually.

The Perl documentation occasionally refers to these files as "fifo's" because they follow first-in-first-out rules: The first byte of data written by a sending application is the first byte of data read by the receiver. UNIX-domain sockets are similar in many ways to UNIX pipes (Chapter 2), and in fact the two are frequently implemented on top of a common code base.

The "UNIX" in UNIX-domain sockets is apt. Although a few platforms, such as OS/2, have facilities similar to UNIX-domain sockets, most operating systems, including Windows and Macintosh, do not support them. However, Windows users can get UNIX-domain sockets by installing the free Cygwin32 compatibility library. This library is available from http://www.cygnus.com/cygwin/.

UNIX-domain sockets are used by the standard UNIX syslog daemon (Chapter 12), the Berkeley lpd printer service, and a number of newer applications such as the XMMS MP3 player (http://www.xmms.org/). In the syslog system, client applications write log messages to a UNIX-domain socket, such as /dev/log . As described in Chapter 14, the syslog daemon reads these messages, filters them according to their severity, and writes them to one or several log files. The lpd printer daemon uses a similar strategy to receive print jobs from clients .

XMMS has a more interesting use for UNIX-domain sockets. By creating and monitoring a UNIX-domain socket, XMMS can exchange information with clients. Among other things, clients can send XMMS commands to play a song or change its volume, or retrieve information from XMMS about what it's currently doing. Doug MacEachern's Xmms module, available from CPAN, provides a Perl interface to XMMS sockets.

Perl provides both a function-oriented and an object-oriented interface to UNIX-domain sockets. We'll look at each in turn .

Function-Oriented Interface to UNIX-Domain Sockets

Creating UNIX-domain sockets with the function-oriented interface is similar to creating TCP/IP sockets. You call socket() to create the socket, connect() to make an outgoing connection, or bind() , listen() , and accept() to accept incoming connections.

To create a UNIX-domain socket, call socket() with a domain type of AF_UNIX and a protocol of PF_UNSPEC (protocol unspecified). These constants are exported by the socket module. You are free to create either SOCK_STREAM or SOCK_DGRAM sockets:

 use Socket; socket(S, AF_UNIX, SOCK_STREAM, PF_UNSPEC)               or die "Can't create stream socket: $! socket(D, AF_UNIX, SOCK_DGRAM, PF_UNSPEC)               or die "Can't create datagram socket: $! 

Having created the socket, we can make an outgoing connection to a waiting server by calling connect() . The chief difference is that we must create the rendezvous address using a pathname and the utility function sockaddr_un () . This code fragment tries to connect to a server listening at the address /tmp/daytime :

 my $dest = sockaddr_un('/tmp/daytime'); connect(S,$dest) or die "Can't connect: $!"; 

A UNIX-domain address is simply a pathname that has been padded to a fixed length with nulls and can be created with sockaddr_un() . The members of the sockaddr_un() family of functions are similar to their IP counterparts:

$packed_addr = sockaddr_un($path)

($path) = sockaddr_un($packed_addr)

In a scalar context, sockaddr_un() takes a file pathname and turns it into a UNIX-domain destination address suitable for bind() and connect() . In an array context, the sockaddr_un() reverses this operation, which is handy for interpreting the return value of recv() and getsockname() .

If this context-specific behavior makes you nervous, you can use the pack_sockaddr_un() and unpack_sockaddr_un() functions instead:

$packed_addr = pack_sockaddr_un($path)

pack_sockaddr_un() packs a file path into a UNIX domain address regardless of array or scalar context.

$path = unpack_sockaddr_un($packed_addr)

unpack_sockaddr_un() transforms a packed UNIX-domain socket into a file path, regardless of array or scalar context.

Servers must bind to a UNIX-domain address by calling bind() with the desired rendezvous address. This example binds to the socket named /tmp/daytime :

 bind(S,sockaddr_un('/tmp/daytime')) or die "Can't bind: $!"; 

If successful, bind() returns a true value. Common reasons for failure include:

"address already in use" (EADDRINUSE) The rendezvous point already exists, as a regular file, a regular directory, or a socket created by a previous invocation of your script. UNIX-domain servers must unlink the socket file before they exit.

"permission denied " (EACCES) Permissions deny the current process the ability to create the socket file at the selected location. The same rules that apply to creating a file for writing apply to UNIX-domain sockets. On UNIX systems the /tmp directory is often chosen by unprivileged scripts as the location for sockets.

"not a directory" (ENOTDIR) The selected path included a component that was not a valid directory. Additional errors are possible if the selected path is not local. For example, socket addresses on read-only filesystems or network-mounted filesystems are disallowed .

Once a UNIX-domain socket is created and initialized , it can be used like a TCP/IP socket. Programs can call read() , sysread () , print() , or syswrite() to communicate in a stream-oriented fashion, or send() and recv() to use a message-oriented API. Servers may accept new incoming connections with listen() and accept() .

The functions that return socket addresses, such as getpeername() , getsockname() , and recv() , return packed UNIX-domain addresses when used with UNIX-domain sockets. These must be unpacked with sockaddr_un() or unpack_sockaddr_un() to retrieve a human-readable file path.

You should be aware that some versions of Perl have a bug in the routines that return socket names . On such versions, the array forms of sockaddr_un() and unpack_sockaddr_un() will fail. This is not as bad as it sounds because UNIX-domain applications don't need to recover this information as frequently as TCP/IP applications do. However, if you do need to recover the pathname of the local or remote socket, you can work around the Perl bug by applying unpack() with a format of "x2z" to the value returned by getpeername() or getsockname() :

 $path = unpack "x2z",getpeername(S); 

Another thing to be aware of is that a UNIX-domain socket created by a client can connect() without calling bind() , just as one can with a TCP/IP socket. In this case, the system creates an invisible endpoint for communication, and getsockname() returns a path of length 0. This is roughly equivalent to the operating system's method of using ephemeral ports for outgoing TCP/IP connections.

Object-Oriented Interface to UNIX-Domain Sockets

The standard IO::Socket module provides object-oriented access to UNIX-domain sockets. Simply create an object of type IO::Socket::UNIX, and use it as you would a TCP/IP-based IO::Socket object. Compared to IO::Socket::INET, the main change is the new() object constructor, which takes a different set of named arguments. IO::Socket::UNIX adds the hostpath() and peerpath() methods (described next ) and does not support the TCP/IP-specific sockaddr () , sockport() , sockhost(>) , peeraddr() , or peerport() methods.

$socket = IO::Socket::UNIX-new('/path/to/socket')

The single-argument form of IO::Socket::UNIX->new() attempts to connect to the indicated UNIX-domain socket, assuming a socket type of SOCK_STREAM . If successful, it returns an IO::Socket::UNIX object.

$socket = IO::Socket::UNIX-new(arg1 => val1, arg2 => val2,...)

The named-argument form of new() takes a set of name=> value pairs and creates a new IO::Socket::UNIX object. The recognized arguments are listed in Table 22.1.

$path = $socket->hostpath()

The hostpath() method returns the path to the UNIX socket at the local end. The method returns undef for unbound sockets.

$path = $socket- peerpat>()

peerpath() returns the path to the UNIX socket at the remote end. The method returns undef for unconnected sockets.

Table 22.1 lists the arguments recognized by IO:: Socket::UNIX->new() . Typical scenarios include:

  • Create a socket and connect() it to the process listening on /var/log .

     $socket = IO::Socket::UNIX->new(Type=>SOCK_STREAM,                                 Peer=>'/dev/log'); 
  • Create a listening socket bound to /tmp/mysock . Allow up to SOMAXCONN incoming connections to wait in the incoming queue.

     $socket = IO::Socket::UNIX->new(Type => SOCK_STREAM,                                 Local => '/tmp/mysock',                                 Listen => SOMAXCONN); 
  • Create a UNIX-domain socket for use with outgoing datagram transmissions.

     $socket = IO::Socket::UNIX->new(Type => SOCK_DGRAM); 
  • Create a UNIX-domain socket bound to /tmp/mysock for use with incoming datagram transmissions.

 $socket = IO::Socket::UNIX->new(Type => SOCK_DGRAM,                                 Local=> '/tmp/mysock'); 
Table 22.1. Arguments to IO::Socket::UNIX->new()
Arguments Description Value
Type Socket type, defaults to SOCK_STREAM SOCK_STREAM or SOCK_DGRAM
Local Local socket path <path>
Peer Remote socket path <path>
Listen Queue size for listen <integer>

UNIX-Domain Sockets and File Permissions

Because UNIX-domain sockets use physical files as rendezvous points, the access mode of the socket file affects what processes are allowed access to it. This can be used to advantage as an access control mechanism.

When the bind() function (and the IO::Socket::UNIX->new() method) creates the socket file, the permissions of the resulting file are determined by the process's current umask. If umask is 0000, then the socket file is created with octal mode 0777 (all bits turned on). A directory listing shows world-writable symbolic permissions of srwxrwxrwx . This means that any process can connect to the socket and send and receive messages using it.

To restrict access to the socket, prior to creating it you can modify the umask using Perl's built-in umask() function. For example, a umask of octal 0117 creates socket files with permissions of srw-rw ----, allowing socket access only to processes running with the same user and group as the server. 0177 is even more restrictive and forbids access to all processes not running with the same user ID as the server. For example, a server running as root might want to create its sockets using this umask to prevent any client that does not also have root privileges from connecting.

If you encounter difficulties using UNIX-domain sockets, inspect the permissions of the socket files and adjust the umask if they are not what you want. In the examples that follow, we explicitly set the umask to 0111 prior to creating the socket. This creates a world-writable socket, allowing any process to connect, but turns off the execute bits, which are not relevant for socket files. An alternative strategy is to call the Perl chmod() function explicitly.

Server applications are free to use the peer's socket path as a form of authentication. Before servicing a request, they can recover the peer path and insist that the socket file be owned by a particular user or group, or that it has been created in a particular directory that only a designated user or group has access to.


   
Top


Network Programming with Perl
Network Programming with Perl
ISBN: 0201615711
EAN: 2147483647
Year: 2000
Pages: 173

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net