-
Notifications
You must be signed in to change notification settings - Fork 201
Description
In Network 2.8, it is difficult to create a new socket of the same shape as an existing socket due to missing interfaces. In C, I can do this via:
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
typedef int SOCKET; /* Unix */
#define INVALID_SOCKET ((SOCKET)-1);
SOCKET cloneSocket(SOCKET s)
{
struct sockaddr_storage saddr;
struct sockaddr *sa = (struct sockaddr *)&saddr;
socklen_t len;
SOCKET s2 = INVALID_SOCKET;
int stype;
int sproto;
len = sizeof(stype);
if (getsockopt(s, SOL_SOCKET, SO_TYPE, &stype, &len) == -1 ||
getsockopt(s, SOL_SOCKET, SO_PROTOCOL, &sproto, &len) == -1)
return s2;
len = sizeof(saddr);
memset(&saddr, 0, len);
if (getsockname(s, sa, &len) != -1)
s2 = socket(sa->sa_family, stype, sproto);
return s2;
}But Network.Socket does not export a way to recover the address family of a socket other than case matching the constructor, and no way to map the integer returned from (getSocketOption sock Type) back to a SocketType that can be used to create a new socket, and SO_PROTOCOL is missing entirely, so one can simply hope that defaultProtocol is good enough.
As systems evolve and new values for some of these fields are added, the "sum type" representation of these fields creates constant compatibility issues. These could in some cases be much better handled via PatternSynonyms than fixed sum types. Something like the below suitably refined (e.g. Perhaps SocketFamily can be 8 bits on BSD and 16 bits on Linux, with some Haskell type defined accordingly to match the platform's sa_family_t)
{-# LANGUAGE CPP #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
#include <sys/param.h>
#include <sys/socket.h>
module Network.Socket.Types
( AddressFamily
( AF_INET
, AF_INET6
, AF_UNIX
)
) where
import Data.Word
newtype AddressFamily = AddressFamily { packAddressFamily :: Word16 }
deriving (Eq)
#ifdef AF_INET
pattern AF_INET :: AddressFamily
pattern AF_INET = AddressFamily (#const AF_INET)
#else
pattern AF_INET = AddressFamily 0
#endif
#ifdef AF_INET6
pattern AF_INET6 :: AddressFamily
pattern AF_INET6 = AddressFamily (#const AF_INET6)
#else
pattern AF_INET6 = AddressFamily 0
#endif
#ifdef AF_UNIX
pattern AF_UNIX :: AddressFamily
pattern AF_UNIX = AddressFamily (#const AF_UNIX)
#else
pattern AF_UNIX = AddressFamily 0
#endif
instance Show AddressFamily where
show AF_INET = "inet"
show AF_INET6 = "inet6"
show AF_UNIX = "unix"
show (AddressFamily n) = "<family" ++ show n ++ ">"and so on for various similar cases.