summaryrefslogtreecommitdiff
path: root/src/Network/DHT/Tox.hs
blob: d6fc866f5bad09003dc43eaecf25a63e955e3a34 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
module Network.DHT.Tox where

import Data.Serialize
import Data.Default
import Text.PrettyPrint.HughesPJClass

import Network.DHT.Types
import Network.DatagramServer.Types
import qualified Network.DatagramServer.Tox as Tox
import Network.KRPC.Method
import Data.Word
import Data.ByteString (ByteString)
import Data.IP
import Data.Bool
import Data.Maybe
import Control.Monad
import System.Random

instance Kademlia Tox.Message where
    data DHTData Tox.Message ip = ToxData
    namePing      _   = Tox.Ping
    nameFindNodes _   = Tox.GetNodes
    initializeDHTData = return ToxData

instance Pretty (NodeId Tox.Message) where
    pPrint (Tox.NodeId nid) = encodeHexDoc nid

instance Serialize (Query Tox.Message (Ping Tox.Message)) where
    get = getToxPing False Network.DHT.Types.Query Tox.QueryNonce
    put (Network.DHT.Types.Query extra Ping) = putToxPing False (Tox.qryNonce extra)
instance Serialize (Response Tox.Message (Ping Tox.Message)) where
    get = getToxPing True Network.DHT.Types.Response Tox.ResponseNonce
    put (Network.DHT.Types.Response extra Ping) = putToxPing True (Tox.rspNonce extra)

instance Serialize (Query Tox.Message (FindNode Tox.Message ip)) where
    get = do
        nid <- get
        n8 <- get
        return $ Network.DHT.Types.Query (Tox.QueryNonce n8) (FindNode nid)
    put (Network.DHT.Types.Query (Tox.QueryNonce n8) (FindNode nid)) = do
        put nid
        put n8
instance Serialize (Response Tox.Message (NodeFound Tox.Message IPv4)) where
    get = do
        num <- get :: Get Word8
        when (num > 4) $ fail "Too many nodes in Tox get-nodes reply"
        ns0 <- sequence $ replicate (fromIntegral num) (nodeFormatToNodeInfo <$> get)
        -- TODO: Allow tcp and ipv6. For now filtering to udp ip4...
        let ns = flip mapMaybe ns0 $ \(NodeInfo nid addr u) -> do
                    guard $ not u
                    ip4 <- fromAddr addr
                    return $ NodeInfo nid ip4 ()
        n8 <- get
        return $ Network.DHT.Types.Response (Tox.ResponseNonce n8) $ NodeFound ns
    put (Network.DHT.Types.Response (Tox.ResponseNonce n8) (NodeFound ns)) = do
        put ( fromIntegral (length ns) :: Word8 )
        forM_ ns $ \(NodeInfo nid ip4 ()) -> do
            put Tox.NodeFormat { nodePublicKey = nid
                               , nodeIsTCP     = False
                               , nodeIP        = IPv4 (nodeHost ip4)
                               , nodePort      = nodePort ip4
                               }
        put n8

instance KRPC Tox.Message (Query    Tox.Message (FindNode  Tox.Message IPv4))
                          (Response Tox.Message (NodeFound Tox.Message IPv4)) where
    method = Method Tox.GetNodes
    validateExchange = validateToxExchange
    makeQueryExtra _ _ _ _ = Tox.QueryNonce <$> randomIO
    makeResponseExtra _ _ q _ = return $ Tox.ResponseNonce $ Tox.qryNonce $ queryExtra q
    messageSender q _ = Tox.msgClient q
    messageResponder _ r = Tox.msgClient r

instance KRPC Tox.Message (Query    Tox.Message (Ping Tox.Message))
                          (Response Tox.Message (Ping Tox.Message)) where
    method = Method Tox.Ping
    validateExchange = validateToxExchange
    makeQueryExtra _ _ _ _ = Tox.QueryNonce <$> randomIO
    makeResponseExtra _ _ q _ = return $ Tox.ResponseNonce $ Tox.qryNonce $ queryExtra q
    messageSender q _ = Tox.msgClient q
    messageResponder _ r = Tox.msgClient r

instance DataHandlers ByteString Tox.Message

instance Default Bool where def = False

getToxPing isPong c n = do
    q'r <- get :: Get Word8
    when (bool 0 1 isPong /= q'r) $
        fail "Tox ping/pong parse fail."
    n8 <- get :: Get Tox.Nonce8
    return $ c (n n8) Ping

putToxPing isPong n8 = do
    put (bool 0 1 isPong :: Word8)
    put n8

validateToxExchange q r = qnonce == rnonce
      where
        qnonce = Tox.qryNonce . queryExtra    . Tox.msgPayload $ q
        rnonce = Tox.rspNonce . responseExtra . Tox.msgPayload $ r


nodeFormatToNodeInfo nf = NodeInfo nid addr u
 where
    u    = Tox.nodeIsTCP nf
    addr = NodeAddr (Tox.nodeIP nf) (Tox.nodePort nf)
    nid  = Tox.nodePublicKey nf

-- instance Default Bool where def = False