diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2011, Mikhail Vorozhtsov
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are met:
+
+- Redistributions of source code must retain the above copyright notice, 
+  this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright 
+  notice, this list of conditions and the following disclaimer in the 
+  documentation and/or other materials provided with the distribution.
+- Neither the names of the copyright owners nor the names of the 
+  contributors may be used to endorse or promote products derived 
+  from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/include/posix-socket.macros.h b/include/posix-socket.macros.h
new file mode 100644
--- /dev/null
+++ b/include/posix-socket.macros.h
@@ -0,0 +1,14 @@
+#ifndef POSIX_SOCKET_MARCROS_H
+#define POSIX_SOCKET_MARCROS_H
+
+#ifndef hsc_alignment
+# define hsc_alignment(t) \
+   hsc_printf("%lu", (unsigned long) offsetof (struct { char x__; t (y__); }, y__));
+#endif
+#define hsc_offsetof(s,f) \
+  hsc_printf("%lu", (unsigned long) offsetof (s, f));
+#define hsc_itype(t) \
+  hsc_printf("%s%lu", ((t)(-1)) < 0 ? "Int" : "Word", sizeof (t) * 8);
+
+#endif /* POSIX_SOCKET_MARCROS_H */
+
diff --git a/posix-socket.cabal b/posix-socket.cabal
new file mode 100644
--- /dev/null
+++ b/posix-socket.cabal
@@ -0,0 +1,45 @@
+Name: posix-socket
+Version: 0.1
+Category: System
+Stability: experimental
+Synopsis: Bindings to the POSIX socket API
+Description:
+  This package provides bindings to the POSIX socket API.
+
+Homepage: https://github.com/mvv/posix-socket
+Bug-Reports: https://github.com/mvv/posix-socket/issues
+
+Author: Mikhail Vorozhtsov <mikhail.vorozhtsov@gmail.com>
+Maintainer: Mikhail Vorozhtsov <mikhail.vorozhtsov@gmail.com>
+Copyright: 2011, 2017 Mikhail Vorozhtsov <mikhail.vorozhtsov@gmail.com>
+License: BSD3
+License-File: LICENSE
+
+Extra-Source-Files:
+  include/posix-socket.macros.h
+
+Tested-With: GHC==7.10.3, GHC==8.0.2, GHC==8.2.2
+
+Cabal-Version: >= 1.6.0
+Build-Type: Simple
+
+Source-Repository head
+  Type: git
+  Location: https://github.com/mvv/posix-socket.git
+
+Library
+  Build-Depends: base               >= 4.7 && < 5
+               , unix               >= 2.4
+               , bytestring         >= 0.9.1.10
+               , data-default-class >= 0.0.1
+               , data-flags         >= 0.0.3
+               , network-ip         >= 0.2.1
+               , transformers-base  >= 0.3
+  Hs-Source-Dirs: src
+  Include-Dirs: include
+  GHC-Options: -Wall
+  Exposed-Modules:
+    System.Posix.Socket
+    System.Posix.Socket.Inet
+    System.Posix.Socket.Local
+
diff --git a/src/System/Posix/Socket.hsc b/src/System/Posix/Socket.hsc
new file mode 100644
--- /dev/null
+++ b/src/System/Posix/Socket.hsc
@@ -0,0 +1,814 @@
+{-# LANGUAGE UnicodeSyntax #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE DoAndIfThenElse #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE DataKinds #-}
+
+-- | POSIX sockets.
+module System.Posix.Socket
+  ( Socket
+  , withSocketFd
+  , unsafeSocketFd
+  , unsafeSocketFromFd
+  , SockFamily(..)
+  , SockAddr(..)
+  , SockType(..)
+  , streamSockType
+  , datagramSockType
+  , rawSockType
+  , seqPacketSockType
+  , SockProto(..)
+  , defaultSockProto
+  , SockOpt(..)
+  , SO_ERROR(..)
+  , SO_KEEPALIVE(..)
+  , SO_REUSEADDR(..)
+  , SockOps
+  , sendSockOp
+  , recvSockOp
+  , MsgFlags
+  , peekMsgFlag
+  , truncMsgFlag
+  , oobMsgFlag
+  , dontRouteMsgFlag
+
+  , socket
+  , getSockOpt
+  , setSockOpt
+  , bind
+  , connect
+  , tryConnect
+  , listen
+  , accept
+  , getLocalAddr
+  , getRemoteAddr
+  , hasOobData
+  , recvBufs
+  , recvBuf
+  , recv'
+  , recv
+  , recvBufsFrom
+  , recvBufFrom
+  , recvFrom'
+  , recvFrom
+  , sendBufs
+  , sendMany'
+  , sendMany
+  , sendBuf
+  , send'
+  , send
+  , sendBufsTo
+  , sendManyTo'
+  , sendManyTo
+  , sendBufTo
+  , sendTo'
+  , sendTo
+  , shutdown
+  , close
+  ) where
+
+import Data.Typeable (Typeable)
+import Data.Proxy (Proxy(..))
+import Data.Word
+import Data.Bits ((.|.))
+import Data.Default.Class
+import Data.List (partition)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Internal as BS
+import qualified Data.ByteString.Unsafe as BS
+import Data.Flags (Flags(..), (.>=.))
+import Data.Flags.TH (bitmaskWrapper)
+import Data.Foldable (forM_)
+import Control.Applicative ((<$>))
+import Control.Monad (void, when, foldM)
+import Control.Monad.Base
+import Control.Exception (throwIO)
+import Control.Concurrent (threadWaitRead, threadWaitWrite)
+import Control.Concurrent.MVar
+import Foreign.Storable (Storable(..))
+import Foreign.Ptr (Ptr, castPtr, nullPtr, plusPtr)
+import Foreign.Marshal.Alloc (alloca, allocaBytesAligned)
+import Foreign.Marshal.Utils (with)
+#if __GLASGOW_HASKELL__ >= 703
+import Foreign.C.Types (CInt(..), CSize(..))
+import System.Posix.Types (CSsize(..))
+#else
+import Foreign.C.Types (CInt, CSize)
+import System.Posix.Types (CSsize)
+#endif
+import Foreign.C.Error (Errno(..), eOK, eINVAL, eMSGSIZE, eINPROGRESS, eAGAIN,
+                        eWOULDBLOCK, getErrno, errnoToIOError,
+                        throwErrno, throwErrnoIfMinus1, throwErrnoIfMinus1_)
+import System.IO.Error (eofErrorType, mkIOError)
+import System.Posix.Types (Fd(..))
+import System.Posix.IO (closeFd)
+import GHC.Conc (closeFdWith)
+
+#ifdef __linux__
+# include <linux/version.h>
+# if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 27)
+#   define HAVE_SOCKET_WITH_FLAGS
+# endif
+# if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 28)
+#   define HAVE_ACCEPT_WITH_FLAGS
+# endif
+#endif
+
+#if !defined (HAVE_SOCKET_WITH_FLAGS) || !defined(HAVE_ACCEPT_WITH_FLAGS)
+import System.Posix.Internals (setNonBlockingFD)
+#endif
+
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <posix-socket.macros.h>
+
+-- | Socket of a particular family.
+newtype Socket f = Socket (MVar Fd) deriving (Typeable, Eq)
+
+-- | Lock the socket and pass the underlying file descriptor to the given
+--   action.
+withSocketFd ∷ MonadBase IO μ ⇒ Socket f → (Fd → IO α) → μ α
+withSocketFd (Socket v) f = liftBase $ withMVar v f
+{-# INLINE withSocketFd #-}
+
+-- | Get the underlying file descriptor.
+unsafeSocketFd ∷ MonadBase IO μ ⇒ Socket f → μ Fd
+unsafeSocketFd (Socket v) = liftBase $ readMVar v
+
+-- | Use file descriptor as a socket.
+unsafeSocketFromFd ∷ MonadBase IO μ ⇒ Fd → μ (Socket f)
+unsafeSocketFromFd = liftBase . fmap Socket . newMVar
+
+-- | Socket address.
+class SockAddr a where
+  -- | Maximum size of a socket address.
+  sockAddrMaxSize ∷ Proxy a → Int
+  -- | Size of a particular socket address.
+  sockAddrSize    ∷ a → Int
+  -- | Read socket address from a memory buffer.
+  peekSockAddr    ∷ Ptr a -- ^ Buffer
+                  → Int   -- ^ Buffer size
+                  → IO a
+  -- | Write socket address to a memory buffer.
+  pokeSockAddr    ∷ Ptr a -- ^ Buffer of sufficient size
+                  → a     -- ^ The address to poke
+                  → IO ()
+
+-- | Socket family.
+class SockAddr (SockFamilyAddr f) ⇒ SockFamily f where
+  type SockFamilyAddr f
+  -- | Socket family code.
+  sockFamilyCode ∷ f → CInt
+
+-- | Socket type.
+newtype SockType = SockType CInt deriving (Typeable, Eq, Ord, Show, Storable)
+
+-- | See /SOCK_STREAM/.
+streamSockType ∷ SockType
+streamSockType = SockType #const SOCK_STREAM
+
+-- | See /SOCK_DGRAM/.
+datagramSockType ∷ SockType
+datagramSockType = SockType #const SOCK_DGRAM
+
+-- | See /SOCK_RAW/.
+rawSockType ∷ SockType
+rawSockType = SockType #const SOCK_RAW
+
+-- | See /SOCK_SEQPACKET/.
+seqPacketSockType ∷ SockType
+seqPacketSockType = SockType #const SOCK_SEQPACKET
+
+-- | Socket protocol.
+newtype SockProto = SockProto CInt deriving (Typeable, Eq, Ord, Show, Storable)
+
+-- | Default socket protocol (corresponds to @0@).
+defaultSockProto ∷ SockProto
+defaultSockProto = SockProto 0
+
+instance Default SockProto where
+  def = defaultSockProto
+
+-- | Socket option.
+class Storable (SockOptRaw o) ⇒ SockOpt o where
+  -- | Option value type
+  type SockOptValue o
+  -- | FFI-level option value type
+  type SockOptRaw o
+  -- | Whether option is readable
+  type SockOptReadable o ∷ Bool
+  -- | Whether option is writable
+  type SockOptWritable o ∷ Bool
+  -- | Convert to FFI-level value
+  sockOptRaw   ∷ o → SockOptValue o → SockOptRaw o
+  -- | Convert from FFI-level value
+  sockOptValue ∷ o → SockOptRaw o → SockOptValue o
+  -- | Option protocol level
+  sockOptLevel ∷ o → CInt
+  -- | Option code
+  sockOptCode  ∷ o → CInt
+
+data SO_ERROR = SO_ERROR deriving (Typeable, Eq, Show)
+
+instance SockOpt SO_ERROR where
+  type SockOptValue    SO_ERROR = Errno
+  type SockOptRaw      SO_ERROR = CInt
+  type SockOptReadable SO_ERROR = 'True
+  type SockOptWritable SO_ERROR = 'False
+  sockOptRaw   _ (Errno e) = e
+  sockOptValue _ = Errno
+  sockOptLevel _ = #const SOL_SOCKET
+  sockOptCode  _ = #const SO_ERROR
+
+data SO_KEEPALIVE = SO_KEEPALIVE deriving (Typeable, Eq, Show)
+
+instance SockOpt SO_KEEPALIVE where
+  type SockOptValue    SO_KEEPALIVE = Bool
+  type SockOptRaw      SO_KEEPALIVE = CInt
+  type SockOptReadable SO_KEEPALIVE = 'True
+  type SockOptWritable SO_KEEPALIVE = 'True
+  sockOptRaw _ False = 0
+  sockOptRaw _ True  = 1
+  sockOptValue _ = (/= 0)
+  sockOptLevel _ = #const SOL_SOCKET
+  sockOptCode  _ = #const SO_KEEPALIVE
+
+data SO_REUSEADDR = SO_REUSEADDR deriving (Typeable, Eq, Show)
+
+instance SockOpt SO_REUSEADDR where
+  type SockOptValue    SO_REUSEADDR = Bool
+  type SockOptRaw      SO_REUSEADDR = CInt
+  type SockOptReadable SO_REUSEADDR = 'True
+  type SockOptWritable SO_REUSEADDR = 'True
+  sockOptRaw _ False = 0
+  sockOptRaw _ True  = 1
+  sockOptValue _ = (/= 0)
+  sockOptLevel _ = #const SOL_SOCKET
+  sockOptCode  _ = #const SO_REUSEADDR
+
+-- | Socket operations. Used by 'shutdown'.
+$(bitmaskWrapper "SockOps" ''Int []
+    [("sendSockOp", 1),
+     ("recvSockOp", 2)])
+
+-- | Message flags.
+newtype MsgFlags = MsgFlags CInt deriving (Typeable, Eq, Show, Storable, Flags)
+
+-- | See /MSG_PEEK/.
+peekMsgFlag ∷ MsgFlags
+peekMsgFlag = MsgFlags #const MSG_PEEK
+
+-- | See /MSG_TRUNC/.
+truncMsgFlag ∷ MsgFlags
+truncMsgFlag = MsgFlags #const MSG_TRUNC
+
+-- | See /MSG_OOB/.
+oobMsgFlag ∷ MsgFlags
+oobMsgFlag = MsgFlags #const MSG_OOB
+
+-- | See /MSG_DONTROUTE/.
+dontRouteMsgFlag ∷ MsgFlags
+dontRouteMsgFlag = MsgFlags #const MSG_DONTROUTE
+
+allocaMaxAddr ∷ SockAddr a ⇒ Proxy a → (Ptr a → #{itype socklen_t} → IO α) → IO α
+allocaMaxAddr addrProxy f =
+    allocaBytesAligned size #{alignment struct sockaddr} $
+      (`f` (fromIntegral size))
+  where size = sockAddrMaxSize addrProxy
+
+allocaAddr ∷ SockAddr a ⇒ a → (Ptr a → #{itype socklen_t} → IO α) → IO α
+allocaAddr addr f =
+    allocaBytesAligned size #{alignment struct sockaddr} $
+      (`f` (fromIntegral size))
+  where size = sockAddrSize addr
+
+peekAddrOfSize ∷ SockFamily f
+               ⇒ f → Ptr (SockFamilyAddr f) → Ptr #{itype socklen_t}
+               → IO (SockFamilyAddr f)
+peekAddrOfSize fam p pSize = do
+  outSize ← fromIntegral <$> peek pSize
+  famCode ∷ #{itype sa_family_t} ← #{peek struct sockaddr, sa_family} p
+  when (fromIntegral famCode /= sockFamilyCode fam) $
+    ioError $ userError "Invalid socket address family"
+  peekSockAddr p outSize
+
+withAddr ∷ SockFamily f
+         ⇒ f
+         → SockFamilyAddr f
+         → (Ptr (SockFamilyAddr f) → #{itype socklen_t} → IO α)
+         → IO α
+withAddr fam addr f =
+    allocaAddr addr $ \p size → do
+      pokeSockAddr p addr
+      #{poke struct sockaddr, sa_family} p famCode
+      f p size
+  where famCode ∷ #{itype sa_family_t}
+        famCode = fromIntegral $ sockFamilyCode fam
+
+-- | Create a socket. See /socket(3)/.
+-- The underlying file descriptor is non-blocking.
+socket ∷ (SockFamily f, MonadBase IO μ)
+       ⇒ f → SockType → SockProto → μ (Socket f)
+socket f (SockType t) p = liftBase $ do
+  fd ← throwErrnoIfMinus1 "socket" $
+         c_socket (sockFamilyCode f)
+#ifdef HAVE_SOCKET_WITH_FLAGS
+           (t .|. #{const SOCK_NONBLOCK}) p
+#else
+           t p
+  onException (setNonBlockingFD fd True) (closeFd fd)
+#endif
+  fmap Socket $ newMVar $ Fd fd
+
+getFdOpt ∷ ∀ o . (SockOpt o, SockOptReadable o ~ 'True)
+         ⇒ Fd → o → IO (SockOptValue o)
+getFdOpt fd o =
+  alloca $ \p →
+    with (fromIntegral $ sizeOf (undefined ∷ SockOptRaw o)) $ \pSize → do
+      throwErrnoIfMinus1_ "getSockOpt" $
+        c_getsockopt fd (sockOptLevel o) (sockOptCode o) p pSize
+      sockOptValue o <$> peek p
+
+-- | Get socket option value. See /getsockopt(3)/.
+getSockOpt ∷ (SockOpt o, SockOptReadable o ~ 'True, MonadBase IO μ)
+           ⇒ Socket f → o → μ (SockOptValue o)
+getSockOpt s o = withSocketFd s $ \fd → getFdOpt fd o
+
+-- | Set socket option value. See /setsockopt(3)/.
+setSockOpt ∷ (SockOpt o, SockOptWritable o ~ 'True, MonadBase IO μ)
+           ⇒ Socket f → o → SockOptValue o → μ ()
+setSockOpt s o v = withSocketFd s $ \fd →
+    with raw $ \p →
+      throwErrnoIfMinus1_ "setSockOpt" $
+        c_setsockopt fd (sockOptLevel o) (sockOptCode o) p $
+          fromIntegral (sizeOf raw)
+  where raw = sockOptRaw o v
+
+-- | Bind socket to the specified address. See /bind(3)/.
+bind ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
+     ⇒ Socket f → SockFamilyAddr f → μ ()
+bind s addr = withSocketFd s $ \fd →
+  withAddr (undefined ∷ f) addr $ \p size →
+    throwErrnoIfMinus1_ "bind" $ c_bind fd p $ fromIntegral size
+
+-- | Connect socket to the specified address. This function blocks.
+-- See /connect(3)/.
+connect ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
+        ⇒ Socket f → SockFamilyAddr f → μ ()
+connect s addr = withSocketFd s $ \fd →
+    withAddr (undefined ∷ f) addr $ \p size →
+      doConnect fd p $ fromIntegral size
+  where doConnect fd p size = do
+          r ← c_connect fd p size
+          if r == -1 then do
+            errno ← getErrno
+            case errno of
+              e | e == eINPROGRESS → do
+                threadWaitWrite fd
+                errno' ← getFdOpt fd SO_ERROR
+                when (errno' /= eOK) $
+                  throwIO $ errnoToIOError "connect" errno' Nothing Nothing
+              _ → throwErrno "connect"
+          else
+            return ()
+
+-- | Try to connect socket without blocking. On success 'True' is returned.
+-- If the connection did not succeed immediately, 'False' is returned.
+-- See /connect(3)/.
+tryConnect ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
+           ⇒ Socket f → SockFamilyAddr f → μ Bool
+tryConnect s addr = withSocketFd s $ \fd →
+  withAddr (undefined ∷ f) addr $ \p size → do
+    r ← c_connect fd p $ fromIntegral size
+    if r == -1
+      then do
+        errno ← getErrno
+        if errno == eINPROGRESS
+          then return False
+          else throwErrno "connect"
+      else
+        return True
+
+-- | Listen for connections on the given socket. See /listen(2)/.
+listen ∷ MonadBase IO μ ⇒ Socket f → Int → μ ()
+listen s backlog = withSocketFd s $ \fd →
+  throwErrnoIfMinus1_ "listen" $ c_listen fd $ fromIntegral backlog
+
+-- | Accept a connection on the given socket. This function blocks.
+-- See /accept(2)/.
+accept ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
+       ⇒ Socket f → μ (Socket f, SockFamilyAddr f)
+accept s = withSocketFd s $ \fd →
+    allocaMaxAddr (Proxy ∷ Proxy (SockFamilyAddr f)) $ \p size →
+      with size $ \pSize → doAccept fd p pSize
+  where doAccept fd p pSize = do
+          cfd ← c_accept fd p pSize
+#ifdef HAVE_ACCEPT_WITH_FLAGS
+                  #{const SOCK_NONBLOCK}
+#endif
+          if cfd == -1 then do
+            errno ← getErrno
+            case errno of
+              e | e == eAGAIN || e == eWOULDBLOCK → do
+                threadWaitRead fd
+                doAccept fd p pSize
+              _ → throwErrno "accept"
+          else do
+            addr ← peekAddrOfSize (undefined ∷ f) p pSize
+#ifndef HAVE_ACCEPT_WITH_FLAGS
+            setNonBlockingFD cfd True
+#endif
+            (, addr) <$> unsafeSocketFromFd (Fd cfd)
+
+-- | Get the local address. See /getsockname(3)/.
+getLocalAddr ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
+             ⇒ Socket f → μ (SockFamilyAddr f)
+getLocalAddr s = withSocketFd s $ \fd →
+  allocaMaxAddr (Proxy ∷ Proxy (SockFamilyAddr f)) $ \p size →
+    with size $ \pSize → do
+      throwErrnoIfMinus1_ "getLocalAddr" $ c_getsockname fd p pSize
+      peekAddrOfSize (undefined ∷ f) p pSize
+
+-- | Get the remote address. See /getpeername(3)/.
+getRemoteAddr ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
+              ⇒ Socket f → μ (SockFamilyAddr f)
+getRemoteAddr s = withSocketFd s $ \fd →
+  allocaMaxAddr (Proxy ∷ Proxy (SockFamilyAddr f)) $ \p size →
+    with size $ \pSize → do
+      throwErrnoIfMinus1_ "getRemoteAddr" $ c_getpeername fd p pSize
+      peekAddrOfSize (undefined ∷ f) p pSize
+
+-- | Check if socket has out-of-band data. See /sockatmark(3)/.
+hasOobData ∷ MonadBase IO μ ⇒ Socket f → μ Bool
+hasOobData s = withSocketFd s $ \fd →
+  fmap (== 1) $ throwErrnoIfMinus1 "hasOOBData" $ c_sockatmark fd
+
+throwCustomErrno ∷ String → Errno → IO α
+throwCustomErrno loc errno =
+  throwIO $ errnoToIOError loc errno Nothing Nothing
+
+throwInval ∷ String → IO α
+throwInval loc = throwCustomErrno loc eINVAL
+
+recvBufsFromFd ∷ ∀ f . SockFamily f
+               ⇒ f → Fd → [(Ptr Word8, Int)] → MsgFlags
+               → IO (Maybe (SockFamilyAddr f), Int, MsgFlags)
+recvBufsFromFd _ fd bufs' flags = do
+  let (bufs, bufs'') = partition ((> 0) . snd) bufs'
+      nn             = length bufs
+  when (any ((< 0) . snd) bufs'' || nn == 0) $ throwInval "recv"
+  when (nn > #{const UIO_MAXIOV}) $ throwCustomErrno "recv" eMSGSIZE
+  allocaBytesAligned #{size struct msghdr}
+                     #{alignment struct msghdr} $ \pHdr →
+    allocaMaxAddr (Proxy ∷ Proxy (SockFamilyAddr f)) $ \pAddr addrLen → do
+      #{poke struct msghdr, msg_name}    pHdr pAddr
+      #{poke struct msghdr, msg_namelen} pHdr addrLen
+      let doRecv = do
+            r ← c_recvmsg fd pHdr flags
+            case r of
+              (-1) → do
+                errno ← getErrno
+                case errno of
+                  e | e == eAGAIN || e == eWOULDBLOCK → do
+                    threadWaitRead fd
+                    doRecv
+                  _ → throwErrno "recv"
+              0 → throwIO $ mkIOError eofErrorType "recv" Nothing Nothing
+              _ → do
+                addrLen' ← #{peek struct msghdr, msg_namelen} pHdr ∷
+                              IO #{itype socklen_t}
+                addr     ← if addrLen' == 0
+                             then return Nothing
+                             else fmap Just $ peekSockAddr pAddr $
+                                    fromIntegral addrLen'
+                flags'   ← #{peek struct msghdr, msg_flags} pHdr
+                return (addr, fromIntegral r, flags')
+      allocaBytesAligned (nn * #{size struct iovec})
+                         #{alignment struct iovec} $ \pIoVs → do
+        void . ($ bufs) . ($ pIoVs) . foldM $ \pIoV (p, n) → do
+          #{poke struct iovec, iov_base} pIoV p
+          #{poke struct iovec, iov_len}  pIoV (fromIntegral n ∷ CSize)
+          return $ plusPtr pIoV #{size struct iovec}
+        #{poke struct msghdr, msg_iov}        pHdr pIoVs
+        #{poke struct msghdr, msg_iovlen}     pHdr (fromIntegral nn ∷ CSize)
+        #{poke struct msghdr, msg_control}    pHdr nullPtr
+        #{poke struct msghdr, msg_controllen} pHdr (0 ∷ CSize)
+        #{poke struct msghdr, msg_flags}      pHdr (0 ∷ CInt)
+        doRecv
+
+recvBufsFrom' ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
+              ⇒ Socket f → [(Ptr Word8, Int)] → MsgFlags
+              → μ (Maybe (SockFamilyAddr f), Int, MsgFlags)
+recvBufsFrom' s bufs flags = withSocketFd s $ \fd →
+  recvBufsFromFd (undefined ∷ f) fd bufs flags
+
+-- | Receive a message from a connected socket, possibly utilizing multiple
+-- memory buffers. See /recvmsg(3)/.
+recvBufs ∷ (SockFamily f, MonadBase IO μ)
+         ⇒ Socket f           -- ^ The socket
+         → [(Ptr Word8, Int)] -- ^ Memory buffers
+         → MsgFlags           -- ^ Message flags
+         → μ (Int, MsgFlags)  -- ^ Received message length and flags
+recvBufs s bufs flags = do
+  (_, r, flags') ← recvBufsFrom' s bufs flags
+  return (r, flags')
+
+-- | Receive a message from a connected socket. See /recvmsg(3)/.
+recvBuf ∷ (SockFamily f, MonadBase IO μ)
+        ⇒ Socket f          -- ^ The socket
+        → Ptr α             -- ^ Buffer pointer
+        → Int               -- ^ Buffer length
+        → MsgFlags          -- ^ Message flags
+        → μ (Int, MsgFlags) -- ^ Received message length and flags
+recvBuf s p len flags = recvBufs s [(castPtr p, len)] flags
+
+-- | Receive a message from a connected socket. See /recvmsg(3)/.
+recv' ∷ (SockFamily f, MonadBase IO μ)
+      ⇒ Socket f                 -- ^ The socket
+      → Int                      -- ^ Maximum message length
+      → MsgFlags                 -- ^ Message flags
+      → μ (ByteString, MsgFlags) -- ^ Received message contents and flags
+recv' s len flags =
+  liftBase $ BS.createAndTrim' len $ \p → do
+    (r, flags') ← recvBuf s p len flags
+    return (0, r, flags')
+
+-- | Receive a message from a connected socket. See /recvmsg(3)/.
+recv ∷ (SockFamily f, MonadBase IO μ)
+     ⇒ Socket f     -- ^ The socket
+     → Int          -- ^ Maximum message length
+     → μ ByteString -- ^ Received message contents
+recv s len = fst <$> recv' s len noFlags
+
+-- | Receive a message from an unconnected socket, possibly utilizing multiple
+-- memory buffers. See /recvmsg(3)/.
+recvBufsFrom ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
+             ⇒ Socket f                            -- ^ The socket
+             → [(Ptr Word8, Int)]                  -- ^ Memory buffers
+             → MsgFlags                            -- ^ Message flags
+             → μ (SockFamilyAddr f, Int, MsgFlags)
+             -- ^ Received message source address, length, and flags
+recvBufsFrom s bufs flags = withSocketFd s $ \fd → do
+  (mAddr, n, flags') ← recvBufsFromFd (undefined ∷ f) fd bufs flags
+  let getpeername =
+        allocaMaxAddr (Proxy ∷ Proxy (SockFamilyAddr f)) $ \p size →
+          with size $ \pSize → do
+            throwErrnoIfMinus1_ "recv" $ c_getpeername fd p pSize
+            peekAddrOfSize (undefined ∷ f) p pSize
+  (, n, flags') <$> maybe getpeername return mAddr
+
+-- | Receive a message from an unconnected socket. See /recvmsg(3)/.
+recvBufFrom ∷ (SockFamily f, MonadBase IO μ)
+            ⇒ Socket f -- ^ The socket
+            → Ptr α    -- ^ Buffer pointer
+            → Int      -- ^ Buffer length
+            → MsgFlags -- ^ Message flags
+            → μ (SockFamilyAddr f, Int, MsgFlags)
+            -- ^ Received message source address, length, and flags
+recvBufFrom s p len flags = recvBufsFrom s [(castPtr p, len)] flags
+
+-- | Receive a message from an unconnected socket. See /recvmsg(3)/.
+recvFrom' ∷ (SockFamily f, MonadBase IO μ)
+          ⇒ Socket f -- ^ The socket
+          → Int      -- ^ Maximum message length
+          → MsgFlags -- ^ Message flags
+          → μ (SockFamilyAddr f, ByteString, MsgFlags)
+          -- ^ Received message source address, contents, and flags
+recvFrom' s len flags = liftBase $ do
+  (bs, (addr, flags')) ← BS.createAndTrim' len $ \p → do
+    (addr, len', flags') ← recvBufFrom s p len flags
+    return (0, len', (addr, flags'))
+  return (addr, bs, flags')
+
+-- | Receive a message from an unconnected socket. See /recvmsg(3)/.
+recvFrom ∷ (SockFamily f, MonadBase IO μ)
+         ⇒ Socket f -- ^ The socket
+         → Int      -- ^ Maximum message length
+         → μ (SockFamilyAddr f, ByteString)
+         -- ^ Received message source address and contents
+recvFrom s len = do
+  (addr, bs, _) ← recvFrom' s len noFlags
+  return (addr, bs)
+
+_sendBufs ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
+          ⇒ Socket f → [(Ptr Word8, Int)] → MsgFlags
+          → Maybe (SockFamilyAddr f) → μ Int
+_sendBufs s bufs' flags mAddr = withSocketFd s $ \fd → do
+  let (bufs, bufs'') = partition ((> 0) . snd) bufs'
+      nn             = length bufs
+  when (any ((< 0) . snd) bufs'') $ throwInval "send"
+  when (nn > #{const UIO_MAXIOV}) $ throwCustomErrno "send" eMSGSIZE
+  if nn == 0 then return 0
+  else allocaBytesAligned #{size struct msghdr}
+                          #{alignment struct msghdr} $ \pHdr → do
+    let doSend = do
+          r ← c_sendmsg fd pHdr flags
+          if r == -1 then do
+            errno ← getErrno
+            case errno of
+              e | e == eAGAIN || e == eWOULDBLOCK → do
+                threadWaitWrite fd
+                doSend
+              _ → throwErrno "send"
+          else
+            return $ fromIntegral r
+    let cont = allocaBytesAligned (nn * #{size struct iovec})
+                                  #{alignment struct iovec} $ \pIoVs → do
+          void . ($ bufs) . ($ pIoVs) . foldM $ \pIoV (p, n) → do
+            #{poke struct iovec, iov_base} pIoV p
+            #{poke struct iovec, iov_len}  pIoV (fromIntegral n ∷ CSize)
+            return $ plusPtr pIoV #{size struct iovec}
+          #{poke struct msghdr, msg_iov}        pHdr pIoVs
+          #{poke struct msghdr, msg_iovlen}     pHdr (fromIntegral nn ∷ CSize)
+          #{poke struct msghdr, msg_control}    pHdr nullPtr
+          #{poke struct msghdr, msg_controllen} pHdr (0 ∷ CSize)
+          #{poke struct msghdr, msg_flags}      pHdr (0 ∷ CInt)
+          doSend
+    case mAddr of
+      Just addr →
+        withAddr (undefined ∷ f) addr $ \pAddr addrLen → do
+          #{poke struct msghdr, msg_name}    pHdr pAddr
+          #{poke struct msghdr, msg_namelen} pHdr addrLen
+          cont
+      Nothing → do
+        #{poke struct msghdr, msg_name}    pHdr nullPtr
+        #{poke struct msghdr, msg_namelen} pHdr (0 ∷ #{itype socklen_t})
+        cont
+
+-- | Send a message split into several memory buffers on a connected socket.
+-- See /sendmsg(3)/.
+sendBufs ∷ (SockFamily f, MonadBase IO μ)
+         ⇒ Socket f           -- ^ The socket
+         → [(Ptr Word8, Int)] -- ^ Memory buffers
+         → MsgFlags           -- ^ Message flags
+         → μ Int              -- ^ The number of bytes sent 
+sendBufs s bufs flags = _sendBufs s bufs flags Nothing
+
+withBufs ∷ [ByteString] → ([(Ptr Word8, Int)] → IO α) → IO α
+withBufs bss f = go bss []
+  where go []          rbufs = f (reverse rbufs)
+        go (bs : bss') rbufs = BS.unsafeUseAsCStringLen bs $ \(p, len) →
+                                 go bss' ((castPtr p, len) : rbufs)
+
+-- | Send a message split into several 'ByteString's on a connected socket.
+-- See /sendmsg(3)/.
+sendMany' ∷ (SockFamily f, MonadBase IO μ)
+          ⇒ Socket f     -- ^ The socket
+          → [ByteString] -- ^ Message contents
+          → MsgFlags     -- ^ Message flags
+          → μ Int        -- ^ The number of bytes sent
+sendMany' s bss flags =
+  liftBase $ withBufs bss $ \bufs → sendBufs s bufs flags
+
+-- | Send a message split into several 'ByteString's on a connected socket.
+-- See /sendmsg(3)/.
+sendMany ∷ (SockFamily f, MonadBase IO μ)
+         ⇒ Socket f     -- ^ The socket
+         → [ByteString] -- ^ Message contents
+         → μ Int        -- ^ The number of bytes sent
+sendMany s bss = sendMany' s bss noFlags
+
+-- | Send a message on a connected socket. See /sendmsg(3)/.
+sendBuf ∷ (SockFamily f, MonadBase IO μ)
+        ⇒ Socket f -- ^ The socket
+        → Ptr α    -- ^ Buffer pointer
+        → Int      -- ^ Buffer length
+        → MsgFlags -- ^ Message flags
+        → μ Int    -- ^ The number of bytes sent
+sendBuf s p len flags = sendBufs s [(castPtr p, len)] flags
+
+-- | Send a message on a connected socket. See /sendmsg(3)/.
+send' ∷ (SockFamily f, MonadBase IO μ)
+      ⇒ Socket f   -- ^ The socket
+      → ByteString -- ^ Message contents
+      → MsgFlags   -- ^ Message flags
+      → μ Int      -- ^ The number of bytes sent
+send' s bs flags = liftBase $ BS.unsafeUseAsCStringLen bs $ \(p, len) →
+                     sendBuf s p len flags
+
+-- | Send a message on a connected socket. See /sendmsg(3)/.
+send ∷ (SockFamily f, MonadBase IO μ)
+     ⇒ Socket f   -- ^ The socket
+     → ByteString -- ^ Message contents
+     → μ Int      -- ^ The number of bytes sent
+send s bs = send' s bs noFlags
+
+-- | Send a message split into several memory buffers on an unconnected
+-- socket. See /sendmsg(3)/.
+sendBufsTo ∷ (SockFamily f, MonadBase IO μ)
+           ⇒ Socket f           -- ^ The socket
+           → [(Ptr Word8, Int)] -- ^ Memory buffers
+           → MsgFlags           -- ^ Message flags
+           → SockFamilyAddr f   -- ^ Message destination address
+           → μ Int              -- ^ The number of bytes sent
+sendBufsTo s bufs flags addr = _sendBufs s bufs flags (Just addr)
+
+-- | Send a message split into several 'ByteString's on an unconnected socket.
+-- See /sendmsg(3)/.
+sendManyTo' ∷ (SockFamily f, MonadBase IO μ)
+            ⇒ Socket f         -- ^ The socket
+            → [ByteString]     -- ^ Message contents
+            → MsgFlags         -- ^ Message flags
+            → SockFamilyAddr f -- ^ Message destination address
+            → μ Int            -- ^ The number of bytes sent
+sendManyTo' s bss flags addr = liftBase $ withBufs bss $ \bufs →
+                                 sendBufsTo s bufs flags addr
+
+-- | Send a message split into several 'ByteString's on an unconnected socket.
+-- See /sendmsg(3)/.
+sendManyTo ∷ (SockFamily f, MonadBase IO μ)
+           ⇒ Socket f         -- ^ The socket
+           → [ByteString]     -- ^ Message contents
+           → SockFamilyAddr f -- ^ Message destination address
+           → μ Int            -- ^ The number of bytes sent
+sendManyTo s bss addr = sendManyTo' s bss noFlags addr
+
+-- | Send a message on an unconnected socket. See /sendmsg(3)/.
+sendBufTo ∷ (SockFamily f, MonadBase IO μ)
+          ⇒ Socket f         -- ^ The socket
+          → Ptr α            -- ^ Buffer pointer
+          → Int              -- ^ Buffer length
+          → MsgFlags         -- ^ Message flags
+          → SockFamilyAddr f -- ^ Message destination address
+          → μ Int            -- ^ The number of bytes sent
+sendBufTo s p len flags addr = sendBufsTo s [(castPtr p, len)] flags addr
+
+-- | Send a message on an unconnected socket. See /sendmsg(3)/.
+sendTo' ∷ (SockFamily f, MonadBase IO μ)
+        ⇒ Socket f         -- ^ The socket
+        → ByteString       -- ^ Message contents
+        → MsgFlags         -- ^ Message flags
+        → SockFamilyAddr f -- ^ Message destination address
+        → μ Int            -- ^ The number of bytes sent
+sendTo' s bs flags addr =
+  liftBase $ BS.unsafeUseAsCStringLen bs $ \(p, len) →
+    sendBufTo s p len flags addr
+
+-- | Send a message on an unconnected socket. See /sendmsg(3)/.
+sendTo ∷ (SockFamily f, MonadBase IO μ)
+       ⇒ Socket f         -- ^ The socket
+       → ByteString       -- ^ Message contents
+       → SockFamilyAddr f -- ^ Message destination address
+       → μ Int            -- ^ The number of bytes sent
+sendTo s bs addr = sendTo' s bs noFlags addr
+
+-- | Shut down a part of a full-duplex connection. See /shutdown(3)/.
+shutdown ∷ MonadBase IO μ ⇒ Socket f → SockOps → μ ()
+shutdown s dirs = withSocketFd s $ \fd →
+    forM_ how $ throwErrnoIfMinus1_ "shutdown" . c_shutdown fd
+  where how = if dirs .>=. sendSockOp then
+                if dirs .>=. recvSockOp then
+                  Just #{const SHUT_RDWR}
+                else
+                  Just #{const SHUT_WR}
+              else
+                if dirs .>=. recvSockOp then
+                  Just #{const SHUT_RD}
+                else
+                  Nothing
+
+-- | Close the socket. See /close(3)/.
+close ∷ MonadBase IO μ ⇒ Socket f → μ ()
+close (Socket v) = liftBase $ modifyMVar_ v $ \fd → do
+  when (fd >= 0) $ closeFdWith closeFd fd
+  return (-1)
+
+foreign import ccall "socket"
+  c_socket ∷ CInt → CInt → SockProto → IO CInt
+foreign import ccall "getsockopt"
+  c_getsockopt ∷ Fd → CInt → CInt → Ptr α → Ptr #{itype socklen_t} → IO CInt
+foreign import ccall "setsockopt"
+  c_setsockopt ∷ Fd → CInt → CInt → Ptr α → #{itype socklen_t} → IO CInt
+foreign import ccall "bind"
+  c_bind ∷ Fd → Ptr α → #{itype socklen_t} → IO CInt
+foreign import ccall "connect"
+  c_connect ∷ Fd → Ptr α → #{itype socklen_t} → IO CInt
+foreign import ccall "listen"
+  c_listen ∷ Fd → CInt → IO CInt
+#ifdef HAVE_ACCEPT_WITH_FLAGS
+foreign import ccall "accept4"
+  c_accept ∷ Fd → Ptr α → Ptr #{itype socklen_t} → CInt → IO CInt
+#else
+foreign import ccall "accept"
+  c_accept ∷ Fd → Ptr α → Ptr #{itype socklen_t} → IO CInt
+#endif
+foreign import ccall "getsockname"
+  c_getsockname ∷ Fd → Ptr α → Ptr #{itype socklen_t} → IO CInt
+foreign import ccall "getpeername"
+  c_getpeername ∷ Fd → Ptr α → Ptr #{itype socklen_t} → IO CInt
+foreign import ccall "sockatmark"
+  c_sockatmark ∷ Fd → IO CInt
+foreign import ccall "recvmsg"
+  c_recvmsg ∷ Fd → Ptr β → MsgFlags → IO CSsize
+foreign import ccall "sendmsg"
+  c_sendmsg ∷ Fd → Ptr β → MsgFlags → IO CSsize
+foreign import ccall "shutdown"
+  c_shutdown ∷ Fd → CInt → IO CInt
+
diff --git a/src/System/Posix/Socket/Inet.hsc b/src/System/Posix/Socket/Inet.hsc
new file mode 100644
--- /dev/null
+++ b/src/System/Posix/Socket/Inet.hsc
@@ -0,0 +1,68 @@
+{-# LANGUAGE UnicodeSyntax #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+-- | Internet address families.
+module System.Posix.Socket.Inet
+  ( AF_INET(..)
+  , AF_INET6(..)
+  ) where
+
+import Data.Typeable (Typeable)
+import Network.IP.Addr
+import Control.Applicative ((<$>), (<*>))
+import Foreign.C.Types (CSize)
+import Foreign.Storable (Storable(..))
+import System.Posix.Socket
+
+#include <sys/socket.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+
+-- | IPv4 socket family.
+data AF_INET = AF_INET deriving (Typeable, Eq, Show)
+
+instance SockAddr Inet4Addr where
+  sockAddrMaxSize _ = #{size struct sockaddr_in}
+  sockAddrSize    _ = #{size struct sockaddr_in}
+  peekSockAddr p sz =
+    if sz /= #{size struct sockaddr_in}
+      then ioError $ userError $
+             "peekSockAddr(Inet4Addr): invalid size " ++ show sz ++
+             " (expected " ++ show (#{size struct sockaddr_in} ∷ CSize) ++ ")"
+      else InetAddr <$> #{peek struct sockaddr_in, sin_addr} p
+                    <*> #{peek struct sockaddr_in, sin_port} p
+  pokeSockAddr p (InetAddr addr port) = do
+    #{poke struct sockaddr_in, sin_port} p port
+    #{poke struct sockaddr_in, sin_addr} p addr
+
+instance SockFamily AF_INET where
+  type SockFamilyAddr AF_INET = Inet4Addr
+  sockFamilyCode _ = #const AF_INET
+
+-- | IPv6 socket family.
+data AF_INET6 = AF_INET6 deriving (Typeable, Eq, Show)
+
+instance SockAddr Inet6Addr where
+  sockAddrMaxSize _ = #{size struct sockaddr_in6}
+  sockAddrSize    _ = #{size struct sockaddr_in6}
+  peekSockAddr p sz =
+    if sz /= #{size struct sockaddr_in6}
+      then ioError $ userError $
+             "peekSockAddr(Inet6Addr): invalid size " ++ show sz ++
+             " (expected " ++ show (#{size struct sockaddr_in6} ∷ CSize) ++ ")"
+      else InetAddr <$> #{peek struct sockaddr_in6, sin6_addr} p
+                    <*> #{peek struct sockaddr_in6, sin6_port} p
+  pokeSockAddr p (InetAddr addr port) = do
+    #{poke struct sockaddr_in6, sin6_port} p port
+    #{poke struct sockaddr_in6, sin6_addr} p addr
+
+instance SockFamily AF_INET6 where
+  type SockFamilyAddr AF_INET6 = Inet6Addr
+  sockFamilyCode _ = #const AF_INET6
+
diff --git a/src/System/Posix/Socket/Local.hsc b/src/System/Posix/Socket/Local.hsc
new file mode 100644
--- /dev/null
+++ b/src/System/Posix/Socket/Local.hsc
@@ -0,0 +1,62 @@
+{-# LANGUAGE UnicodeSyntax #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE TypeFamilies #-}
+
+-- | Local address family.
+module System.Posix.Socket.Local
+  ( LocalAddr(..)
+  , aLocalAddr
+  , AF_LOCAL(..)
+  ) where
+
+import Data.Typeable (Typeable)
+import Data.Proxy (Proxy(..))
+import Data.Word (Word8)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Internal as BS
+import qualified Data.ByteString.Unsafe as BS
+import Control.Applicative ((<$>))
+import Foreign.Ptr (castPtr, plusPtr)
+import Foreign.Storable (Storable(..))
+import System.Posix.Socket
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+-- | Local socket family.
+data AF_LOCAL = AF_LOCAL deriving (Typeable, Eq, Show)
+
+-- | Local socket address.
+data LocalAddr = LocalAddr ByteString
+               | NoLocalAddr
+               deriving (Typeable, Eq, Ord, Show)
+
+-- | 'LocalAddr' proxy value.
+aLocalAddr ∷ Proxy LocalAddr
+aLocalAddr = Proxy
+
+instance SockAddr LocalAddr where
+  sockAddrMaxSize _ = #{size struct sockaddr_un}
+  sockAddrSize (LocalAddr path) = #{offset struct sockaddr_un, sun_path}
+                                + BS.length path + 1
+  sockAddrSize NoLocalAddr = #{size sa_family_t}
+  peekSockAddr p sz = do
+    let offset = #{offset struct sockaddr_un, sun_path}
+    if sz < offset + 1
+      then return NoLocalAddr
+      else LocalAddr <$> BS.packCStringLen
+                           (castPtr $ plusPtr p offset, sz - offset - 1)
+  pokeSockAddr p (LocalAddr path) = do
+    let offset = #{offset struct sockaddr_un, sun_path}
+    BS.unsafeUseAsCStringLen path $ \(pBs, len) → do
+      BS.memcpy (castPtr $ plusPtr p offset) (castPtr pBs) (fromIntegral len)
+      poke (castPtr $ plusPtr p $ offset + len) (0 ∷ Word8)
+  pokeSockAddr _ NoLocalAddr = return ()
+
+instance SockFamily AF_LOCAL where
+  type SockFamilyAddr AF_LOCAL = LocalAddr
+  sockFamilyCode _ = #const AF_LOCAL
+
