Skip to content

Commit 3838ec7

Browse files
karknukazu-yamamoto
authored andcommitted
Implement total Show functions for SockAddr
The previous implementation of show used unsafePerformIO and getNameInfo. getNameInfo depends on show SockAddr incase of a failure. The showHostAddress and showHostAddress6 are based on showIPv4 and showIPv6 in the iproute package.
1 parent fcc2d86 commit 3838ec7

File tree

1 file changed

+42
-9
lines changed

1 file changed

+42
-9
lines changed

Network/Socket/Info.hsc

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ module Network.Socket.Info where
1010

1111
import Foreign.Marshal.Alloc (alloca, allocaBytes)
1212
import Foreign.Marshal.Utils (maybeWith, with)
13-
import GHC.IO (unsafePerformIO)
1413
import GHC.IO.Exception (IOErrorType(NoSuchThing))
1514
import System.IO.Error (ioeSetErrorString, mkIOError)
1615

@@ -441,16 +440,50 @@ instance Show SockAddr where
441440
#else
442441
showsPrec _ SockAddrUnix{} = error "showsPrec: not supported"
443442
#endif
444-
showsPrec _ addr@(SockAddrInet port _)
445-
= showString (unsafePerformIO $
446-
fst <$> getNameInfo [NI_NUMERICHOST] True False addr >>=
447-
maybe (fail "showsPrec: impossible internal error") return)
443+
showsPrec _ (SockAddrInet port ha)
444+
= showHostAddress ha
448445
. showString ":"
449446
. shows port
450-
showsPrec _ addr@(SockAddrInet6 port _ _ _)
447+
showsPrec _ (SockAddrInet6 port _ ha6 _)
451448
= showChar '['
452-
. showString (unsafePerformIO $
453-
fst <$> getNameInfo [NI_NUMERICHOST] True False addr >>=
454-
maybe (fail "showsPrec: impossible internal error") return)
449+
. showHostAddress6 ha6
455450
. showString "]:"
456451
. shows port
452+
453+
454+
-- Taken from on the implementation of showIPv4 in Data.IP.Addr
455+
showHostAddress :: HostAddress -> ShowS
456+
showHostAddress ip =
457+
let (u3, u2, u1, u0) = hostAddressToTuple ip in
458+
foldr1 (.) . intersperse (showChar '.') $ map showInt [u3, u2, u1, u0]
459+
460+
-- Taken from showIPv6 in Data.IP.Addr.
461+
462+
-- | Show an IPv6 address in the most appropriate notation, based on recommended
463+
-- representation proposed by <http://tools.ietf.org/html/rfc5952 RFC 5952>.
464+
--
465+
-- /The implementation is completely compatible with the current implementation
466+
-- of the `inet_ntop` function in glibc./
467+
showHostAddress6 :: HostAddress6 -> ShowS
468+
showHostAddress6 ha6@(a1, a2, a3, a4)
469+
-- IPv4-Mapped IPv6 Address
470+
| a1 == 0 && a2 == 0 && a3 == 0xffff =
471+
showString "::ffff:" . showHostAddress a4
472+
-- IPv4-Compatible IPv6 Address (exclude IPRange ::/112)
473+
| a1 == 0 && a2 == 0 && a3 == 0 && a4 >= 0x10000 =
474+
showString "::" . showHostAddress a4
475+
-- length of longest run > 1, replace it with "::"
476+
| end - begin > 1 =
477+
showFields prefix . showString "::" . showFields suffix
478+
| otherwise =
479+
showFields fields
480+
where
481+
fields =
482+
let (u7, u6, u5, u4, u3, u2, u1, u0) = hostAddress6ToTuple ha6 in
483+
[u7, u6, u5, u4, u3, u2, u1, u0]
484+
showFields = foldr (.) id . intersperse (showChar ':') . map showHex
485+
prefix = take begin fields -- fields before "::"
486+
suffix = drop end fields -- fields after "::"
487+
begin = end + diff -- the longest run of zeros
488+
(diff, end) = minimum $
489+
scanl (\c i -> if i == 0 then c - 1 else 0) 0 fields `zip` [0..]

0 commit comments

Comments
 (0)