Skip to content

Network 2.8 missing conversions for Socket family and type. #427

@vdukhovni

Description

@vdukhovni

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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions