Skip to content

Commit 7040ac3

Browse files
committed
Merge PR #441
2 parents 08a7642 + 3838ec7 commit 7040ac3

File tree

2 files changed

+92
-9
lines changed

2 files changed

+92
-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..]

tests/Network/SocketSpec.hs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,53 @@ spec = do
219219
s <- mkSocket fd
220220
sendAll s "HELLO WORLD"
221221
tcpTest client server
222+
223+
describe "getNameInfo" $ do
224+
it "works for IPv4 address" $ do
225+
let addr = SockAddrInet 80 (tupleToHostAddress (127, 0, 0, 1))
226+
(hn_m, sn_m) <- getNameInfo [NI_NUMERICHOST, NI_NUMERICSERV] True True addr
227+
228+
hn_m `shouldBe` (Just "127.0.0.1")
229+
sn_m `shouldBe` (Just "80")
230+
231+
it "works for IPv6 address" $ do
232+
let addr = SockAddrInet6 80 0
233+
(tupleToHostAddress6 (0x2001, 0x0db8, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7)) 0
234+
(hn_m, sn_m) <- getNameInfo [NI_NUMERICHOST, NI_NUMERICSERV] True True addr
235+
hn_m `shouldBe`(Just "2001:db8:2:3:4:5:6:7")
236+
sn_m `shouldBe` (Just "80")
237+
238+
it "works for IPv6 address" $ do
239+
let addr = SockAddrInet6 80 0
240+
(tupleToHostAddress6 (0x2001, 0x0db8, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7)) 0
241+
(hn_m, sn_m) <- getNameInfo [NI_NUMERICHOST, NI_NUMERICSERV] True True addr
242+
hn_m `shouldBe`(Just "2001:db8:2:3:4:5:6:7")
243+
sn_m `shouldBe` (Just "80")
244+
245+
it "works for global multicast IPv6 address" $ do
246+
let addr = SockAddrInet6 80 0
247+
(tupleToHostAddress6 (0xfe01, 0x0db8, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7)) 0
248+
(hn_m, sn_m) <- getNameInfo [NI_NUMERICHOST, NI_NUMERICSERV] True True addr
249+
hn_m `shouldBe`(Just "fe01:db8:2:3:4:5:6:7")
250+
sn_m `shouldBe` (Just "80")
251+
252+
describe "show SocketAddr" $ do
253+
it "works for IPv4 address" $
254+
let addr = SockAddrInet 80 (tupleToHostAddress (127, 0, 0, 1)) in
255+
show addr `shouldBe` "127.0.0.1:80"
256+
257+
it "works for IPv6 address" $
258+
let addr = SockAddrInet6 80 0
259+
(tupleToHostAddress6 (0x2001, 0x0db8, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7)) 0 in
260+
show addr `shouldBe` "[2001:db8:2:3:4:5:6:7]:80"
261+
262+
it "works for IPv6 address with zeros" $
263+
let addr = SockAddrInet6 80 0
264+
(tupleToHostAddress6 (0x2001, 0x0db8, 0x2, 0x3, 0x0, 0x0, 0x0, 0x7)) 0 in
265+
show addr `shouldBe` "[2001:db8:2:3::7]:80"
266+
267+
it "works for multicast IPv6 address with reserved scope" $ do
268+
let addr = SockAddrInet6 80 0
269+
(tupleToHostAddress6 (0xff01, 0x1234, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7)) 0
270+
show addr `shouldBe` "[ff01:1234:2:3:4:5:6:7]:80"
271+

0 commit comments

Comments
 (0)