Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP


Binding to user-defined socked addresses #78

wants to merge 4 commits into from

3 participants


As requested in #11, I've reapplied 414edbb to current master.

I haven't throughly tested this patch, but cabal test passes fine (so no existing functionality seems to be broken) and example from #11 OP works fine, showing udev events as expected.


I'd like for us to address:

This pull request extends the sockaddr type with a SockAddrRaw with permits users to use other socket family without having to define all types of sockaddr (in my case netlink which is linux specific). It has some caveats, since the sockaddr doesn't have any length, there's no way to peek back the structure to the haskell type. It might be a bit too OS specific or type unsafe for the network packet, let me know if i can improve the situation somehow.

Is there now way to enable reading back the SockAddr as a SockAddrRaw? At the very least we should make sure users get sensible error messages if they try.


The main problem is, struct sockaddr (and its family-specific counterparts) do not provide information about actual address size (hardcoded sa_data[14]), so this is quite ugly.

The following now works:

let netlinksockaddr = [ 0, 0 -- nl_pad
                      , 0, 0, 0, 0 -- nl_pid
                      , 1, 0, 0, 0 -- nl_group
    sa = (SockAddrRaw AF_ROUTE netlinksockaddr)

withSockAddr sa $ \ptr _ -> do
    pokeSockAddr ptr sa
    p <- peekSockAddr ptr
    -- Prints "{Raw(AF_ROUTE) [0,0,0,0,0,0,1,0,0,0,113,127,0,0]}"
    print p

unfortunately, there's no way to get the peek object back. The length is dependent on the family, so short of implementing every family protocols (which is not possible anyway, as it's a not a finite list and barely portable), you'll always miss the length.

The best you can do if you want to peek at unknown sockaddr, is take @drdaeman approach, and recover some bits, either the common part (the family), or the common part + sa_data (14 bytes that might be uninitialized but will be there). There's also way for a sockaddr to be bigger than 16 bytes (for example sockaddr_in6), but it will be completely unsafe to peek at the extra bits.


btw, maybe it would be safer (less hard to shoot yourself in the foot) to:

  • don't allocate less than 14 bytes (sa_data size), even if the byte list is smaller. Otherwise it could have dire consequences with @drdaeman peek's patch (crash, reading something else's memory, etc).
  • check for known families in the SockAddrRaw and prevent marshalling. Otherwise you could poke a "SockAddrRaw AF_INET6 [1]" and during the peek, the AF_INET6 family would make it look for sockaddr_in6 bytes (family+26 bytes) when there's only 1 byte allocated (or 14 with previous recommendation), and would have same consequences as above.

@vincenthz How about those two commits? (45296c4, e4186c6)


That looks like a good idea and would fix both problems. One thing though (bikeshed color) is that i would rather deny marshalling known families through SockAddrRaw instead of adding the extra check.


My thought was that if we can't prevent creation of such values (we can't type family as something like Family - KnownFamily, right?), and they're technically safe to use, then there's no reason to prevent from using them. Or there is?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 45 additions and 3 deletions.
  1. +6 −0 Network/Socket.hsc
  2. +39 −3 Network/Socket/Types.hsc
