diff --git a/COPYING b/COPYING
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
-Copyright (c) 2008, Taru Karttunen
+Copyright (c) 2008-2014, Taru Karttunen
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/Network/Fancy.hsc b/Network/Fancy.hsc
--- a/Network/Fancy.hsc
+++ b/Network/Fancy.hsc
@@ -6,12 +6,13 @@
      -- * Datagram clients
      connectDgram, withDgram, StringLike, recv,send, closeSocket,
      -- * Servers
-     ServerSpec(..), serverSpec, 
+     ServerSpec(..), serverSpec,
      Threading(..), Reverse(..),
      streamServer, dgramServer, sleepForever,
      -- * Other
      getCurrentHost,
-     Socket
+     Socket,
+     NetworkException(..),
     ) where
 
 import Control.Concurrent
@@ -28,7 +29,8 @@
 #else
 import Foreign hiding (unsafeForeignPtrToPtr)
 #endif
-import Foreign.C
+import Foreign.C(CString,peekCString,withCString,Errno(..),eAGAIN,eINTR,eWOULDBLOCK,getErrno,eINPROGRESS)
+import Foreign.C.Types
 import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
 import Numeric(showHex)
 import System.IO(Handle, hClose, IOMode(ReadWriteMode))
@@ -44,6 +46,9 @@
 import GHC.Conc(asyncDoProc)
 #endif
 
+import Network.Fancy.Error
+import Network.Fancy.Internal
+
 #ifndef WINDOWS
 #include <arpa/inet.h>
 #include <errno.h>
@@ -81,11 +86,11 @@
 #endif /* WINDOWS */
 
 setNonBlockingFD' :: FD -> IO ()
-setNonBlockingFD' =
+setNonBlockingFD' fd =
 #if __GLASGOW_HASKELL__ < 611
-    System.Posix.Internals.setNonBlockingFD
+    System.Posix.Internals.setNonBlockingFD fd
 #else
-    flip System.Posix.Internals.setNonBlockingFD True
+    System.Posix.Internals.setNonBlockingFD fd True
 #endif
 
 type HostName = String
@@ -114,35 +119,38 @@
 
 -- | Send the string as one chunk
 send :: StringLike string => Socket -> string -> IO ()
