packages feed

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 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 ()