network-anonymous-i2p 0.9.2 → 0.10.0
raw patch · 6 files changed
+69/−35 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Network.Anonymous.I2P: defaultEndPoint :: EndPoints
+ Network.Anonymous.I2P.Types.Sam: EndPoints :: EndPoint -> EndPoint -> EndPoints
+ Network.Anonymous.I2P.Types.Sam: data EndPoints
+ Network.Anonymous.I2P.Types.Sam: tcp :: EndPoints -> EndPoint
+ Network.Anonymous.I2P.Types.Sam: type EndPoint = (HostName, PortNumber)
+ Network.Anonymous.I2P.Types.Sam: type HostName = HostName
+ Network.Anonymous.I2P.Types.Sam: type PortNumber = ServiceName
+ Network.Anonymous.I2P.Types.Sam: udp :: EndPoints -> EndPoint
+ Network.Anonymous.I2P.Types.Session: sam :: Context -> EndPoints
- Network.Anonymous.I2P: createDestination :: (MonadIO m, MonadMask m) => Maybe SignatureType -> m (PrivateDestination, PublicDestination)
+ Network.Anonymous.I2P: createDestination :: (MonadIO m, MonadMask m) => EndPoints -> Maybe SignatureType -> m (PrivateDestination, PublicDestination)
- Network.Anonymous.I2P: withSession :: (MonadIO m, MonadMask m) => SocketType -> (Context -> m a) -> m a
+ Network.Anonymous.I2P: withSession :: (MonadIO m, MonadMask m) => EndPoints -> SocketType -> (Context -> m a) -> m a
- Network.Anonymous.I2P: withSession' :: (MonadIO m, MonadMask m) => SocketType -> (PrivateDestination, PublicDestination) -> (Context -> m a) -> m a
+ Network.Anonymous.I2P: withSession' :: (MonadIO m, MonadMask m) => EndPoints -> SocketType -> (PrivateDestination, PublicDestination) -> (Context -> m a) -> m a
- Network.Anonymous.I2P.Protocol: sendDatagram :: (MonadIO m, MonadMask m, Connectable d, Destination d) => String -> d -> ByteString -> m ()
+ Network.Anonymous.I2P.Protocol: sendDatagram :: (MonadIO m, MonadMask m, Connectable d, Destination d) => EndPoint -> String -> d -> ByteString -> m ()
- Network.Anonymous.I2P.Types.Session: Context :: Socket -> SocketType -> String -> PrivateDestination -> PublicDestination -> Context
+ Network.Anonymous.I2P.Types.Session: Context :: EndPoints -> Socket -> SocketType -> String -> PrivateDestination -> PublicDestination -> Context
Files
- network-anonymous-i2p.cabal +2/−1
- src/Network/Anonymous/I2P.hs +39/−29
- src/Network/Anonymous/I2P/Protocol.hs +5/−3
- src/Network/Anonymous/I2P/Types/Sam.hs +19/−0
- src/Network/Anonymous/I2P/Types/Session.hs +2/−0
- test/Network/Anonymous/I2P/ProtocolSpec.hs +2/−2
network-anonymous-i2p.cabal view
@@ -1,6 +1,6 @@ name: network-anonymous-i2p category: Network -version: 0.9.2 +version: 0.10.0 description: Haskell API for I2P anonymous networking license: MIT @@ -41,6 +41,7 @@ exposed-modules: Network.Anonymous.I2P Network.Anonymous.I2P.Error Network.Anonymous.I2P.Types.Destination + Network.Anonymous.I2P.Types.Sam Network.Anonymous.I2P.Types.Session Network.Anonymous.I2P.Types.Socket Network.Anonymous.I2P.Protocol
src/Network/Anonymous/I2P.hs view
@@ -16,7 +16,8 @@ -- $i2p-server -- ** Setting up the context - createDestination + defaultEndPoint + , createDestination , withSession , withSession' @@ -41,6 +42,7 @@ import qualified Network.Anonymous.I2P.Types.Destination as D import qualified Network.Anonymous.I2P.Types.Session as S import qualified Network.Anonymous.I2P.Types.Socket as S +import qualified Network.Anonymous.I2P.Types.Sam as S -------------------------------------------------------------------------------- -- $i2p-introduction @@ -79,14 +81,15 @@ -- Establishing a 'S.VirtualStream' connection with a remote works as follows: -- -- @ --- main = 'withSession' 'S.VirtualStream' withinSession +-- main = 'withSession' 'defaultEndPoint' 'S.VirtualStream' withinSession -- -- where -- dest :: 'D.PublicDestination' -- dest = undefined -- -- withinSession :: 'S.Context' -> IO () --- withinSession ctx = connectStream ctx dest worker +-- withinSession ctx = +-- 'connectStream' ctx dest worker -- -- worker (sock, addr) = do -- -- Now you may use sock to communicate with the remote; addr contains @@ -99,7 +102,7 @@ -- Sending a 'S.DatagramRepliable' message to a remote: -- -- @ --- main = 'withSession' 'S.DatagramRepliable' withinSession +-- main = 'withSession' 'defaultEndPoint' 'S.DatagramRepliable' withinSession -- -- where -- dest :: 'D.PublicDestination' @@ -107,7 +110,7 @@ -- -- withinSession :: 'S.Context' -> IO () -- withinSession ctx = do --- sendDatagram ctx dest \"Hello, anonymous world!\" +-- 'sendDatagram' ctx dest \"Hello, anonymous world!\" -- -- -- SAM requires the master connection of a session to be alive longer -- -- than any opertions that occur on the session are. Since sending a @@ -123,11 +126,12 @@ -- Running a server that accepts 'S.VirtualStream' connections. -- -- @ --- main = 'withSession' 'S.VirtualStream' withinSession +-- main = 'withSession' 'defaultEndPoint' 'S.VirtualStream' withinSession -- -- where -- withinSession :: 'S.Context' -> IO () --- withinSession ctx = serveStream ctx worker +-- withinSession ctx = +-- 'serveStream' ctx worker -- -- worker (sock, addr) = do -- -- Now you may use sock to communicate with the remote; addr contains @@ -140,12 +144,12 @@ -- Receiving 'S.DatagramAnonymous' messages from remotes: -- -- @ --- main = 'withSession' 'S.DatagramAnonymous' withinSession +-- main = 'withSession' 'defaultEndPoint' 'S.DatagramAnonymous' withinSession -- -- where -- withinSession :: 'S.Context' -> IO () -- withinSession ctx = --- serveDatagram ctx worker +-- 'serveDatagram' ctx worker -- -- worker (sock, addr) = do -- -- Now you may use sock to communicate with the remote; addr is an @@ -155,6 +159,10 @@ -- @ -------------------------------------------------------------------------------- +-- | The default host/port SAM uses +defaultEndPoint :: S.EndPoints +defaultEndPoint = S.EndPoints ("127.0.0.1", "7656") ("127.0.0.1", "7655") + -- | Create a new I2P destination endpoint. -- -- All communication in I2P starts with having our own host endpoint @@ -169,13 +177,13 @@ -- the address other people can connect to. createDestination :: ( MonadIO m , MonadMask m) - => Maybe D.SignatureType -- ^ Algorithm to use for signature encryption. As per I2P spec defaults to DSA_SHA1. + => S.EndPoints -- ^ Our SAM bridge endpoints + -> Maybe D.SignatureType -- ^ Algorithm to use for signature encryption. As per I2P spec defaults to DSA_SHA1. -> m (D.PrivateDestination, D.PublicDestination) -- ^ The private and public destinations. -createDestination signatureType = - P.connect "127.0.0.1" "7656" (\(sock, _) -> do - _ <- P.version sock - P.createDestination signatureType sock) - +createDestination (S.EndPoints (tcpHost, tcpPort) _) signatureType = + P.connect tcpHost tcpPort (\(sock, _) -> do + _ <- P.version sock + P.createDestination signatureType sock) -- | Starts a new I2P session. A connection with the SAM bridge will be -- established, and a 'S.Context' object will be created and passed to the @@ -185,12 +193,13 @@ -- properly closed. withSession :: ( MonadIO m , MonadMask m) - => S.SocketType -- ^ The type of socket we will be using. + => S.EndPoints -- ^ Our SAM bridge endpoints + -> S.SocketType -- ^ The type of socket we will be using. -> (S.Context -> m a) -- ^ The computation to run -> m a -withSession socketType callback = do - destPair <- createDestination Nothing - withSession' socketType destPair callback +withSession sam socketType callback = do + destPair <- createDestination sam Nothing + withSession' sam socketType destPair callback -- | Alternative implementation of 'withSession' that explicitly accepts a -- 'D.Destination' pair to use to set up the session. This can be useful @@ -198,18 +207,19 @@ -- endpoint. withSession' :: ( MonadIO m , MonadMask m) - => S.SocketType -- ^ The type of socket we will be using. + => S.EndPoints -- ^ Our SAM bridge endpoints + -> S.SocketType -- ^ The type of socket we will be using. -> (D.PrivateDestination, D.PublicDestination) -- ^ Destination to use -> (S.Context -> m a) -- ^ The computation to run -> m a -withSession' socketType (privDest, pubDest) callback = +withSession' sam socketType (privDest, pubDest) callback = let bindSession (sock, _) = do _ <- P.version sock sessionId <- P.createSessionWith Nothing privDest socketType sock - callback (S.Context sock socketType sessionId privDest pubDest) + callback (S.Context sam sock socketType sessionId privDest pubDest) - in P.connect "127.0.0.1" "7656" bindSession + in uncurry P.connect (S.tcp sam) bindSession -- | Starts a server to accept 'S.VirtualStream' connections from other hosts -- and handles them concurrently in different threads. Any acquired resources @@ -219,7 +229,7 @@ => S.Context -> ((Network.Socket, D.PublicDestination) -> IO ()) -> m () -serveStream (S.Context _ _ sessionId _ _) callback = +serveStream (S.Context (S.EndPoints (tcpHost, tcpPort) _) _ _ sessionId _ _) callback = let acceptNext accepted' (sock, _) = do _ <- P.version sock (incomingSock, incomingDest) <- P.acceptStream sessionId sock @@ -228,7 +238,7 @@ acceptFork = liftIO $ do accepted' <- newEmptyMVar - _ <- forkIO $ P.connect "127.0.0.1" "7656" (acceptNext accepted') + _ <- forkIO $ P.connect tcpHost tcpPort (acceptNext accepted') -- This blocks until an actual connection is accepted, so we ensure there -- is always just one thread waiting for a new connection. @@ -245,7 +255,7 @@ => S.Context -> ((BS.ByteString, Maybe D.PublicDestination) -> IO ()) -> m () -serveDatagram (S.Context sock _ _ _ _) callback = +serveDatagram (S.Context _ sock _ _ _ _) callback = let receiveNext received' = do res <- P.receiveDatagram sock putMVar received' True @@ -271,7 +281,7 @@ -> D.PublicDestination -- ^ Destination to connect to. -> ((Network.Socket, D.PublicDestination) -> IO ()) -- ^ Computation to run once connection has been established. -> m () -connectStream (S.Context _ _ sessionId _ _) remoteDestination callback = +connectStream (S.Context (S.EndPoints (tcpHost, tcpPort) _) _ _ sessionId _ _) remoteDestination callback = let connectAddress (sock, _) = do -- Connect to the remote destination within our session. _ <- P.version sock @@ -281,7 +291,7 @@ -- the 'localDestination' and the 'remoteDestination' liftIO $ callback (sock, remoteDestination) - in P.connect "127.0.0.1" "7656" connectAddress + in P.connect tcpHost tcpPort connectAddress -- | Sends a datagram to a remote destination. -- @@ -296,4 +306,4 @@ -> D.PublicDestination -- ^ Destination to send message to -> BS.ByteString -- ^ The message to send -> m () -sendDatagram (S.Context _ _ sessionId _ _) = P.sendDatagram sessionId +sendDatagram (S.Context (S.EndPoints _ udp) _ _ sessionId _ _) = P.sendDatagram udp sessionId
src/Network/Anonymous/I2P/Protocol.hs view
@@ -46,6 +46,7 @@ import qualified Network.Anonymous.I2P.Protocol.Parser.Ast as Ast import qualified Network.Anonymous.I2P.Types.Destination as D import qualified Network.Anonymous.I2P.Types.Socket as S +import qualified Network.Anonymous.I2P.Types.Sam as S -- | According to the I2P protocol, the first two tokens in a response are always @@ -284,11 +285,12 @@ , MonadMask m , D.Connectable d , D.Destination d) - => String -- ^ Our session id + => S.EndPoint -- ^ SAM UDP endpoint + -> String -- ^ Our session id -> d -- ^ Destination we wish to send message to -> BS.ByteString -- ^ Message we wish to send -> m () -- ^ Returning state -sendDatagram sessionId destination message +sendDatagram (udpHost, udpPort) sessionId destination message | BS.length message > maxLength = E.i2pError (E.mkI2PError E.messageTooLongErrorType) | otherwise = let sendString = @@ -300,7 +302,7 @@ in do -- Establish connection to UDP SAM service at port 7655 - addrinfos <- liftIO $ Network.getAddrInfo Nothing (Just "127.0.0.1") (Just "7655") + addrinfos <- liftIO $ Network.getAddrInfo Nothing (Just udpHost) (Just udpPort) let serveraddr = head addrinfos sock <- liftIO $ Network.socket (Network.addrFamily serveraddr) Network.Datagram Network.defaultProtocol liftIO $ Network.connect sock (Network.addrAddress serveraddr)
+ src/Network/Anonymous/I2P/Types/Sam.hs view
@@ -0,0 +1,19 @@+-- | SAM endpoint types +module Network.Anonymous.I2P.Types.Sam where + +import qualified Network.Socket as NS + +-- | Alias for a hostname +type HostName = NS.HostName + +-- | Alias for a port number +type PortNumber = NS.ServiceName + +-- | A SAM endpoint +type EndPoint = (HostName, PortNumber) + +-- | Describes all endpoints required by SAM +data EndPoints = EndPoints { + tcp :: EndPoint, + udp :: EndPoint + }
src/Network/Anonymous/I2P/Types/Session.hs view
@@ -3,11 +3,13 @@ import qualified Network.Anonymous.I2P.Types.Destination as D import qualified Network.Anonymous.I2P.Types.Socket as S +import qualified Network.Anonymous.I2P.Types.Sam as S import qualified Network.Socket as NS -- | Context object that is required for all functions that operate on top -- of the SAM bridge. data Context = Context { + sam :: S.EndPoints, -- ^ Endpoints to our SAM bridge conn :: NS.Socket, -- ^ Our connection with the SAM bridge socketType :: S.SocketType, -- ^ The type of connection we are managing sessionId :: String, -- ^ Our session id
test/Network/Anonymous/I2P/ProtocolSpec.hs view
@@ -417,7 +417,7 @@ sinkSock <- takeMVar sinkSock' -- Put a message on top of our source socket - P.sendDatagram sourceSessionId sinkDestination (BS8.pack "Hello, world!") + P.sendDatagram ("127.0.0.1", "7655") sourceSessionId sinkDestination (BS8.pack "Hello, world!") putStrLn "Now attempting to receive a datagram in the sink" @@ -466,7 +466,7 @@ sourceSessionId <- takeMVar sourceSessionId' -- Put a message on top of our source socket - P.sendDatagram sourceSessionId sinkDestination bigMessage `shouldThrow` U.isI2PError E.messageTooLongErrorType + P.sendDatagram ("127.0.0.1", "7655") sourceSessionId sinkDestination bigMessage `shouldThrow` U.isI2PError E.messageTooLongErrorType return ()