-send (Socket s) bs = B.unsafeUseAsCStringLen (toBS bs) $ \(ptr,len) -> do
-                     r <- throwErrnoIfMinus1RetryMayBlock "send" (c_send s (castPtr ptr) (fromIntegral len) 0) (threadWaitWrite (fromIntegral s))
-                     when (r/=fromIntegral len) $ fail "send: partial packet sent!"
+send (Socket s) bs =
+  let loop ptr len = do
+        r <- writeOp "send" s (c_send s (castPtr ptr) (fromIntegral len) 0)
+        let r' = fromIntegral r
+        if r' >= len then return () else loop (plusPtr ptr r') (r' - len)
+  in B.unsafeUseAsCStringLen (toBS bs) $ \(ptr,len) -> loop ptr len
 -- | Receive one chunk with given maximum size
 recv :: StringLike string => Socket -> Int -> IO string
 recv (Socket s) len= fmap fromBS (
                      B.createAndTrim len $ \ptr -> do
-                     r <- throwErrnoIfMinus1RetryMayBlock "recv" (c_recv s (castPtr ptr) (fromIntegral len) 0) (threadWaitRead (fromIntegral s))
+                     r <- readOp "recv" s (c_recv s (castPtr ptr) (fromIntegral len) 0)
                      return $ fromIntegral r)
 
 recvFrom :: StringLike string => Socket -> Int -> SocketAddress -> IO (string,SocketAddress)
 recvFrom (Socket s) buflen (SA _ salen) = do
   sa <- mallocForeignPtrBytes salen
   withForeignPtr sa $ \sa_ptr -> do
-  str<- B.createAndTrim buflen $ \ptr -> do
+  (str,len) <- B.createAndTrim' buflen $ \ptr -> do
     with (fromIntegral salen) $ \salen_ptr -> do
-    fmap fromIntegral $ throwErrnoIfMinus1RetryMayBlock "recvfrom"
-                                                        (c_recvfrom s ptr (fromIntegral buflen) 0 sa_ptr salen_ptr)
-                                                        (threadWaitRead (fromIntegral s))
-  return (fromBS str, SA sa salen)
+    rd  <- readOp "recvfrom" s $ c_recvfrom s ptr (fromIntegral buflen) 0 sa_ptr salen_ptr
+    len <- peek salen_ptr
+    return (0,fromIntegral rd, fromIntegral len)
+  return (fromBS str, SA sa len)
 
 sendTo :: StringLike string => SocketAddress -> Socket -> string -> IO ()
 sendTo (SA sa salen) (Socket s) str = do
   withForeignPtr sa $ \sa_ptr -> do
-  B.unsafeUseAsCStringLen (toBS str) $ \(ptr,len) -> do
-  r <- throwErrnoIfMinus1RetryMayBlock "sendTo" 
-                                       (c_sendto s (castPtr ptr) (fromIntegral len) 0 sa_ptr (fromIntegral salen))
-                                       (threadWaitWrite (fromIntegral s))
-  when (r/=fromIntegral len) $ fail "sendTo: partial packet sent!"
+  let loop ptr len = do
+        r <- writeOp "sendTo" s $ c_sendto s (castPtr ptr) (fromIntegral len) 0 sa_ptr (fromIntegral salen)
+        let r' = fromIntegral r
+        if r' >= len then return () else loop (plusPtr ptr r') (r' - len)
+  B.unsafeUseAsCStringLen (toBS str) $ \(ptr,len) -> loop ptr len
 
 foreign import CALLCONV SAFE_ON_WIN "recv" c_recv :: CInt -> Ptr Word8 -> CSize -> CInt -> IO (#type ssize_t)
 foreign import CALLCONV SAFE_ON_WIN "send" c_send :: CInt -> Ptr Word8 -> CSize -> CInt -> IO (#type ssize_t)
@@ -151,7 +159,7 @@
 
 -- | Close the socket specified.
 closeSocket :: Socket -> IO ()
-closeSocket (Socket fd) = throwErrnoIfMinus1_ "close" $ c_close fd
+closeSocket sock@(Socket fd) = throwIfError_ sock "close" $ c_close fd
 
 foreign import CALLCONV unsafe "bind"    c_bind    :: CInt -> Ptr () -> (SLen) -> IO CInt
 foreign import CALLCONV unsafe "listen"  c_listen  :: CInt -> CInt -> IO CInt
@@ -163,7 +171,6 @@
 foreign import CALLCONV unsafe "close" c_close :: CInt -> IO CInt
 #endif
 
-newtype Socket = Socket CInt
 
 -- | Open a datagram (udp) socket for the given block and close it afterwards.
 withDgram :: Address -> (Socket -> IO a) -> IO a
@@ -192,17 +199,18 @@
 connect :: CType -> SocketAddress -> IO Socket
 connect stype (SA sa len) = do
   fam <- getFamily (SA sa len)
-  s   <- throwErrnoIfMinus1 "socket" $ c_socket fam stype 0
+  s   <- newsock fam stype
   setNonBlockingFD' s
+  let sock = Socket s
   let loop = do r   <- withForeignPtr sa $ \ptr -> c_connect s ptr (fromIntegral len)
 	       	err <- getErrno
        	        case r of
                   -1 | err == eINTR       -> do loop
 		     | err == eINPROGRESS -> do threadWaitWrite (fromIntegral s)
                                                 soe <- getsockopt_error s
-                                                if soe==0 then return (Socket s) else fail "connect"
-                     |  otherwise         -> do fail "connect"
-                  _                       -> do return $ Socket s
+                                                if soe==0 then return sock else throwNetworkException sock "connect" (Errno soe)
+                     |  otherwise         -> do throwNetworkException sock "connect" err
+                  _                       -> do return sock
   loop
 
 foreign import ccall unsafe getsockopt_error :: CInt -> IO CInt
@@ -212,10 +220,10 @@
 getFamily :: SocketAddress -> IO CFamily
 getFamily (SA sa _) = worker >>= return . fromIntegral
     where worker :: IO #type sa_family_t
-	  worker = withForeignPtr sa (#peek struct sockaddr, sa_family) 
+	  worker = withForeignPtr sa (#peek struct sockaddr, sa_family)
 
 csas :: (SocketAddress -> IO a) -> [SocketAddress] -> IO a
-csas _ []       = fail "No such host"
+csas _ []       = throwOther NoSuchHostException
 csas c [sa]     = c sa
 csas c (sa:sas) = do x <- try' (c sa)
                      case x of
@@ -225,6 +233,26 @@
 try' :: IO a -> IO (Either SomeException a)
 try' = E.try
 
+writeOp :: String -> CInt -> IO Int64 -> IO Int64
+writeOp desc s op = loop
+    where fd = fromIntegral s
+          loop = do res <- op
+                    if res /= -1 then return res else getErrno >>= eh
+          eh err | err == eINTR = loop
+                 | err == eWOULDBLOCK || err == eAGAIN = threadWaitWrite fd >> loop
+                 | True = throwNetworkException (Socket s) desc err
+
+readOp :: String -> CInt -> IO Int64 -> IO Int64
+readOp desc s op = loop
+    where fd = fromIntegral s
+          loop = do res <- op
+                    if res /= -1 then return res else getErrno >>= eh
+          eh err | err == eINTR = loop
+                 | err == eWOULDBLOCK || err == eAGAIN = threadWaitRead fd >> loop
+                 | True = throwNetworkException (Socket s) desc err
+
+
+
 withResolverLock :: IO a -> IO a
 #ifdef WINDOWS
 withResolverLock c = resolverLock `seq` c
@@ -236,7 +264,10 @@
 withResolverLock x = x
 #endif
 
-data SocketAddress = SA !(ForeignPtr ()) !Int deriving(Show)
+data SocketAddress = SA !(ForeignPtr ()) !Int
+
+instance Show SocketAddress where show _ = "SocketAddress"
+
 type AddrInfoT     = Word8
 
 type CFamily      = Int
@@ -255,10 +286,10 @@
 a2sas t f (IPv4 hn p)        = getAddrInfo hn (show p) f afInet t
 a2sas t f (IPv6 hn p)        = getAddrInfo hn (show p) f afInet6 t
 #ifdef WINDOWS
-a2sas _ _ (Unix _)           = fail "Unix sockets not supported on Windows"
+a2sas _ _ (Unix _)           = throwOther UnsupportedAddressFamily
 #else
 a2sas _ _ (Unix fp)          = do let maxSize = ((#size struct sockaddr_un)-(#offset struct sockaddr_un, sun_path))
-                                  when (length fp >= maxSize) $ fail "Too long address for Unix socket"
+                                  when (length fp >= maxSize) $ throwOther AddressTooLongException
                                   sa <- mallocForeignPtrBytes $ fromIntegral salLocal
                                   withForeignPtr sa $ \sa_ptr -> do
                                   (#poke struct sockaddr_un, sun_family) sa_ptr afLocal
@@ -267,7 +298,7 @@
                                   pokeArray0 0 ((#ptr struct sockaddr_un, sun_path) sa_ptr) $ map tw fp
                                   return [SA sa salLocal]
 salLocal :: Int
-salLocal   =  #size struct sockaddr_un 
+salLocal   =  #size struct sockaddr_un
 
 #endif
 
@@ -313,40 +344,13 @@
   bracket getAI c_freeaddrinfo unai
 
 foreign import CALLCONV unsafe "freeaddrinfo" c_freeaddrinfo :: Ptr AddrInfoT -> IO ()
-foreign import CALLCONV   safe "getaddrinfo"  c_getaddrinfo  :: Ptr CChar -> Ptr CChar -> 
+foreign import CALLCONV   safe "getaddrinfo"  c_getaddrinfo  :: Ptr CChar -> Ptr CChar ->
 							     Ptr AddrInfoT -> Ptr (Ptr AddrInfoT) ->
 							     IO CInt
 
 
-throwGAIErrorIf :: IO CInt -> IO ()
-throwGAIErrorIf comp = do 
-  err <- comp
-  when (err /= 0) (gaiError err >>= fail)
 
--- Don't use gai_strerror with winsock - it is not thread-safe there.
-gaiError :: CInt -> IO String
-#ifdef WINDOWS
 
-gaiError (#const EAI_AGAIN)    = return "Temporary failure in name resolution."
-gaiError (#const EAI_BADFLAGS) = return "Invalid value for ai_flags."
-gaiError (#const EAI_FAIL)     = return "Nonrecoverable failure in name resolution."
-gaiError (#const EAI_FAMILY)   = return "The ai_family member is not supported."
-gaiError (#const EAI_MEMORY)   = return "Memory allocation failure."
-gaiError (#const EAI_NODATA)   = return "No address associated with nodename."
-gaiError (#const EAI_NONAME)   = return "Neither nodename nor servname provided, or not known."
-gaiError (#const EAI_SERVICE)  = return "The servname parameter is not supported for ai_socktype."
-gaiError (#const EAI_SOCKTYPE) = return "The ai_socktype member is not supported."
-gaiError x                     = return ("Unknown gai_error value "++show x)
-
-#else
-
-gaiError err = c_gai_strerror err >>= peekCString
-
-foreign import CALLCONV unsafe "gai_strerror" c_gai_strerror :: CInt -> IO (Ptr CChar)
-
-#endif
-
-
 --hostNameToNumber :: Address -> IO [Address]
 --hostNameToNumber = error "FIXME"
 --hostNumberToName :: Address -> IO [Address]
@@ -356,11 +360,14 @@
 getCurrentHost :: IO HostName
 getCurrentHost = do
   allocaArray 256 $ \buffer -> do
-    throwErrnoIfMinus1_ "gethostname" $ c_gethostname buffer 256
+    throwIfError_ invSock "gethostname" $ c_gethostname buffer 256
     peekCString buffer
 
 foreign import CALLCONV unsafe "gethostname" c_gethostname :: Ptr CChar -> CSize -> IO CInt
 
+invSock :: Socket
+invSock = Socket (-1)
+
 -- SERVERS
 
 data Threading  = Threaded  -- ^ Run each request in a separate thread without blocking the server loop.
@@ -389,7 +396,7 @@
 streamServer :: ServerSpec -> (Handle -> Address -> IO ()) -> IO [ThreadId]
 streamServer ss sfun = do
   sas <- a2sas sockStream (aiNumericserv .|. aiPassive) (address ss)
-  when (null sas) $ fail "No address for server!"
+  when (null sas) $ throwOther NoSuchHostException
   let sf ha psa = case threading ss of
                     Threaded -> forkIO (clo ha $ sfun ha psa) >> return ()
                     Inline   -> clo ha $ sfun ha psa
@@ -398,7 +405,7 @@
                 False -> id
   forM sas $ \sa -> do
      fam  <- getFamily sa
-     sock <- throwErrnoIfMinus1 "socket" $ c_socket fam sockStream 0
+     sock <- newsock fam sockStream
      setNonBlockingFD' sock
      let socket = Socket sock
      let on :: CInt
@@ -416,18 +423,21 @@
                    loop
      forkIO loop
 
+newsock :: CFamily -> CType -> IO CInt
+newsock fam typ = throwIfError invSock "socket" $ c_socket fam typ 0
+
 foreign import CALLCONV unsafe "setsockopt" c_setsockopt ::
   CInt -> CInt -> CInt -> Ptr a -> CInt -> IO CInt
 
 -- | Bind a socket to an address. Be wary of AF_LOCAL + NFS blocking?
 bind :: Socket -> SocketAddress -> IO ()
-bind (Socket sock) (SA sa len) = do
-  withForeignPtr sa $ \sa_ptr -> 
-    throwErrnoIfMinus1_ "bind" $ c_bind sock sa_ptr (fromIntegral len)
+bind s@(Socket fd) (SA sa len) = do
+  withForeignPtr sa $ \sa_ptr ->
+    throwIfError_ s "bind" $ c_bind fd sa_ptr (fromIntegral len)
 
 -- | Listen on an socket
 listen :: Socket -> Int -> IO ()
-listen (Socket s) iv = throwErrnoIfMinus1_ "listen" (c_listen s (toEnum iv))
+listen s@(Socket fd) iv = throwIfError_ s "listen" (c_listen fd (toEnum iv))
 
 accept :: Socket -> SocketAddress -> IO (Socket, SocketAddress)
 accept (Socket lfd) (SA _ len) = do
@@ -435,10 +445,10 @@
   s  <- withForeignPtr sa $ \sa_ptr -> do
 #ifndef WINDOWS
           with (fromIntegral len) $ \len_ptr -> do
-            throwErrnoIfMinus1RetryMayBlock "accept" (c_accept lfd sa_ptr len_ptr) (threadWaitRead (fromIntegral lfd))
+            readOp "accept" lfd $ fmap fromIntegral $ c_accept lfd sa_ptr len_ptr
 #else
           if threaded then with (fromIntegral len) $ \len_ptr -> do
-                           throwErrnoIfMinus1 "accept" (c_accept lfd sa_ptr len_ptr)
+                           throwIfError (Socket lfd) "accept" (c_accept lfd sa_ptr len_ptr)
                       else allocaBytes (#size struct network_fancy_aaccept) $ \ptr -> do
                            (#poke struct network_fancy_aaccept, s)    ptr lfd
                            (#poke struct network_fancy_aaccept, addr) ptr sa_ptr
@@ -446,10 +456,11 @@
                            r <- asyncDoProc c_nf_async_accept ptr
                            when (r /= 0) $ ioError (errnoToIOError "accept" (Errno (fromIntegral r)) Nothing Nothing)
                            (#peek struct network_fancy_aaccept, s) ptr
-                           
+
 #endif
-  setNonBlockingFD' s
-  return (Socket s,SA sa len)
+  let s' = fromIntegral s
+  setNonBlockingFD' s'
+  return (Socket s',SA sa len)
 
 foreign import CALLCONV SAFE_ON_WIN "accept"  c_accept  :: CInt -> Ptr () -> Ptr (SLen) -> IO CInt
 #ifdef WINDOWS
@@ -463,10 +474,10 @@
                 -> IO [ThreadId] -- ^ ThreadIds of the server listener processes.
 dgramServer ss sfun = do
   sas <- a2sas sockDgram (aiNumericserv .|. aiPassive) (address ss)
-  when (null sas) $ fail "No address for server!"
+  when (null sas) $ throwOther NoSuchHostException
   forM sas $ \sa -> do
      fam  <- getFamily sa
-     sock <- throwErrnoIfMinus1 "socket" $ c_socket fam sockDgram 0
+     sock <- newsock fam sockDgram
      setNonBlockingFD' sock
      let socket = Socket sock
      let on :: CInt
@@ -508,10 +519,11 @@
                           p <- ntohs =<< (#peek struct sockaddr_in6, sin6_port) sa_ptr
                           return $ IPv6 n (fromIntegral p)
 #ifndef WINDOWS
-      | f == afLocal-> do n <- peekCString $ (#ptr struct sockaddr_un, sun_path) sa_ptr
+      | f == afLocal-> do if len == 0 then return (Unix "") else do
+                          n <- peekCString $ (#ptr struct sockaddr_un, sun_path) sa_ptr
                           return $ Unix n
 #endif
-      | otherwise   -> do fail "Unsupported address family!"
+      | otherwise   -> do throwOther UnsupportedAddressFamilyException
 
 foreign import CALLCONV unsafe ntohs :: Word16 -> IO Word16
 
@@ -532,7 +544,7 @@
       | f == afLocal -> do n <- peekCString $ (#ptr struct sockaddr_un, sun_path) sa_ptr
                            return $ Unix n
 #endif
-      | otherwise    -> do fail "Unsupported address family!"
+      | otherwise    -> do throwOther UnsupportedAddressFamilyException
 
 
 type SLen = #type socklen_t
diff --git a/Network/Fancy/Error.hs b/Network/Fancy/Error.hs
new file mode 100644
--- /dev/null
+++ b/Network/Fancy/Error.hs
@@ -0,0 +1,102 @@
+-----------------------------------------------------------------------------
+--
+-- Module      :  Network.Fancy.Error
+-- Copyright   :  Taru Karttunen <taruti@taruti.net>
+-- License     :  BSD3
+--
+-- Maintainer  :  taruti@taruti.net
+-- Stability   :
+-- Portability :
+--
+-- |
+--
+-----------------------------------------------------------------------------
+
+module Network.Fancy.Error (
+    NetworkException(..),
+    throwGAIErrorIf,
+    throwIfError,
+    throwIfError_,
+    throwNetworkException,
+    throwOther,
+) where
+
+import Control.Exception
+import Control.Monad
+import Data.Typeable
+import Foreign
+import Foreign.C
+import System.IO.Unsafe
+
+import Network.Fancy.Internal
+
+-- | Exceptions occuring in network-fancy.
+data NetworkException = SocketException !String !Socket !Errno
+                      | GetAdddrInfoException !CInt
+                      | UnsupportedAddressFamilyException
+                      | NoSuchHostException
+                      | AddressTooLongException
+                        deriving(Typeable)
+
+instance Exception NetworkException
+
+instance Show NetworkException where
+    show (SocketException s _ v) = s ++ ": " ++ strerror v
+    show (GetAdddrInfoException v) = unsafePerformIO $ gaiError v
+    show UnsupportedAddressFamilyException = "Unsupported address family"
+    show NoSuchHostException = "No such host"
+    show AddressTooLongException = "Network address too long"
+
+throwOther :: NetworkException -> IO any
+throwOther x = throwIO $! x
+
+throwIfError_ :: Socket -> String -> IO CInt -> IO ()
+throwIfError_ sock desc act = throwIfError sock desc act >> return ()
+
+
+throwIfError :: Socket -> String -> IO CInt -> IO CInt
+throwIfError sock desc act = do
+    res <- act
+    when (res == -1) (throwIO . SocketException desc sock =<< getErrno)
+    return res
+
+throwNetworkException :: Socket -> String -> Errno -> IO any
+throwNetworkException sock desc err = throwIO $! SocketException desc sock err
+
+
+strerror :: Errno -> String
+strerror (Errno val) = unsafePerformIO $
+  allocaArray 512 $ \buffer -> do
+    _ <- c_strerror_r val buffer 511
+    peekCString buffer
+
+foreign import ccall unsafe "strerror_r" c_strerror_r :: CInt -> Ptr CChar -> CSize -> IO CInt
+
+throwGAIErrorIf :: IO CInt -> IO ()
+throwGAIErrorIf comp = do
+  err <- comp
+  when (err /= 0) $ throwIO $ GetAdddrInfoException err
+
+-- Don't use gai_strerror with winsock - it is not thread-safe there.
+gaiError :: CInt -> IO String
+#ifdef WINDOWS
+
+gaiError (#const EAI_AGAIN)    = return "Temporary failure in name resolution."
+gaiError (#const EAI_BADFLAGS) = return "Invalid value for ai_flags."
+gaiError (#const EAI_FAIL)     = return "Nonrecoverable failure in name resolution."
+gaiError (#const EAI_FAMILY)   = return "The ai_family member is not supported."
+gaiError (#const EAI_MEMORY)   = return "Memory allocation failure."
+gaiError (#const EAI_NODATA)   = return "No address associated with nodename."
+gaiError (#const EAI_NONAME)   = return "Neither nodename nor servname provided, or not known."
+gaiError (#const EAI_SERVICE)  = return "The servname parameter is not supported for ai_socktype."
+gaiError (#const EAI_SOCKTYPE) = return "The ai_socktype member is not supported."
+gaiError x                     = return ("Unknown gai_error value "++show x)
+
+#else
+
+gaiError err = c_gai_strerror err >>= peekCString
+
+foreign import CALLCONV unsafe "gai_strerror" c_gai_strerror :: CInt -> IO (Ptr CChar)
+
+#endif
+
diff --git a/Network/Fancy/Internal.hs b/Network/Fancy/Internal.hs
new file mode 100644
--- /dev/null
+++ b/Network/Fancy/Internal.hs
@@ -0,0 +1,23 @@
+-----------------------------------------------------------------------------
+--
+-- Module      :  Network.Fancy.Internal
+-- Copyright   :  Taru Karttunen <taruti@taruti.net>
+-- License     :  BSD3
+--
+-- Maintainer  :  taruti@taruti.net
+-- Stability   :
+-- Portability :
+--
+-- |
+--
+-----------------------------------------------------------------------------
+
+module Network.Fancy.Internal (
+  Socket(..),
+) where
+
+
+import Foreign.C
+
+newtype Socket = Socket CInt
+
diff --git a/cbits.c b/cbits.c
--- a/cbits.c
+++ b/cbits.c
@@ -26,7 +26,7 @@
 
 
 #endif
-int getSocketError(void) { return errno; }
+//int getSocketError(void) { return errno; }
 
 int getsockopt_error(int fd) {
   int estat, res;
diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,6 @@
+# network-fancy changelog
+
+## Version 0.2.0 2014-12-09
+
+- Use NetworkExceptions for precise error reporting
+- Handle unix domain sockets wich don't set SocketAddr on recvFrom
diff --git a/network-fancy.cabal b/network-fancy.cabal
--- a/network-fancy.cabal
+++ b/network-fancy.cabal
@@ -1,34 +1,50 @@
-Name:                network-fancy
-Version:             0.1.5.2
-Synopsis:            Networking support with a cleaner API
-Description:         Networking support with a cleaner API
-License:             BSD3
-License-file:        COPYING
-Copyright:           Taru Karttunen <taruti@taruti.net>
-Author:              Taru Karttunen
-Category:            Network
-Maintainer:          taruti@taruti.net
-Build-Type:          Simple
-Cabal-version:       >= 1.6
-Homepage:	     http://github.com/taruti/network-fancy
-Source-repository head
-  type:     git
-  location: http://github.com/taruti/network-fancy
+name: network-fancy
+version: 0.2.0
+cabal-version: >=1.6
+build-type: Simple
+license: BSD3
+license-file: COPYING
+copyright: Taru Karttunen <taruti@taruti.net>
+maintainer: taruti@taruti.net
+homepage: http://github.com/taruti/network-fancy
+synopsis: Networking support with a cleaner API
+description: Networking support with a cleaner API
+category: Network
+author: Taru Karttunen
+data-dir: ""
+Extra-Source-Files: changelog.md
 
-Library
-    Build-Depends:       base == 4.*, bytestring
-    Exposed-modules:     Network.Fancy
-    C-Sources:           cbits.c
-    GHC-Options:         -Wall
-    Extensions:          TypeSynonymInstances, ForeignFunctionInterface, CPP, DeriveDataTypeable, FlexibleInstances
-    if os(windows) {
-      CPP-Options:       -DWINDOWS=WINDOWS -DCALLCONV=stdcall -DSAFE_ON_WIN=safe
-      CC-Options:        -DWINDOWS=WINDOWS -DCALLCONV=stdcall -DSAFE_ON_WIN=safe
-      Extra-Libraries:   ws2_32
-    } else {
-      if os(solaris) {
-        Extra-Libraries: socket
-      }
-      CPP-Options:       -DCALLCONV=ccall -DSAFE_ON_WIN=unsafe
-      CC-Options:        -DCALLCONV=ccall -DSAFE_ON_WIN=unsafe
-    }
+source-repository head
+    type: git
+    location: http://github.com/taruti/network-fancy
+
+library
+    build-depends: base ==4.*, bytestring -any
+
+    if os(windows)
+        exposed: True
+        buildable: True
+        cpp-options: -DWINDOWS=WINDOWS -DCALLCONV=stdcall
+                     -DSAFE_ON_WIN=safe
+        cc-options: -DWINDOWS=WINDOWS -DCALLCONV=stdcall -DSAFE_ON_WIN=safe
+        extra-libraries: ws2_32
+    else
+
+        if os(solaris)
+            exposed: True
+            buildable: True
+            extra-libraries: socket
+        exposed: True
+        buildable: True
+        cpp-options: -DCALLCONV=ccall -DSAFE_ON_WIN=unsafe
+        cc-options: -DCALLCONV=ccall -DSAFE_ON_WIN=unsafe
+    exposed-modules: Network.Fancy
+    other-modules: Network.Fancy.Error Network.Fancy.Internal
+    exposed: True
+    buildable: True
+    c-sources: cbits.c
+    extensions: TypeSynonymInstances ForeignFunctionInterface CPP
+                DeriveDataTypeable FlexibleInstances
+    ghc-options: -Wall
+
+