6 Network/Socket.hsc
@@ -315,6 +315,12 @@ instance Show SockAddr where
. showString "]:"
. shows port
+ showsPrec _ (SockAddrRaw family raw_data)
+ = showString "{Raw("
+ . shows family
+ . showString ") "
+ . shows raw_data
+ . showString "}"
-- Connection Functions
42 Network/Socket/Types.hsc
@@ -39,6 +39,7 @@ module Network.Socket.Types
, pokeSockAddr
, sizeOfSockAddr
, sizeOfSockAddrByFamily
+ , sizeOfSockAddrByFamily'
, withSockAddr
, withNewSockAddr
@@ -52,6 +53,9 @@ module Network.Socket.Types
#include "HsNet.h"
+#define member_size(type, member) sizeof(((type *)0)->member)
+#define member_count(type, member) member_size(type, member) / member_size(type, member[0])
import Control.Concurrent.MVar
import Control.Monad
import Data.Bits
@@ -777,6 +781,9 @@ data SockAddr -- C Names
| SockAddrUnix
String -- sun_path
+ | SockAddrRaw
+ Family -- socket family
+ [Word8] -- raw bytes
deriving (Eq, Ord, Typeable)
#if defined(WITH_WINSOCK) || defined(cygwin32_HOST_OS)
@@ -801,17 +808,27 @@ sizeOfSockAddr (SockAddrInet _ _) = #const sizeof(struct sockaddr_in)
#if defined(IPV6_SOCKET_SUPPORT)
sizeOfSockAddr (SockAddrInet6 _ _ _ _) = #const sizeof(struct sockaddr_in6)
+sizeOfSockAddr (SockAddrRaw _ bytes) =
+ max (#const sizeof(struct sockaddr)) $ (#const sizeof(sa_family_t)) + length bytes
-- | Computes the storage requirements (in bytes) required for a
-- 'SockAddr' with the given 'Family'.
sizeOfSockAddrByFamily :: Family -> Int
+sizeOfSockAddrByFamily f = case sizeOfSockAddrByFamily' f of
+ Just size -> size
+ Nothing -> error $
+ "Network.Socket.Internal.sizeOfSockAddrByFamily: unsupported address family: " ++
+ show f
+sizeOfSockAddrByFamily' :: Family -> Maybe Int
-sizeOfSockAddrByFamily AF_UNIX = #const sizeof(struct sockaddr_un)
+sizeOfSockAddrByFamily' AF_UNIX = Just (#const sizeof(struct sockaddr_un))
#if defined(IPV6_SOCKET_SUPPORT)
-sizeOfSockAddrByFamily AF_INET6 = #const sizeof(struct sockaddr_in6)
+sizeOfSockAddrByFamily' AF_INET6 = Just (#const sizeof(struct sockaddr_in6))
-sizeOfSockAddrByFamily AF_INET = #const sizeof(struct sockaddr_in)
+sizeOfSockAddrByFamily' AF_INET = Just (#const sizeof(struct sockaddr_in))
+sizeOfSockAddrByFamily' _ = Nothing
-- | Use a 'SockAddr' with a function requiring a pointer to a
-- 'SockAddr' and the length of that 'SockAddr'.
@@ -873,6 +890,20 @@ pokeSockAddr p (SockAddrInet6 (PortNum port) flow addr scope) = do
(#poke struct sockaddr_in6, sin6_addr) p addr
(#poke struct sockaddr_in6, sin6_scope_id) p scope
+pokeSockAddr p sa@(SockAddrRaw family bytes) = do
+ let saSize = sizeOfSockAddr sa
+ minSize = fromMaybe 0 (sizeOfSockAddrByFamily' family)
+ if saSize < minSize
+ then
+ ioError (userError ("won't marshall badly sized SockAddrRaw of " ++
+ (show family) ++ ": " ++ (show minSize) ++ " bytes required but only "
+ ++ (show saSize) ++ " are available"))
+ else do
+#if defined(darwin_TARGET_OS)
+ zeroMemory p (sizeOfSockAddr sa)
+ (#poke struct sockaddr, sa_family) p (fromIntegral (packFamily family) :: CSaFamily)
+ pokeArray ((#ptr struct sockaddr, sa_data) p) bytes
-- | Read a 'SockAddr' from the given memory location.
peekSockAddr :: Ptr SockAddr -> IO SockAddr
@@ -896,6 +927,11 @@ peekSockAddr p = do
scope <- (#peek struct sockaddr_in6, sin6_scope_id) p
return (SockAddrInet6 (PortNum port) flow addr scope)
+ _ -> do
+ let fam = unpackFamily $ fromIntegral $ toInteger family
+ data_ptr = (#ptr struct sockaddr, sa_data) p
+ raw_data <- peekArray (#const member_count(struct sockaddr, sa_data)) data_ptr
+ return (SockAddrRaw fam raw_data)
Something went wrong with that request. Please try again.