Allegro CL Socket Library

The index for the Allegro CL Documentation is in index.htm. The documentation is described in introduction.htm.

This document contains the following sections:

1.0 Introduction and background
2.0 Characteristics
3.0 Stream Sockets
   3.1 Connections
   3.2 Host Naming
4.0 Variables
5.0 Functions
6.0 Errors
7.0 Examples
8.0 Secure Socket Layer (SSL)
   8.1 SSL History
   8.2 Secure connections
   8.3 Client/Server
   8.4 Authentication
   8.5 Certificates
   8.6 Installation
   8.7 The Allegro CL SSL API

1.0 Introduction and background

Sockets are a mechanism for interprocess communication designed at U.C. Berkeley for use in their version of Unix. Sockets have been added to many other versions of Unix and there is an implementation of sockets for Windows called Winsock. This document describes the Allegro interface to sockets. This interface works on Unix and on Windows.

Symbols naming objects in the socket utility are in the acl-socket package. It has the nickname socket.

The socket module is not included in all versions of Allegro CL. If it is present, it is (by default) included in a development image (one built with the include-devel-env argument to build-lisp-image specified true). To load the socket module if it is not present in an image, evaluate

(require :sock)

Note that runtime images cannot include the development environment (so include-devel-env must be specified nil when a runtime image is being built). If the socket module is needed, it must be loaded when the image is built. See runtime.htm, building-images.htm and delivery.htm for more information.

2.0 Characteristics

There are three independent characteristics of sockets:

type

Valid values: :stream or :datagram.

A :stream socket offers a reliable, two-way, stream connection between sockets. Reliable means that what you send is received at the other end in the exact order you sent it. Stream means that the receiver reads a stream of bytes and sees no record boundaries. It uses the internet protocol TCP.

A :datagram socket offers unreliable, one-way, connectionless packet communication. Unreliable means that the packet may or may not be delivered. Packets may be delivered in an order other than in the order they were sent. Record boundaries are maintained: if the sender sends two ten byte packets and if the packets get through, the receiver will receive two ten byte packets rather than one twenty byte packet. For each packet you send you must give the destination address. It uses the internet protocol UDP.

address family

Valid values: :internet or :file.

In order to send to another socket the socket must have a name.

An :internet socket is named by a 32-bit host number and a 16-bit port number. On Unix, port numbers less than 1024 can only be allocated by a process with the user id of root. A :file socket is named by a file on a local disk. This is called the Unix address family but we've chosen to call it the :file address family since it really isn't Unix specific. This address family can only permit processes on the same machine to communicate.

Note that the current version of the socket interface on Windows (Winsock, version 1.1), does not support the :file address family.

format

Valid values: :text or :binary, or, for :stream sockets only, :bivalent (see note below)

This isn't a property of the Unix socket implementation but is instead something we've added for the Common Lisp implementation since a Lisp stream is either binary (supports read-byte, etc.) or text (supports read-char, etc.).

Note on bivalent format:

Starting in release 5.0.1, the bivalent format is accepted for stream sockets. Bivalent means that the stream will accept text and binary stream functions. That is, you can write-byte or write-char, read-byte or read-char. A bivalent stream is useful in the http protocol (used between web browsers and web servers) since in that protocol the header data is sent in text format and the body can be in binary data (image files, for example).

Internally a bivalent socket stream is configured like a binary socket stream with 8 bit bytes. Character position is not maintained.

Bivalent socket streams have very efficient read-sequence and write-sequence implementations (as long as the sequence is either a vector of element-type charcter, (unsigned-byte 8) or (signed-byte 8)).

Bivalent socket streams also support the chunking protocol found in http/1.1. This protocol allows the sender to signal end of file without closing down the stream.

3.0 Stream Sockets

3.1 Connections

Stream sockets have a fourth characteristic called connect, with a value :active or :passive. In order to use stream sockets you have to set up a link between two of them. That link is called a connection. You set up a connection in this way:

  1. Machine A: create a passive socket at port port-b:
    (setq s-a (make-socket :connect :passive :local-port port-b))
  2. Machine B: create an active socket telling it to connect to Machine A, port port-b:
    (setq s-b (make-socket :remote-host "machine-a" :remote-port port-b))
  3. Machine A: wait for a connect request from anyone and when it occurs return a stream for I/O:
    (setq str-a (accept-connection s-a))
  4. When the accept-connection returns, machine A can use stream str-a to send messages to machine B and machine B can use stream s-b to send messages to machine A.

Note that steps 2 and 3 can occur in either order.

