[project @ 1996-01-08 20:28:12 by partain]
[ghc-hetmet.git] / ghc / lib / ghc / SocketPrim.lhs
diff --git a/ghc/lib/ghc/SocketPrim.lhs b/ghc/lib/ghc/SocketPrim.lhs
new file mode 100644 (file)
index 0000000..917b68f
--- /dev/null
@@ -0,0 +1,966 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1995
+%
+% Last Modified: Fri Jul 21 15:14:43 1995
+% Darren J Moffat <moffatd@dcs.gla.ac.uk>
+\section[Socket]{Haskell 1.3 Socket bindings}
+
+
+\begin{code}      
+module SocketPrim (
+
+    Socket,            
+    Family(..),                
+    SocketType(..),
+    SockAddr(..),
+    HostAddress(..),
+
+    socket,            -- :: Family -> SocketType -> Int -> IO Socket 
+    connect,           -- :: Socket -> SockAddr -> IO ()
+    bindSocket,                -- :: Socket -> SockAddr -> IO ()
+    listen,            -- :: Socket -> Int -> IO ()
+    accept,            -- :: Socket -> IO (Socket, SockAddr)
+    getPeerName,       -- :: Socket -> IO SockAddr
+    getSocketName,     -- :: Socket -> IO SockAddr
+
+    socketPort,                -- :: Socket -> IO Int
+
+    writeSocket,       -- :: Socket -> String -> IO Int
+    readSocket,                -- :: Socket -> Int -> IO (String, Int)
+    readSocketAll,     -- :: Socket -> IO String
+
+    socketToHandle,    -- :: Socket -> IO Handle
+
+-- Alternative read/write interface not yet implemented.
+--    sendto           -- :: Socket -> String -> SockAddr -> IO Int
+--    recvfrm          -- :: Socket -> Int -> SockAddr -> IO (String, Int)
+--    sendmsg          -- :: Socket -> Message -> MsgFlags -> IO Int
+--    recvmsg          -- :: Socket -> MsgFlags -> IO Message
+
+    shutdown,          -- :: Socket -> Int -> IO ()
+    sClose,            -- :: Socket -> IO ()
+
+    inet_addr,         -- :: String -> HostAddress
+    inet_ntoa,         -- :: HostAddress -> String
+
+    sIsConnected,      -- :: Socket -> IO Bool
+    sIsBound,          -- :: Socket -> IO Bool
+    sIsListening,      -- :: Socket -> IO Bool 
+    sIsReadable,       -- :: Socket -> IO Bool
+    sIsWritable,       -- :: Socket -> IO Bool
+
+
+-- Special Constants
+
+    aNY_PORT,
+    iNADDR_ANY,
+--    sOL_SOCKET,
+    sOMAXCONN,
+    maxListenQueue,
+
+
+-- The following are exported ONLY for use in the BSD module and
+-- should not be used else where.
+
+    packFamily, unpackFamily,
+    packSocketType,
+    packSockAddr, unpackSockAddr
+
+) where
+import CError
+import LibPosix
+import LibPosixUtil
+import PreludeGlaST
+import PreludePrimIO   ( newEmptyMVar, putMVar, _MVar )
+import PreludeStdIO
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+\subsection[Socket-SocketTypes]{Socket Types}
+%*                                                                     *
+%************************************************************************
+
+
+There are a few possible ways to do this.  The first is convert the
+structs used in the C library into an equivalent Haskell type. An
+other possible implementation is to keep all the internals in the C
+code and use an Int\# and a status flag. The second method is used here
+since a lot of the C structures are not required to be manipulated.
+Originally the status was non mutable so we had to return a new socket
+each time we changed the status.  This version now uses mutable
+variables to avoid the need to do this.         The result is a cleaner
+interface and better security since the application programmer now
+can't circumvent the status information to perform invalid operations
+on sockets.          
+
+
+\begin{code}  
+data SocketStatus = 
+                -- Returned Status               Function called
+                  NotConnected                 -- socket
+                | Bound                        -- bindSocket
+                | Listening                    -- listen
+                | Connected                    -- connect/accept
+                | Error String                 -- Any
+                  deriving (Eq, Text)
+
+data Socket = MkSocket 
+               Int                                     -- File Descriptor Part
+               Family
+               SocketType
+               Int                                     -- Protocol Number
+               (MutableVar _RealWorld SocketStatus)    -- Status Flag
+
+
+\end{code}
+
+In C bind takes either a $struct sockaddr_in$ or a $struct
+sockaddr_un$ but these are always type cast to $struct sockaddr$.  We
+attempt to emulate this and provide better type checking. Note that
+the socket family fields are redundant since this is caputured in the
+constructor names, it has thus be left out of the Haskell $SockAddr$
+data type.
+
+
+\begin{code}
+type HostAddress = _Word
+
+data SockAddr =                -- C Names                              
+    SockAddrUnix       -- struct sockaddr_un
+       String          -- sun_path
+                 
+  | SockAddrInet       -- struct sockaddr_in
+       Int             -- sin_port
+       HostAddress     -- sin_addr
+
+    deriving Eq
+      
+\end{code}
+
+
+
+%************************************************************************
+%*                                                                     *
+\subsection[Socket-Connections]{Connection Functions}
+%*                                                                     *
+%************************************************************************
+
+
+In the following connection and binding primitives.  The names of the
+equivalent C functions have been preserved where possible. It should
+be noted that some of these names used in the C library, bind in
+particular, have a different meaning to many Haskell programmers and
+have thus been renamed by appending the prefix Socket.
+
+
+Create an unconnected socket of the given family, type and protocol.
+The most common invocation of $socket$ is the following:
+\begin{verbatim}
+   ...
+   socket AF_INET Stream 6     >>= \ my_socket ->
+   ...
+\end{verbatim}
+
+\begin{code}      
+socket :: Family ->    -- Family Name (usually AF_INET)
+         SocketType -> -- Socket Type (usually Stream)
+         Int ->        -- Protocol Number (getProtocolByName to find value)
+         IO Socket     -- Unconnected Socket
+
+socket family stype protocol = 
+    _ccall_ socket (packFamily family) (packSocketType stype) protocol
+                                               `thenPrimIO` \ s -> 
+    if s == -1 then
+       getCErrorCode                           `thenPrimIO` \ errno ->
+       (case errno of              
+           EACCES ->
+               fail "socket: Permission Denied"
+           EMFILE  ->
+               fail "socket: No more descriptiors available"
+           ENFILE  ->
+               fail "socket: System file table is full"
+           ENOBUFS  ->
+               fail "socket: Insufficient Buffer space to create socket"
+           EPROTONOSUPPOR ->
+               fail ("socket: Protocol " ++ show protocol ++
+                       " not supported for Family " ++ show family)
+           EPROTOTYPE ->
+               fail ("socket: Protocol " ++ show protocol ++
+                       " wrong type for socket")
+           _   ->
+               fail ("socket: " ++ (errorCodeToStr errno))
+       )
+    else
+       newVar NotConnected                     `thenPrimIO` \ status ->
+       return (MkSocket s family stype protocol status)
+\end{code}
+      
+Given a port number this {\em binds} the socket to that port. This
+means that the programmer is only interested in data being sent to
+that port number. The $Family$ passed to $bindSocket$ must
+be the same as that passed to $socket$.         If the special port
+number $aNY_PORT$ is passed then the system assigns the next
+available use port.
+
+Port numbers for standard unix services can be found by calling
+$getServiceEntry$.  These are traditionally port numbers below
+1000; although there are afew, namely NFS and IRC, which used higher
+numbered ports.
+
+The port number allocated to a socket bound by using $aNY_PORT$ can be
+found by calling $port$
+
+\begin{code}
+bindSocket :: Socket ->                        -- Unconnected Socket
+             SockAddr ->               -- Address to Bind to
+             IO ()
+
+bindSocket (MkSocket s family stype protocol status) addr =
+    readVar status                             `thenST` \ currentStatus ->
+    if currentStatus /= NotConnected then
+       fail ("bindSocket: can't peform bind on socket in status " ++
+           show currentStatus)
+    else
+       packSockAddr addr                               `thenPrimIO` \ addr' ->
+       let (_,sz) = boundsOfByteArray addr' in
+       _casm_ ``%r = bind(%0, (struct sockaddr*)%1, %2);''
+           s addr' sz                                  `thenPrimIO` \ result ->
+       if result == -1 then
+           getCErrorCode                               `thenPrimIO` \ errno ->
+           (case errno of
+           EACCES      ->
+               fail "bindSocket: The requested address is protected"
+           EADDRINUSE ->
+               fail "bindSocket: Address in use by another process"
+           EADDRNOTAVAIL ->
+               fail "bindSocket: Address not available"
+           EBADF       ->
+               fail "bindSocket: invalid descriptor"
+           EFAULT      ->
+               fail "bindSocket: name parameter not in vaild user address space"
+           EINVAL      ->
+               fail "bindSocket: namelen invalid size for given family"
+           ENOTSOCK ->
+               fail "bindSocket: attempt to bind a non socket descriptor"
+           _           ->
+               fail ("bindSocket: " ++ (errorCodeToStr errno))
+         )
+       else
+         writeVar status (Bound)                               `seqPrimIO`
+         return ()
+
+\end{code}
+       
+
+Make a connection to an already opened socket on a given machine and port.
+assumes that we have already called createSocket, othewise it will fail.
+                       
+This is the dual to $bindSocket$.  The {\em server} process will
+usually bind to a port number, the {\em client} will then connect to 
+the same port number.  Port numbers of user applications are normally
+agreed in advance, otherwise we must rely on some hacky mechanism for telling
+the {\em otherside} what port number we have been allocated.          
+
+\begin{code}
+connect :: Socket ->                   -- Unconnected Socket
+          SockAddr ->                  -- Socket address stuff
+          IO ()
+
+connect (MkSocket s family stype protocol status) addr =
+      readVar status                            `thenST` \ currentStatus ->
+      if currentStatus /= NotConnected then
+       fail ("connect: can't peform connect on socket in status " ++
+             show currentStatus)
+      else
+       packSockAddr addr                               `thenPrimIO` \ addr' ->
+       let (_,sz) = boundsOfByteArray addr' in
+       _casm_ ``%r = connect(%0,(struct sockaddr*)%1, %2);''
+                s addr' sz                             `thenPrimIO` \ result ->
+       if result == -1 then
+         getCErrorCode                                 `thenPrimIO` \ errno ->
+         (case errno of
+           EADDRINUSE ->
+               fail "connect: address in use"
+           EADDRNOTAVAIL ->
+               fail "connect: address not available on remote machine"
+           EAFNOSUPPORT ->
+               fail "connect: invalid socket address family"
+           EALREADY ->
+               fail ("connect: socket in non-blocking and previous " ++
+                    "attempt to connect not yet complteted")
+           EBADF ->
+               fail "connect: socket in not a vaild descriptor"
+           ECONNREFUSED ->
+               fail "connect: connection refused by peer"
+           EFAULT ->
+               fail "connect: address parameter outside process address space"
+           EINPROGRESS ->
+               fail ("connect: socket is non-blocking and connection can " ++
+                    "not be completed imediately")
+           EINTR ->
+               fail "connect: connection interrupted before delivery signal"
+           EINVAL ->
+               fail ("connect: namlen not size of valid address for " ++
+                    "specified family")
+           EISCONN ->
+               fail "connect: socket is already connected"
+           ENETUNREACH ->
+               fail "connect: network unreachable"
+           ENOTSOCK ->
+               fail "connect: file descriptor passed instead of socket"
+           ETIMEDOUT ->
+               fail "connect: timed out without establishing connection"
+           _   ->
+               fail ("connect: " ++ (errorCodeToStr errno))
+         )
+       else
+         writeVar status (Connected)                   `seqPrimIO`
+         return ()
+
+\end{code}
+       
+The programmer must call $listen$ to tell the system software
+that they are now interested in receiving data on this port.  This
+must be called on the bound socket before any calls to read or write
+data are made. 
+
+The programmer also gives a number which indicates the length of the
+incoming queue of unread messages for this socket. On most systems the
+maximum queue length is around 5.  To remove a message from the queue
+for processing a call to $accept$ should be made.      
+
+\begin{code}
+listen :: Socket ->                    -- Connected & Bound Socket
+         Int ->                        -- Queue Length
+         IO ()
+
+listen (MkSocket s family stype protocol status) backlog =
+      readVar status                            `thenST` \ currentStatus ->
+      if currentStatus /= Bound then
+       fail ("listen: can't peform listen on socket in status " ++
+             show currentStatus)
+      else
+       _ccall_ listen s backlog                        `thenPrimIO` \ result ->
+       if result == -1 then
+         getCErrorCode                                 `thenPrimIO` \ errno ->
+         (case errno of
+           EBADF ->
+               fail "listen: socket file descriptor invalid"
+           ENOTSOCK ->
+               fail "listen: file descriptor is not a socket"
+           EOPNOTSUPP ->
+               fail "listen: not supported fro this type of socket"
+           _   ->
+               fail ("listen: " ++ (errorCodeToStr errno))
+         )
+       else
+         writeVar status (Listening)                   `seqPrimIO`
+         return ()
+\end{code}
+
+A call to $accept$ only returns when data is available on the given
+socket, unless the socket has been set to non-blocking.         It will
+return a new socket which should be used to read the incoming data and
+should then be closed. Using the socket returned by $accept$ allows
+incoming requests to be queued on the original socket.
+
+
+\begin{code}
+accept :: Socket ->                    -- Queue Socket
+         IO (Socket,                   -- Readable Socket
+             SockAddr)                 -- Peer details
+
+accept sock@(MkSocket s family stype protocol status) =
+    readVar status                         `thenST` \ currentStatus ->
+    sIsAcceptable sock                     >>= \ okay ->
+    if not okay then    
+       fail ("accept: can't peform accept on socket in status " ++
+             show currentStatus)
+    else
+       allocSockAddr family                    `thenPrimIO` \ (ptr, sz) ->
+       _casm_ ``%r = accept(%0,(struct sockaddr*)%1, &%2);''
+               s ptr sz                                `thenPrimIO` \ sock ->
+       if sock == -1 then
+           getCErrorCode                               `thenPrimIO` \ errno ->
+           (case errno of
+               EBADF ->
+                   fail "accept: descriptor is invalid"
+               EFAULT ->
+                   fail "accept: addr is not in writeable part of address space"
+               ENOTSOCK ->
+                   fail "accept: descriptor is not a socket"
+               EOPNOTSUPP ->
+                   fail ("accept: socket not of type" ++ show stype)
+               EWOULDBLOCK ->
+                   fail "accept: would block"
+               _       ->
+                   fail ("accept: " ++ (errorCodeToStr errno))
+           )
+       else
+           unpackSockAddr ptr                  `thenPrimIO` \ addr ->
+           newVar Connected                    `thenPrimIO` \ status ->
+           return ((MkSocket sock family stype protocol status), addr)
+
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[Socket-DataPass]{Data Passing Primitives}
+%*                                                                     *
+%************************************************************************
+
+To allow Haskell to talk to C programs we need to beable to
+communicate interms of byte streams. $writeSocket$ and
+$readSocket$ should only be used for this purpose and not for
+communication between Haskell programs.         Haskell programs should use
+the 1.3 IO hPutStr and associated machinery for communicating with
+each other.
+
+
+\begin{code}
+writeSocket :: Socket ->               -- Connected Socket
+               String ->               -- Data to send
+               IO Int          -- Number of Bytes sent
+
+writeSocket (MkSocket s family stype protocol status) xs =
+    readVar status                             `thenST` \ currentStatus ->
+    if not ((currentStatus /= Connected) || (currentStatus /= Listening)) then
+        fail ("writeSocket: can't peform write on socket in status " ++
+           show currentStatus)
+    else
+       _ccall_ write s xs (length xs)          `thenPrimIO` \ nbytes ->
+       if nbytes == -1 then
+           getCErrorCode                       `thenPrimIO` \ errno ->
+           (case errno of
+               EBADF ->
+                   fail "writeSocket: invalid file descriptor"
+               EDQUOT ->
+                   fail "writeSocket: disk quota exhausted"
+               EFAULT ->
+                   fail "writeSocket: data area outside address space"
+               EFBIG ->
+                   fail "writeSocket: max file size limit exeeded"
+               EINTR ->
+                   fail "writeSocket: interupt received before data written"
+               EINVAL ->
+                   fail ("writeSocket: The stream is linked below a " ++
+                           "multiplexor. The fd pointer was negative")
+               ENOSPC ->
+                   fail "writeSocket: no space left on device"
+               ENXIO ->
+                   fail "writeSocket: hangup occured on stream"
+               EPIPE ->
+                   fail "writeSocket: attempt to write to unopened pipe"
+               ERANGE ->
+                   fail "writeSocket: to much data to write"
+               EWOULDBLOCK ->
+                   fail "writeSocket: would block"
+               EAGAIN ->
+                   fail "writeSocket: would block"
+               _ ->
+                   fail ("writeSocket: " ++ (errorCodeToStr errno))
+           )
+       else
+           return nbytes
+
+readSocket :: Socket ->                -- Connected Socket
+             Int ->            -- Number of Bytes to Read
+             IO (String, Int)  -- (Data Read, Number of Bytes)
+
+readSocket (MkSocket s family stype protocol status) nbytes =
+    readVar status                             `thenST` \ currentStatus ->
+    if not ((currentStatus /= Connected) || (currentStatus /= Listening)) then
+       fail ("readSocket: can't perform read on socket in status " ++
+           show currentStatus)
+    else
+--     newCharArray (0, nbytes)                `thenPrimIO` \ ptr \ ->
+       _casm_ ``%r = (char*)malloc(1+sizeof(char)*%0);''       nbytes  
+                                               `thenPrimIO` \ buffer ->
+       _ccall_ read s buffer nbytes            `thenPrimIO` \ result ->
+       if result == -1 then
+           getCErrorCode                       `thenPrimIO` \ errno ->
+           (case errno of
+               EAGAIN ->
+                   fail "readSocket: no data to read (non-blocking)"
+               EBADF ->
+                   fail "readSocket: invalid file descriptor"
+               EBADMSG ->
+                   fail "readSocket: not a valid data message"
+               EFAULT ->
+                   fail "readSocket: buffer outside allocated address space"
+               EINTR ->
+                   fail "readSocket: interupted by signal before data"
+               EINVAL ->
+                   fail ("readSocket: The stream is linked below a " ++
+                       "multiplexor. The file descriptor pointer was negative")
+               EIO ->
+                   fail "readSocket: IO error"
+               EISDIR ->
+                   fail "readSocket: descriptor is an NFS directory"
+               EWOULDBLOCK ->
+                   fail "readSocket: would block"
+               _ ->
+                   fail ("readSocket: "  ++ (errorCodeToStr errno))
+           )
+       else
+           return (_unpackPS (_packCString buffer), result)
+
+
+readSocketAll :: Socket -> IO String
+readSocketAll s =
+    let 
+      loop xs =
+        readSocket s 4096                      >>= \ (str, nbytes) ->
+       if nbytes /= 0 then
+           loop (str ++ xs)
+       else
+           return xs
+    in
+       loop ""
+
+\end{code}
+
+The port number the given socket is currently connected to can be
+determined by calling $port$, is generally only useful when bind
+was given $aNY_PORT$.
+
+\begin{code}
+socketPort :: Socket ->                        -- Connected & Bound Socket
+             IO Int                    -- Port Number of Socket
+socketPort sock@(MkSocket s AF_INET stype protocol status) =
+    getSocketName sock             >>= \ (SockAddrInet port _) ->
+    return port
+socketPort (MkSocket s family stype protocol status) =
+    fail ("socketPort: not supported for Family " ++ show family)
+\end{code}
+
+Calling $getPeerName$ returns the address details of the machine,
+other than the local one, which is connected to the socket. This is
+used in programs such as FTP to determine where to send the returning
+data.  The corresponding call to get the details of the local machine
+is $getSocketName$.
+
+\begin{code}
+getPeerName   :: Socket -> IO SockAddr
+getPeerName (MkSocket s family stype protocol status) =
+    allocSockAddr family                           `thenPrimIO` \ (ptr,sz) ->
+    _casm_ ``%r = getpeername(%0,(struct sockaddr*)%1,&%2);''
+            s ptr sz                               `thenPrimIO` \ result ->
+    if result == -1 then
+       getCErrorCode                               `thenPrimIO` \ errno ->
+       fail ("getPeerName: " ++ (errorCodeToStr errno))
+    else
+       unpackSockAddr ptr                          `thenPrimIO` \ addr ->
+       return addr
+
+getSocketName :: Socket -> IO SockAddr
+getSocketName (MkSocket s family stype protocol status) =
+    allocSockAddr family                           `thenPrimIO` \ (ptr,sz) ->
+    _casm_ ``%r = getsockname(%0,(struct sockaddr*)%1, &%2);'' 
+           s ptr sz                                `thenPrimIO` \ result ->
+    if result == -1 then
+       getCErrorCode                               `thenPrimIO` \ errno ->
+       fail ("getSocketName: " ++ (errorCodeToStr errno))
+    else
+       unpackSockAddr ptr                          `thenPrimIO` \ addr ->
+       return addr
+\end{code}
+
+
+%************************************************************************
+%*                                                                     *
+\subsection[Socket-Properties]{Socket Properties}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+{-
+data SocketOption =
+      Debug
+    | AcceptConnection
+    | ReuseAddr
+    | KeepAlive
+    | DontRoute
+    | Broadcast
+    | UseLoopBack
+    | Linger
+    | OOBInline
+    | SendBuffer
+    | RecvBuffer
+    | SendLowWater
+    | RecvLowWater
+    | SendTimeOut
+    | RecvTimeOut
+    | Error
+    | Type
+
+sOL_SOCKET = ``SOL_SOCKET''
+
+setSocketOptions :: Socket ->
+                   Int ->              -- Level
+                   SocketOption ->     -- Option Name
+                   String ->           -- Option Value
+                   IO ()
+
+getSocketOptons :: Socket ->
+                  Int ->               -- Level
+                  SocketOption ->      -- Option Name
+                  IO String            -- Option Value
+-}
+\end{code}
+
+A calling sequence table for the main functions is shown in the table below.
+
+\begin{figure}[h]
+\begin{center}
+\begin{tabular}{|l|c|c|c|c|c|c|c|}
+\hline
+\textbf{A Call to} & socket & connect & bindSocket & listen & accept & read & write \\
+\hline
+\textbf{Precedes} & & & & & & & \\
+\hline 
+socket &       &         &            &        &        &      & \\
+\hline
+connect & +    &         &            &        &        &      & \\
+\hline
+bindSocket & + &         &            &        &        &      & \\
+\hline
+listen &       &         & +          &        &        &      & \\
+\hline
+accept &       &         &            &  +     &        &      & \\
+\hline
+read   &       &   +     &            &  +     &  +     &  +   & + \\
+\hline
+write  &       &   +     &            &  +     &  +     &  +   & + \\
+\hline
+\end{tabular}
+\caption{Sequence Table for Major functions of Socket}
+\label{tab:api-seq}
+\end{center}
+\end{figure}
+
+%************************************************************************
+%*                                                                     *
+\subsection[Socket-OSDefs]{OS Dependant Definitions}
+%*                                                                     *
+%************************************************************************
+
+    
+The following Family and Socket Type declarations were manually derived
+from /usr/include/sys/socket.h on the appropriate machines.
+
+Maybe a configure script that could parse the socket.h file to produce
+the following declaration is required to make it "portable" rather than
+using the dreded \#ifdefs.
+
+Presently only the following machine/os combinations are supported:
+
+\begin{itemize}
+\item Intelx86/Linux
+\item SPARC/SunOS
+\item SPARC/Solaris
+\item Alpha/OSF
+\end{itemize}
+
+\begin{code}
+
+unpackFamily   :: Int -> Family
+packFamily     :: Family -> Int
+
+packSocketType :: SocketType -> Int
+#ifdef sun
+data Family = 
+         AF_UNSPEC     -- unspecified
+       | AF_UNIX       -- local to host (pipes, portals
+       | AF_INET       -- internetwork: UDP, TCP, etc
+       | AF_IMPLINK    -- arpanet imp addresses
+       | AF_PUP        -- pup protocols: e.g. BSP
+       | AF_CHAOS      -- mit CHAOS protocols
+       | AF_NS         -- XEROX NS protocols 
+       | AF_NBS        -- nbs protocols
+       | AF_ECMA       -- european computer manufacturers
+       | AF_DATAKIT    -- datakit protocols
+       | AF_CCITT      -- CCITT protocols, X.25 etc
+       | AF_SNA        -- IBM SNA
+       | AF_DECnet     -- DECnet
+       | AF_DLI        -- Direct data link interface
+       | AF_LAT        -- LAT
+       | AF_HYLINK     -- NSC Hyperchannel
+       | AF_APPLETALK  -- Apple Talk
+       | AF_NIT        -- Network Interface Tap
+       | AF_802        -- IEEE 80.2, also ISO 8802
+       | AF_OSI        -- umberella of all families used by OSI
+       | AF_X25        -- CCITT X.25
+       | AF_OSINET     -- AFI
+       | AF_GOSSIP     -- US Government OSI
+       | AF_IPX        -- Novell Internet Protocol
+       deriving (Eq, Ord, Ix, Text)
+                       
+packFamily = index (AF_UNSPEC, AF_IPX)
+unpackFamily family = (range (AF_UNSPEC, AF_IPX))!!family
+
+#endif
+
+#ifdef __alpha__
+       
+data Family =
+         AF_UNSPEC     -- unspecified 
+       | AF_UNIX       -- local to host (pipes, portals) 
+       | AF_INET       -- internetwork: UDP, TCP, etc. 
+       | AF_IMPLINK    -- arpanet imp addresses 
+       | AF_PUP        -- pup protocols: e.g. BSP 
+       | AF_CHAOS      -- mit CHAOS protocols 
+       | AF_NS         -- XEROX NS protocols 
+       | AF_ISO        -- ISO protocols 
+       | AF_ECMA       -- european computer manufacturers 
+       | AF_DATAKIT    -- datakit protocols 
+       | AF_CCITT      -- CCITT protocols, X.25 etc 
+       | AF_SNA        -- IBM SNA 
+       | AF_DECnet     -- DECnet 
+       | AF_DLI        -- DEC Direct data link interface 
+       | AF_LAT        -- LAT 
+       | AF_HYLINK     -- NSC Hyperchannel 
+       | AF_APPLETALK  -- Apple Talk 
+       | AF_ROUTE      -- Internal Routing Protocol 
+       | AF_LINK       -- Link layer interface 
+       | Pseudo_AF_XTP -- eXpress Transfer Protocol (no AF) 
+       | AF_NETMAN     -- DNA Network Management 
+       | AF_X25        -- X25 protocol 
+       | AF_CTF        -- Common Trace Facility 
+       | AF_WAN        -- Wide Area Network protocols 
+       deriving (Eq, Ord, Ix, Text)
+  
+packFamily = index (AF_UNSPEC, AF_WAN)
+unpackFamily family = (range (AF_UNSPEC, AF_WAN))!!family
+#endif 
+
+       
+#ifdef linux
+data Family = 
+         AF_UNSPEC
+       | AF_UNIX
+       | AF_INET
+       | AF_AX25
+       | AF_IPX
+       deriving (Eq, Ord, Ix, Text)    
+
+packFamily = index (AF_UNSPEC, AF_IPX)
+unpackFamily family = (range (AF_UNSPEC, AF_IPX))!!family
+
+#endif
+
+-- Alpha running OSF or a SPARC with SunOS, rather than Solaris.
+
+#if __alpha__ || (sun && !__svr4__)
+data SocketType = 
+         Stream 
+       | Datagram
+       | Raw 
+       | RDM 
+       | SeqPacket
+       deriving (Eq, Ord, Ix, Text)
+       
+packSocketType stype = 1 + (index (Stream, SeqPacket) stype)   
+#endif
+
+-- This is a Sun running Solaris rather than SunOS    
+
+#if sun && __svr4__
+data SocketType =
+         Datagram
+       | Stream
+       | NC_TPI_COTS_ORD
+       | Raw
+       | RDM
+       | SeqPacket
+       deriving (Eq, Ord, Ix, Text)    
+
+packSocketType stype = 1 + (index (Datagram, SeqPacket) stype)
+#endif 
+    
+
+#if linux
+data SocketType = 
+         Stream 
+       | Datagram
+       | Raw 
+       | RDM 
+       | SeqPacket
+       | Packet
+       deriving (Eq, Ord, Ix, Text)
+
+packSocketType stype = 1 + (index (Stream, Packet) stype)      
+#endif
+
+\end{code}
+
+%************************************************************************
+%*                                                                     *
+\subsection[Socket-Util]{Utility Functions}
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+aNY_PORT = 0::Int
+iNADDR_ANY = ``INADDR_ANY''::_Word
+sOMAXCONN = ``SOMAXCONN''::Int
+maxListenQueue = sOMAXCONN
+
+-------------------------------------------------------------------------------
+shutdown :: Socket -> Int -> IO ()
+shutdown (MkSocket s family stype protocol status) t = 
+    primIOToIO (_ccall_ shutdown s t)
+
+-------------------------------------------------------------------------------
+
+sClose  :: Socket -> IO ()
+sClose (MkSocket s family stype protocol status) = 
+    primIOToIO (_ccall_ close s)
+
+-------------------------------------------------------------------------------
+
+inet_addr :: String -> HostAddress
+inet_addr ipstr = unsafePerformPrimIO (_ccall_ inet_addr ipstr)
+
+-------------------------------------------------------------------------------
+
+inet_ntoa :: HostAddress -> String
+inet_ntoa haddr = unsafePerformPrimIO (
+    _casm_ ``struct in_addr addr;
+            addr.s_addr = htonl(%0);
+            %r = inet_ntoa (addr);'' haddr    `thenPrimIO` \ str ->
+    returnPrimIO (_unpackPS (_packCString str)))
+
+-------------------------------------------------------------------------------
+
+sIsConnected :: Socket -> IO Bool
+sIsConnected (MkSocket s family stype protocol status) =
+    readVar status                     `thenST` \ value ->
+    return (value == Connected)        
+
+-------------------------------------------------------------------------------
+
+sIsBound :: Socket -> IO Bool
+sIsBound (MkSocket s family stype protocol status) =
+    readVar status                     `thenST` \ value ->
+    return (value == Bound)    
+
+-------------------------------------------------------------------------------
+
+sIsListening :: Socket -> IO Bool
+sIsListening (MkSocket s family stype protocol status) =
+    readVar status                     `thenST` \ value ->
+    return (value == Listening)        
+
+-------------------------------------------------------------------------------
+
+sIsReadable  :: Socket -> IO Bool
+sIsReadable (MkSocket s family stype protocol status) =
+    readVar status                     `thenST` \ value ->
+    return (value == Listening || value == Connected)
+
+-------------------------------------------------------------------------------
+
+sIsWritable  :: Socket -> IO Bool
+sIsWritable = sIsReadable
+
+-------------------------------------------------------------------------------
+
+sIsAcceptable :: Socket -> IO Bool
+sIsAcceptable (MkSocket s AF_UNIX Stream protocol status) =
+    readVar status                     `thenST` \ value ->    
+    return (value == Connected || value == Bound || value == Listening)
+sIsAcceptable (MkSocket s AF_UNIX _ protocol status) = 
+    return False
+sIsAcceptable (MkSocket s _ stype protocol status) =
+    readVar status                     `thenST` \ value ->
+    return (value == Connected || value == Listening)
+    
+-------------------------------------------------------------------------------
+
+{-
+sSetBlocking :: Socket -> Bool -> IO ()
+sIsBlocking  :: Socket -> IO Bool
+-}
+
+-------------------------------------------------------------------------------
+
+allocSockAddr :: Family -> PrimIO (_MutableByteArray _RealWorld Int, Int)
+allocSockAddr AF_UNIX = 
+    newCharArray (0,``sizeof(struct sockaddr_un)'')    `thenPrimIO` \ ptr ->
+    let 
+       (_,sz) = boundsOfByteArray ptr
+    in 
+    returnPrimIO (ptr, sz)
+allocSockAddr AF_INET = 
+    newCharArray (0,``sizeof(struct sockaddr_in)'')    `thenPrimIO` \ ptr ->
+    let 
+       (_,sz) = boundsOfByteArray ptr
+    in
+    returnPrimIO (ptr, sz)
+
+-------------------------------------------------------------------------------
+
+unpackSockAddr :: _MutableByteArray _RealWorld Int -> PrimIO SockAddr
+unpackSockAddr arr =
+    _casm_ ``%r = ((struct sockaddr*)%0)->sa_family;'' arr `thenPrimIO` \ fam ->
+    case unpackFamily fam of
+       AF_UNIX -> unpackSockAddrUnix arr
+       AF_INET -> unpackSockAddrInet arr
+
+-------------------------------------------------------------------------------
+
+unpackSockAddrUnix :: (_MutableByteArray _RealWorld Int) -> PrimIO SockAddr
+unpackSockAddrUnix ptr =
+    _casm_ ``%r = ((struct sockaddr_un*)%0)->sun_path;'' ptr
+                                               `thenPrimIO` \ str ->
+    strcpy str                                 `thenPrimIO` \ path ->
+    returnPrimIO (SockAddrUnix path)
+
+-------------------------------------------------------------------------------
+
+unpackSockAddrInet :: (_MutableByteArray _RealWorld Int) -> PrimIO SockAddr
+unpackSockAddrInet ptr =
+    _casm_ ``%r = ntohs(((struct sockaddr_in*)%0)->sin_port);'' ptr
+                                               `thenPrimIO` \ port ->
+    _casm_ ``%r = ntohl(((struct sockaddr_in*)%0)->sin_addr.s_addr);'' ptr
+                                               `thenPrimIO` \ address ->
+    returnPrimIO (SockAddrInet port address)
+
+-------------------------------------------------------------------------------
+
+
+packSockAddr :: SockAddr -> PrimIO (_MutableByteArray _RealWorld Int)
+packSockAddr (SockAddrUnix path) =
+    allocSockAddr AF_UNIX                              `thenPrimIO` \ (ptr,_) ->
+    _casm_ ``(((struct sockaddr_un *)%0)->sun_family) = AF_UNIX;''
+               ptr                                     `thenPrimIO` \ () ->
+    _casm_ ``strcpy ((((struct sockaddr_un *)%0)->sun_path),%1);''
+               ptr path                                `thenPrimIO` \ () ->    
+    returnPrimIO ptr
+
+packSockAddr (SockAddrInet port address) =
+    allocSockAddr AF_INET                              `thenPrimIO` \ (ptr,_) ->
+    _casm_ ``(((struct sockaddr_in *)%0)->sin_family) = AF_INET;''
+               ptr                                     `thenPrimIO` \ () ->
+    _casm_ ``(((struct sockaddr_in *)%0)->sin_port) = htons((int)%1);''
+               ptr port                                `thenPrimIO` \ () ->
+    _casm_ ``(((struct sockaddr_in *)%0)->sin_addr.s_addr) = htonl(%1);''
+               ptr address                             `thenPrimIO` \ () ->
+    returnPrimIO ptr
+
+-------------------------------------------------------------------------------
+
+socketToHandle :: Socket -> IO Handle
+socketToHandle (MkSocket s family stype protocol status) =
+    _casm_ ``%r = fdopen (%0, "r+");'' s     `thenPrimIO` \ ptr ->
+    newEmptyMVar                            >>= \ handle ->
+    putMVar handle (_SocketHandle ptr False) >>
+    return handle
+
+-------------------------------------------------------------------------------
+\end{code}