-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathprotocol.py
431 lines (347 loc) · 14 KB
/
protocol.py
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
#! /usr/bin/env python
#coding=utf-8
import rsa
from time import time
from json import loads, dumps
from base64 import decodestring
import logging
from random import randint
from exception import *
from const import MaxTimeDiff, MaxSearchAddrStep, GetTreeInHours
from node import NeighborNode
from tree import Topic, Article
class PFPMessage( object ):
""
ReplyCode = 0
BackCode = 0
MsgMap = {}
MustHas = { 'Time', 'PubKey' }
LocalNode = None
@classmethod
def Init( cls ):
""
cls.MsgMap.update( { c.code: c for c in cls.__subclasses__() } )
def __new__( cls, code = 0 ):
""
if cls != PFPMessage:
return object.__new__( cls )
SubClass = cls.MsgMap[code]
return object.__new__( SubClass )
def __init__( self, code = 0 ):
""
self.code = code or self.code
def GetBody( self, msgD ):
"this is a received message from remote. decrypt."
BodyStr = self.LocalNode.Decrypt( decodestring( msgD['msg'] ), decodestring( msgD['key'] ))
self.ChkMsgBody( loads( BodyStr ))
return BodyStr
def __getitems__( self, k ):
""
pass
def ChkMsgBody( self, bodyD ):
"for received messages"
if not bodyD.viewkeys() >= self.MustHas:
raise MsgKeyLackErr
for k, v in bodyD.items():
try:
val = getattr( self, '_Check_%s' % k )( v )
if val is not None:
bodyD[k] = val
if k == 'PubKey':
bodyD['PubKeyStr'] = v
except AttributeError:
pass
self.body = bodyD
def Receive( self, msgBody ):
""
VerifyStr = self.GetBody( msgBody )
if VerifyStr: #not QryPubKeyMsg
Neighbor = NeighborNode.Get( self.body['PubKeyStr'], loads( VerifyStr ))
Neighbor.Verify( VerifyStr, decodestring( msgBody['sign'] ))
#self.RcvData( Neighbor, loads( VerifyStr ) )
else:
Neighbor = NeighborNode.Get( self.body['PubKeyStr'], msgBody )
Neighbor.Succeed( self.body['Time'] )
if not self.FlowControl():
return Neighbor
self.RcvData( Neighbor )
if self.ReplyCode > 0:
Neighbor.Buffer( self.Reply( Neighbor ))
if self.BackCode > 0:
Neighbor.Buffer( self.Reply( Neighbor, self.BackCode ))
return Neighbor
def FlowControl( self ):
"record each message for flow control."
return True
def RcvData( self, remote ):
""
pass
def Reply( self, rmtNode, rplCode = None ):
""
RplMsg = PFPMessage( rplCode or self.ReplyCode )
RplMsg.SetRemoteNode( rmtNode )
if RplMsg.InitBody( self ) != False:
return RplMsg.Issue(),
return ()
def Issue( self ):
""
self.body['Time'] = int( time() * 1000 )
self.body['PubKey'] = self.LocalNode.PubKeyStr
#self.body['Address'] = self.LocalNode.Addr
for k, v in self.body.items():
if v is None:
self.body[k] = ''
return chr( self.code ) + self.EncryptBody()
def EncryptBody( self ):
"encrypt and sign"
BodyStr = dumps( self.body )
CryptMsgD = self.RemoteNode.Encrypt( BodyStr )
CryptMsgD['sign'] = self.LocalNode.Sign( BodyStr )
return dumps( CryptMsgD )
def SetRemoteNode( self, rmtNode ):
"send target"
self.RemoteNode = rmtNode
def PostTo( self, *nodes ):
"send to remote node with no response expected"
for rmtNode in nodes:
self.SetRemoteNode( rmtNode )
MsgStr = chr( self.code ) + self.EncryptBody()
rmtNode.Buffer(( MsgStr, ))
NeighborNode.taskQ.put(( 'SendBuffer', rmtNode ))
@staticmethod
def _Check_Time( v ):
""
diff = abs( time() * 1000 - v )
if diff > MaxTimeDiff:
logging.error( '_Check_Time Error %s %s' % ( time() * 1000, v ))
raise RemoteTimeErr
@staticmethod
def _Check_PubKeyType( v ):
""
if v.upper() != 'RSA':
raise PubKeyTypeNotSupportedErr
@staticmethod
def _Check_PubKey( v ):
""
return rsa.PublicKey.load_pkcs1( v )
class QryPubKeyMsg( PFPMessage ):
"""
query the pubkey of the node that only known by addr.
normal steps:
A -----QryPubKeyMsg----> B don't send more info because this message is not encrypted.
A <----NodeInfoMsg------ B A gets B's info includes PubKey
A -----NodeInfoMsg-----> B B gets A's info. they become neighbors.
"""
code = 0x10
ReplyCode = 0x11
def InitBody( self ):
"for send messages"
self.body = {}
def EncryptBody( self ):
"do not encrypt for this message."
return dumps( self.body )
def GetBody( self, msgD ):
"do not verify or decrypt."
self.ChkMsgBody( msgD )
class NodeInfoMsg( PFPMessage ):
"confirm node info. answer for QryPubKeyMsg or SearchAddrMsg or itself"
code = 0x11
ReplyCode = 0x11
Keys = { 'Address', 'NodeName', 'NodeTypeVer', 'PFPVer', 'BaseProtocol', 'Description' }
def InitBody( self, forMsg = None ):
""
if forMsg is None: #to refresh self info to neighbors.
self.body = self.LocalNode.GetInfo()
elif isinstance( forMsg, QryPubKeyMsg ): #--QryPubKeyMsg->, go on.
self.body = self.LocalNode.GetInfo()
self.body['Step'] = 1
elif isinstance( forMsg, NodeInfoMsg ):
if forMsg.body.get( 'Step', 0 ) == 0: #--QryPubKeyMsg->, <-NodeInfoMsg--, --NodeInfoMsg->, Over.
return False
self.body = self.LocalNode.GetInfo() #--QryPubKeyMsg->, <-NodeInfoMsg--, go on.
#self.body['Step'] = 0
elif isinstance( forMsg, SearchAddrMsg ): #--SearchAddrMsg->
#assert forMsg.body["ObjPubKey"] == self.LocalNode.PubKeyStr
self.body = self.LocalNode.GetInfo()
def RcvData( self, remote ):
""
def ChkInnerAddr( addr ):
return True
self.body['Address'] = filter( ChkInnerAddr, self.body['Address'] )
remote.Update( self.body )
class GetNodeMsg( PFPMessage ):
"ask for more neighbor node."
code = 0x12
ReplyCode = 0x13
def InitBody( self ):
""
self.body = {}
class NodeAnswerMsg( PFPMessage ):
""
code = 0x13
def InitBody( self, forMsg = None ):
""
self.body = { 'Nodes': self.RemoteNode.GetSomeInfo() }
def RcvData( self, remote ):
""
for nbData in self.body['Nodes']:
NeighborNode.NewNeighbor( nbData )
class AckMsg( PFPMessage ):
"?"
code = 0x14
class SearchAddrMsg( PFPMessage ):
"""
ask addr for a node with pubkey known.
nomal steps:
A -----SearchAddrMsg---> B
if B is the Obj or B has the Obj in neighbors:
A <----NodeInfoMsg------ B A gets Obj's info
elif Step > 0:
Step--
B -----SearchAddrMsg---> C ask more nodes
if C is the Obj or C has the Obj in neighbors:
A <----NodeInfoMsg------ C A gets Obj's info
elif Step > 0:
......
"""
code = 0x15
ReplyCode = 0x11
def InitBody( self ):
""
self.body = {
"ObjPubKey": self.RemoteNode.PubKeyStr,
#"ObjPubKey": self.LocalNode.PubKeyStr,
"Step": 3,
}
def Receive( self, msgBody ):
"PubKey and Address belongs to the source. ForwardPubKey belongs to the forwarder."
VerifyStr = self.GetBody( msgBody )
body = loads( VerifyStr )
if 'ForwardPubKey' in self.body:
SourceNode = NeighborNode( PubKey = self.body['PubKeyStr'], address = self.body['Address'] ) #temp node. do not save.
ForwardNode = NeighborNode( PubKey = body['ForwardPubKey'] ) #temp node only for verifying
else:
ForwardNode = SourceNode = NeighborNode( PubKey = self.body['PubKeyStr'], address = self.body['Address'] )
ForwardNode.Verify( VerifyStr, decodestring( msgBody['sign'] ))
if not self.FlowControl():
return
if self.LocalNode.PubKeyStr == self.body["ObjPubKey"]: #self is objnode
MsgStrs = PFPMessage.Reply( self, SourceNode )
SourceNode.Buffer( MsgStrs )
return SourceNode
else: #not found
Step = self.body.get( 'Step', 0 )
FwdMsg = PFPMessage( self.code )
FwdMsg.body = {}
FwdMsg.body.update( self.body )
FwdMsg.body['Time'] = int( time() * 1000 )
FwdMsg.body['Step'] = min( Step, MaxSearchAddrStep ) - 1
FwdMsg.body['PubKey'] = FwdMsg.body.pop( 'PubKeyStr' ) #source node's pubK
FwdMsg.body['ForwardPubKey'] = self.LocalNode.PubKeyStr #forward node's pubK
ObjNode = NeighborNode.SearchNode( self.body["ObjPubKey"] )
if ObjNode is not None:
FwdMsg.PostTo( ObjNode )
elif Step > 0:
objPKStr = FwdMsg.body["PubKey"]
FwdMsg.PostTo( *[rmt for rmt in NeighborNode.AllTargets() if rmt.PubKeyStr != objPKStr] )
class NoticeMsg( PFPMessage ):
""
code = 0x16
class QryTreeMsg( PFPMessage ):
"""
ask remote node for trees by query conditions. and tell remote the hot trees to offer.
A -----QryTreeMsg -----> B ( optional )
A <----TreesInfoMsg ---- B
A -----GetTreeMsg -----> B
A <----AtclDataMsg ----- B
"""
code = 0x20
ReplyCode = 0x21
BackCode = 0x20
def InitBody( self, forMsg = None ):
""
#GetTreeInHours = 480
Step = 1 if forMsg is None else ( forMsg.body.get( 'Step', 0 ) - 1 )
if Step < 0:
return False
n, m = divmod( randint( 0, 191 ), 64 )
x = ( 0, m % 8 + 1, m + 9 )[n]
t = int( time() * 1000 ) - x * GetTreeInHours * 3600 * 1000
self.body = {
"From": t - GetTreeInHours * 3600 * 1000,
"To": t,
"Status": 1,
"Step": Step,
}
class TreesInfoMsg( PFPMessage ):
""
code = 0x21
ReplyCode = 0x22
def InitBody( self, forMsg = None ):
""
self.body = {
'Offer': [tpc.GetInfo() for tpc in Topic.Filter( forMsg.body )],
}
#logging.debug( 'TreesInfoMsg: %s' % repr( self.body['Offer'] ))
if not self.body['Offer']:
return False
class GetTreeMsg( PFPMessage ):
""
code = 0x22
ReplyCode = 0x23
def InitBody( self, forMsg = None ):
""
if not forMsg.body.get( 'Offer' ):
return False
self.body = {
'Trees': filter( None, [Topic.Compare( TreeInfo ) for TreeInfo in forMsg.body['Offer']] ),
}
#logging.debug( 'GetTreeMsg: %s' % repr( self.body['Trees'] ))
if not self.body['Trees']:
return False
class AtclDataMsg( PFPMessage ):
""
code = 0x23
def InitBody( self, forMsg = None ):
""
if isinstance( forMsg, GetTreeMsg ):
if not forMsg.body.get( 'Trees' ):
return False
AskBackTrees = [] #for check leaf mode, if the remote leaves are more than local, askback
Atcls = reduce( list.__add__, [Topic.GetReqAtcls( TreeReq, AskBackTrees.append )
for TreeReq in forMsg.body['Trees']] )
self.body = {
'Articles': [atcl.Issue() for atcl in Atcls if atcl.IsPassed()],
}
# if AskBackTrees: do not ask back to avoid request storm
# self.AskBack( AskBackTrees )
elif isinstance( forMsg, GetTimeLineMsg ):
self.body = {
'Articles': [atcl.Issue() for atcl in Article.GetByUser(
*map( forMsg.body.get, ['UserPubKey', 'From', 'To', 'Exist'] )
)],
}
#logging.debug( 'AtclDataMsg: %s' % repr( self.body['Articles'] ))
if not self.body['Articles']:
return False
# def AskBack( self, trees ):
# "send back a GetTreeMsg if remote leaves are more than local"
# BackMsg = GetTreeMsg()
# BackMsg.SetRemoteNode( self.RemoteNode )
# BackMsg.body = { 'Trees': trees }
# BackMsg.RemoteNode.Buffer(( BackMsg.Issue(), ))
def RcvData( self, remote ):
""
Atcls = [Article.Receive( atclData ) for atclData in self.body['Articles']]
Atcls.sort( key = Article.SortType )
pkStr = remote.PubKeyStr
for atcl in Atcls:
if atcl.Check():
atcl.Save( FromNode = pkStr )
class GetTimeLineMsg( PFPMessage ):
""
code = 0x24
ReplyCode = 0x23
def InitBody( self, **body ):
""
self.body = body