Note the asymmetry: a passive socket is not a Lisp stream (you can't do read and write to it). An active socket is a Lisp stream.

When accept-connection is called on a passive socket, it does not return until a connection is made to the passive socket. The value accept-connection returns is a stream.

As long as the passive socket is not closed, new connections can still be made to the port of that socket.

An active socket can be used for only one connection. Once that connection has been made, the socket should be closed and a new active socket created.

3.2 Host Naming

Host naming conventions: this package supports three conventions for naming a host:

hostnameA string using the domain naming convention, e.g. "ftp.franz.com". The domain naming system is case-insensitive.
dottedA string which is the printed representation of the numeric address: e.g. "192.132.95.84". We also support the non standard Berkeley extensions to this format for class A addresses: "23.3" (which is the same as "23.0.0.3") and class B addresses "128.1.3" (which is the same as "128.1.0.3").
ipaddrAn unsigned 32-bit number, representing the IP address in the native byte order for the host. Thus 192.132.95.84 is 192*2^24 + 132*2^16 + 95*2^8 + 84 = 3229900628.

4.0 Variables

The variables defined by the interface are:

*socket-version*

Please provide the value of this variable when asking for technical support with sockets as it tells us whether you have the latest version.

*print-hostname-in-stream*

This variable controls whether the socket printing code converts the ip address of a socket into a hostname. This is usually what you want, however this can be a slow process (taking up to a minute to accomplish). The default value for this variable is t. See the full description for a discussion of the causes of the possible slowdown when the value is t.

5.0 Functions

The first table shows general functions defined by the interface and the second shows accessors.

FunctionArgumentsNotes (follow function link for full description)
accept-connection(sock passive-socket) &key waitGeneric function. Establishes a connection. If wait is nil and no connection is pending, returns nil and does nothing further. If wait is true (the default), waits until a connection is established. When a connection is established, returns the stream that communicates with the socket.
dotted-to-ipaddrdotted &key errorpFunction. Converts a string like "192.132.95.84" or similar format to an unsigned 32-bit IP address.
ipaddr-to-dottedipaddr &key valuesFunction. Convert a 32-bit unsigned IP address, ipaddr, to a string in dotted form.
ipaddr-to-hostnameipaddrFunction. Returns, as a string, the hostname of the machine with the given 32-bit IP address, ipaddr.
lookup-hostname hostnameGiven a string naming a host, a 32-bit IP address, or a string in dotted form, return the 32-bit IP address for the host.
lookup-portportname protocolFunction. Finds the port number using the symbolic name and the protocol.
make-socket&key type format address-family connect &allow-other-keysFunction. See the full description for details.
with-pending-connect&body bodyMacro. See the full description for details.
receive-from(sock datagram-socket) size &key buffer extractGeneric function. This is used to read from a datagram socket.
send-tosock &keyGeneric function with methods for internet-datagram-sockets and file-datagram-sockets
shutdownsock &key directionGeneric function that closes down the specified half of the bidirectional socket connection.
socket-controlstream &key output-chunking output-chunking-eof input-chunkingThis function modifies the state of the socket stream, controlling input and output chunking.
socket-os-fdsockGeneric function. Return the operating system file descriptor associated with this socket.

Socket Accessors

These functions retrieve slot values from socket instances. The values of these slots are set when the socket is created.

FunctionArgumentsNotes (follow function link for full description)
remote-hostsocketGeneric function. Returns an IP address.
local-hostsocketGeneric function. Returns an IP address.
local-portsocket

All are generic functions. All return the values of the particular attribute for socket.

Note: Both internet stream and internet datagram sockets use 16-bit port numbers.

Note that stream (tcp) port N is totally distinct from datagram (udp) port N.

remote-filenamesocket
local-filenamesocket
remote-portsocket
socket-address-familysocket
socket-connectsocket
socket-formatsocket
socket-typesocket

6.0 Errors

When errors are raised by the socket interface, Lisp conditions are signaled. This section describes those conditions.

A condition is a CLOS class and thus fits into the hierarchy of CLOS classes. The condition socket-error is a subclass of the condition stream-error.

socket-error is the superclass for all socket related errors. See More on cl:stream-error in errors.htm.

socket-error denotes operating system detected socket errors. It has the following slots:

NameReader functionWhat
excl::identifierstream-error-identifierSymbol denoting this error (see table below)
excl::codestream-error-codeOperating system dependent error code (if any)
excl::actionstream-error-actionString describing the operation in progress when the error occurred

Handling socket error is difficult because the error returned in exceptional situations can depend on the operating system and the address of the other side of the connection. For example, attempting to make a connection to a machine that is down may result in a "Connection Timed Out" or a "Host Unreachable" error, or maybe something else on certain systems.

The error codes assigned to socket errors vary from operating system to operating system. We translate a large set of the common error codes from a machine dependent number to a symbol which we call the identifier to make it easier for you to write portable code. Condition handling code should check the identifier field (using stream-error-identifier) If the identifier value is :unknown then this is not a common socket error and the operating system dependent code value of the condition must be used.

Possible identifier values and their meanings:

IdentifierMeaning
:address-in-useLocal socket address already in use
:address-not-availableLocal socket address not available
:network-downNetwork is down
:network-resetNetwork has been reset
:connection-abortedConnection aborted
:connection-resetConnection reset by peer
:no-buffer-spaceNo buffer space
:shutdownConnection shut down
:connection-timed-outConnection timed out
:connection-refusedConnection refused
:host-downHost is down
:host-unreachableHost is unreachable
:unknownUnknown error

7.0 Examples

Create an active stream socket connection to a socket that just prints characters to whomever connects to it. After connecting, read the first five characters and print them out.
USER(1): (let ((s (make-socket :remote-host "vapor" :remote-port "chargen")))
           (dotimes (i 5) (print (read-char s))) (close s))
#\space
#\!
#\"
#\#
#\$

Sending a message from frisky to vapor:

on vapor:

USER(1): (print (read (accept-connection
                       (make-socket :connect :passive :local-port 9933)))).. this hangs ...

on frisky:

USER(1): (let ((s (make-socket :remote-host "vapor" :remote-port 9933)))
    (format s "Secret-message~%") (close s))

Then you see on vapor:

Secret-message
Secret-message
USER(2): 

A flaw in this example is that on vapor we've left the socket and the stream open and we lost track of the objects to close them. So, while concise, this is not a good programming style.

Another problem with this example is that when we created the port on vapor we used a specific port number (9933). This means our program will fail if port 9933 is already in use. If possible, it is best to let the system choose a port number (this is done by not specifying a :local-port argument) and then using the local-port function to find out which port was chosen.

If we just want to send a simple message then datagrams might be more appropriate (although the program must guarantee that the message made it because datagram communication is unreliable).

on vapor:

user(2): (setq s (make-socket :type :datagram :local-port 9999))
#<text datagram socket waiting for connection at */9999 @ #x20664e82>
user(3):  

on frisky:

user(10): (setq x (make-socket :type :datagram))
#<text datagram socket waiting for connection at */45602 @ #x20717fb2>
user(11): (send-to x "foo-the-bar" 11 :remote-host "ultra" :remote-port 9999)
11
user(12): 

on vapor:

user(3): (receive-from s 100 :extract t)
"foo-the-bar"
11 ;; length of result
3229900653 ;; frisky's IP address
45602 ;; the port number chosen for the socket by frisky
user(4):

8.0 Secure Socket Layer (SSL)

Allegro CL supports Secure Socket layers as described in this section. See also aserve/aserve.html, which describe Webserver support in Allegro CL.

The SSL factility is not available in all versions. Generally, you must have an Enterprise license to use this function. Also, you must have the OpenSSL libraries installed for the facility to work. Note that shared library versions of the OpenSSL libraries (required by Allegro CL) are not available on all platforms. On platforms where they were available at the time of the Allegro CL 6.0 release, they are included on the distribution CD. (Those platforms are Windows, Solaris, HP (32-bit versions only), Linux (all varieties) and FreeBSD.

The SSL functionality is in the ssl module. To ensure it is loaded, evaluate (require :ssl). Calling either of the two SSL functions, make-ssl-client-stream and make-ssl-server-stream, automatically loads that module. But note if you are including the SSL facility in an application intended for delivery, be sure to include the module by adding the keyword :ssl to the list which is the value of the input-file to generate-application.

8.1 SSL History

In 1994 Netscape Corpororation designed the Secure Socket Layer (SSL) protocol to provide a means of safely and securely transporting private data (such as credit card numbers) between a Web Browser and a Web Server. Rather than tie SSL to the http protocol, Netscape wrote it as a protocol for making any TCP/IP connection secure.

At the end of 1994 version 2 of SSL was introduced and this is first version shipped with a commercial web browser (Netscape Navigator (r)). In 1995 version 3 of SSL was introduced. At that point an international standards organization (IETF) took over work on SSL and renamed the SSL protocol to Transport Layer Security (or TLS). IETF introduced TLS version 1.0 in 1999.

Allegro CL, starting in release 6.0, provides an interface that supports SSL version 2, SSL version 3 and TLS version 1. When we use the name SSL, we mean SSL or TLS.

8.2 Secure connections

A secure TCP connection exists between two processes when both agree on the following:

These three items are determined via negotiation when the connection is made and the first data is to be sent.

8.3 Client/Server

In an SSL connection, one side is the client and the other side is the server. The first side to send data must be the client. Thus in the http environment, the web browser is the client and the web server is the server.

When a secure connection is started, the client starts the negotation by telling the server all the possible ways that it can communicate securely. The server then chooses one of the possible ways and informs the client.

Then the server sends its certificate and possibly other certificates if they are needed to prove that its certificate can be trusted. The important item in the certificate is the public key for the server. The client will use this public key to encrypt a random value which will be used by both the client and server to create the keys needed for the cipher chosen for data transmission.

In theory a certificate isn't necessary if both the client and server side support a key exchange algorithm that can generate a public key on the fly. The ssl libraries we use do not have this capability, thus you must always supply a server certificate.

Once both sides know the keys the other side will use to transmit, the secure data transmission can occur.

8.4 Authentication

The SSL protocol also permits each side of the connection to declare who they are. This is done by the exchange of certificates. The server must send a certificate describing itself to the client. The server can request that the client send a certificate to the server (although in the use of SSL on the web this is never done).

8.5 Certificates

A certificate is a digital document that stores information about an entity in such a way that it can be verified to be true. The primary use of certificates is to store the public key that can be used to send encrypted messages to the entity.

In the SSL protocol Certificates have two uses:

  1. Encrpytion - by providing a public key they enable encrypted messages to be sent.
  2. Authentication - the certificate proves that the entity on the other end of the socket is who it claims to be.

Strictly speaking a certificate isn't required for SSL communication if both sides support a certain key exchange protocol. The OpenSSL libraries we use do not support this protocol thus whenever you create a server SSL stream you must supply a certificate (if you don't have your own we supply one in <Allegro directory>/examples/ssl/server.pem that you can use).

While Certificates support authentication, the SSL protocol doesn't require that you take advantage of this facility. At this time our Lisp SSL API does not contain any functions to verify the authenticity of the certificate sent by other side of the connection. You will have to worry about authenticity issues if you create an SSL web server since web browers do check the validity of certificates.

A certificate contains the following:

  1. A Subject Identifier: a set of fields describing where the subject is geographically and its role within an organization.
  2. A Subject Public Key: the key that can be used to encrypt messages that only the Subject can decrypt since only the Subject has the associated private key.
  3. A Valid Time Interval: the interval of time during which this certificate is valid.
  4. An Issuer Identifier: just like the Subject Identifier but describing the entity that certifies that the Subject is who it says it is and that the public key is the correct one for the subject.
  5. An Issuer signature: a value which can be used by anyone to verify that the Issuer and only the Issuer signed this document testifying to it being correct.
  6. Various other fields: like serial numbers, version numbers, and other minor things.

A certificate is a combination of text and binary data and in order to make it easy to transport certificates they are usually encoded in a form called PEM which turns them in a sequence of printable characters.

When a web browser connects to a site via SSL (which is caused by the use of the 'https:' at the beginning of the url), it checks three things about the certificate:

  1. Does it know the Issuer and did the Issuer sign the certificate? A web browser knows about a set of Issuers (called Certificate Authorites) when it's installed on the machine (the Issuer certificates are part of the files that make up the web browser).
  2. Is the certificiate valid right now or has it expired?
  3. Is the certificate for the machine we've contacted? If the url was https://www.foo.com/whatever then the certificate must be for www.foo.com. The convention used is to store the name of the server machine in the CommonName slot of the Subject Identifier field of the certificate.

If all three tests pass then the web browser silently accepts the certificate and does a secure web page access. If any of the tests fail then the web browser notifies the user and waits for a response. Each browser displays the failure differently. For example, the MicroSoft Internet Explorer (r) shows which of the three tests passed and which failed while the Netscape Navigator (r) just says that it received an invalid certificate. In both cases the person using the web browser is given the option of continuing with the web access. Transmission will still be secure if it is elected to continue. The only issue in doubt is the authenticity of the web server.

8.6 Installation

The Allegro CL SSL interface connects to the Openssl libraries (www.openssl.org). These libraries are not normally found on your machine (except that they may be supplied with some distributions of Linux). We've supplied these libraries for those platforms where there are available in shared-library format (Window, Solaris, HP, Linux, and FreeBSD) on the distribution CDs. See Installing OpenSSL libraries in installation.htm for information on how to install these libraries.

Repeating a bit of what we say in installation.htm, on Windows, after you have installed the libraries, be sure to run the System program in the Control Panel and edit your PATH variable to include the directory where you installed the openssl libraries. And on UNIX, after you have installed the libraries, be sure to add the lib/ subdirectory of your selected directory to the UNIX loaders search path. On Solaris and Linux the search path is stored in the LD_LIBRARY_PATH environment variable. On Hpux the environment variable SHLIB_PATH is used or LPATH.

On UNIX, you may also wish to add the bin/ subdirectory of to your shell's search path so you can access the openssl program that comes with the openssl libraries.

8.7 The Allegro CL SSL API

The two functions provided are make-ssl-client-stream and make-ssl-server-stream.

There is an example in <Allegro directyory>/examples/ssl/. See particularly in that directory the file server.pem, which is a sample certificate and private key file. You can use this file when starting the server side of an ssl connection.

Copyright (c) 1998-2000, Franz Inc. Berkeley, CA., USA. All rights reserved. Created 2000.10.5.