1# -*- coding: utf-8 -*-
2'''
3raeting module provides constants and values for the RAET protocol
4
5Production Ports for Raet
6Master 4505
7Minion(s) 4510
8
9Packet Data Format.
10The data used to initialize a packet is an ordered dict with several fields
11most of the fields are shared with the header data format below so only the
12unique fields are shown here.
13
14Unique Packet data fields
15
16    sh: source host ip address (ipv4) Default: ''
17    sp: source ip port                Default: 7532
18    dh: destination host ip address (ipv4) Default: '127.0.0.1'
19    dp: destination host ip port           Default 7532
20
21Header Data Format.
22The .data in the packet header is an ordered dict  which is used to either
23create a packet to transmit
24or holds the field from a received packet.
25What fields are included in a header is dependent on the header kind.
26
27Header encoding.
28    When the head kind is json = 0, then certain optimizations are
29    used to minimize the header length.
30        The header field keys are two bytes long
31        If a header field value is the default then the field is not included
32        Lengths are encoded as hex strings
33        The flags are encoded as a double char hex string in field 'fg'
34
35header data =
36{
37    ri: raet id Default 'RAET'
38    vn: Version (Version) Default 0
39    pk: Packet Kind (PcktKind)
40    pl: Packet Length (PcktLen)
41    hk: Header kind   (HeadKind) Default 0
42    hl: Header length (HeadLen) Default 0
43
44    se: Source Estate ID (SEID)
45    de: Destination Estate ID (DEID)
46    cf: Correspondent Flag (CrdtFlag) Default 0
47    bf: BroadCast Flag (BcstFlag)  Default 0
48    nf: NAT Flag (NatFlag) Default 0
49    df: Dynamic IP Flag (DynFlag) Default 0
50    vf: IPv6 Flag (IP) (Ipv6Flag) Default 0
51
52    si: Session ID (SID) Default 0
53    ti: Transaction ID (TID) Default 0
54    tk: Transaction Kind (TrnsKind)
55
56    dt: Datetime Stamp  (Datetime) Default 0
57    oi: Order index (OrdrIndx)   Default 0
58
59    wf: Waiting Ack Flag    (WaitFlag) Default 0
60        Next segment or ordered packet is waiting for ack to this packet
61    ml: Message Length (MsgLen)  Default 0
62        Length of message only (unsegmented)
63    sn: Segment Number (SgmtNum) Default 0
64    sc: Segment Count  (SgmtCnt) Default 1
65    sf: Segment Flag  (SgmtFlag) Default 0
66        This packet is part of a segmented message
67    af: Again Flag (AgnFlag) Default 0
68        This segment is being sent again
69
70    bk: Body kind   (BodyKind) Default 0
71    ck: Coat kind   (CoatKind) Default 0
72    fk: Footer kind   (FootKind) Default 0
73    fl: Footer length (FootLen) Default 0
74
75    fg: flags  packed (Flags) Default '00' hs
76         2 char Hex string with bits (vf, df, nf, af, sf, wf, bf, cf)
77         Zeros are TBD flags
78}
79
80Body Data Format
81The Body .data is a Mapping
82
83Body Encoding
84    When the body kind is json = 0, then the .data is json encoded
85
86Body Decoding
87
88
89'''
90
91# pylint: disable=C0103
92
93# Import python libs
94import struct
95import enum
96
97# Import ioflo libs
98from ioflo.aid.odicting import odict
99
100# Import raet libs
101# pylint: disable=wildcard-import,unused-wildcard-import,redefined-builtin
102from .abiding import *  # import globals
103# pylint: enable=wildcard-import,unused-wildcard-import,redefined-builtin
104
105# Used to comput session id wrap around where valid sid is >= modulo N given by
106# (((new - old) % 0x100000000) < (0x100000000 // 2))
107# N//2 = 0x80000000
108SID_WRAP_MODULO = 0x100000000  # session id wraps modulo N = 2^32 = 0x100000000
109SID_WRAP_DELTA = 0x80000000    # session id >= delta at N//2 = 0x80000000
110SID_ROLLOVER = 0xffffffff      # session id rolls over at modulo (N-1) -= 2^32 -1 = 0xffffffff
111
112RAET_PORT = 7530
113RAET_TEST_PORT = 7531
114DEFAULT_SRC_HOST = ''
115DEFAULT_DST_HOST = '127.0.0.1'
116
117UDP_MAX_DATAGRAM_SIZE = (2 ** 16) - 1  # 65535
118UDP_MAX_SAFE_PAYLOAD = 548  # IPV4 MTU 576 - udp headers 28
119# IPV6 MTU is 1280 but headers are bigger
120UDP_MAX_PACKET_SIZE = min(1024, UDP_MAX_DATAGRAM_SIZE)  # assumes IPV6 capable equipment
121UXD_MAX_PACKET_SIZE = (2 ** 16) - 1  # 65535
122MAX_SEGMENT_COUNT = (2 ** 16) - 1  # 65535
123MAX_MESSAGE_SIZE = min(67107840, UDP_MAX_PACKET_SIZE * MAX_SEGMENT_COUNT)
124MAX_HEAD_SIZE = 255
125
126JSON_END = b'\r\n\r\n'
127HEAD_END = b'\n\n'
128
129VERSIONS = odict([('0.1', 0)])
130VERSION_NAMES = odict((v, k) for k, v in VERSIONS.iteritems())
131VERSION = VERSIONS.values()[0]
132
133HELLO_PACKER = struct.Struct('!64s32s80s24s')  # curvecp allow trans bodies
134COOKIESTUFF_PACKER = struct.Struct('!32sLL24s')
135COOKIE_PACKER = struct.Struct('!80s24s')
136INITIATESTUFF_PACKER = struct.Struct('!32s48s24s128s')
137INITIATE_PACKER = struct.Struct('!32s24s248s24s')
138
139
140def get_exception_error(ex):
141    '''
142    Return the error code from an exception
143    '''
144    if hasattr(ex, 'errno'):
145        return ex.errno
146    elif hasattr(ex, 'winerror'):
147        return ex.winerror
148    else:
149        emsg = "Cannot find error code in exception: {0}".format(ex)
150        raise TypeError(emsg)
151
152
153@enum.unique
154class HeadKind(enum.IntEnum):
155    '''
156    Integer Enums of Head Kinds
157    '''
158    raet = 0
159    json = 1
160    binary = 2
161    unknown = 255
162
163
164@enum.unique
165class BodyKind(enum.IntEnum):
166    '''
167    Integer Enums of Body Kinds
168    '''
169    nada = 0
170    json = 1
171    raw = 2
172    msgpack = 3
173    unknown = 255
174
175
176@enum.unique
177class FootKind(enum.IntEnum):
178    '''
179    Integer Enums of Foot Kinds
180    '''
181    nada = 0
182    nacl = 1
183    sha2 = 2
184    crc64 = 3
185    unknown = 255
186
187
188class FootSize(enum.IntEnum):
189    '''
190    Integer Enums of Foot Sizes in bytes
191    '''
192    nada = 0
193    nacl = 64
194    sha2 = 256
195    crc64 = 8
196    unknown = 0
197
198
199@enum.unique
200class CoatKind(enum.IntEnum):
201    '''
202    Integer Enums of Coat Kinds
203    '''
204    nada = 0
205    nacl = 1
206    crc16 = 2
207    crc64 = 3
208    unknown = 255
209
210
211class TailSize(enum.IntEnum):
212    '''
213    Integer Enums of Tail Sizes in bytes
214    '''
215    nada = 0
216    nacl = 24
217    crc16 = 2
218    crc64 = 8
219    unknown = 0
220
221
222@enum.unique
223class TrnsKind(enum.IntEnum):
224    '''
225    Integer Enums of Transaction Kinds
226    '''
227    message = 0
228    join = 1
229    bind = 2
230    allow = 3
231    alive = 4
232    unknown = 255
233
234
235@enum.unique
236class PcktKind(enum.IntEnum):
237    '''
238    Integer Enums of Packet Kinds
239    '''
240    message = 0
241    ack = 1
242    nack = 2
243    resend = 3
244    request = 4
245    response = 5
246    hello = 6
247    cookie = 7
248    initiate = 8
249    unjoined = 9
250    unallowed = 10
251    renew = 11
252    refuse = 12
253    reject = 13
254    pend = 14
255    done = 15
256    unknown = 255
257
258
259@enum.unique
260class Acceptance(enum.IntEnum):
261    '''
262    Integer Enums of Acceptances
263    '''
264    pending = 0
265    accepted = 1
266    rejected = 2
267
268
269@enum.unique
270class AutoMode(enum.IntEnum):
271    '''
272    Integer Enums of Auto Modes
273    '''
274    never = 0
275    once = 1
276    always = 2
277
278
279@enum.unique
280class PackKind(enum.IntEnum):
281    '''
282    Integer Enums of Pack Kinds for Lane Pages
283    '''
284    json = 0
285    pack = 1
286
287
288# head fields that may be included in packet header if not default value
289PACKET_DEFAULTS = odict([
290                            ('sh', DEFAULT_SRC_HOST),
291                            ('sp', RAET_PORT),
292                            ('dh', DEFAULT_DST_HOST),
293                            ('dp', RAET_PORT),
294                            ('ri', 'RAET'),
295                            ('vn', 0),
296                            ('pk', 0),
297                            ('pl', 0),
298                            ('hk', 0),
299                            ('hl', 0),
300                            ('se', 0),
301                            ('de', 0),
302                            ('cf', False),
303                            ('bf', False),
304                            ('nf', False),
305                            ('df', False),
306                            ('vf', False),
307                            ('si', 0),
308                            ('ti', 0),
309                            ('tk', 0),
310                            ('dt', 0),
311                            ('oi', 0),
312                            ('wf', False),
313                            ('sn', 0),
314                            ('sc', 1),
315                            ('ml', 0),
316                            ('sf', False),
317                            ('af', False),
318                            ('bk', 0),
319                            ('ck', 0),
320                            ('fk', 0),
321                            ('fl', 0),
322                            ('fg', '00'),
323                      ])
324
325PACKET_FIELDS = ['sh', 'sp', 'dh', 'dp',
326                 'ri', 'vn', 'pk', 'pl', 'hk', 'hl',
327                 'se', 'de', 'cf', 'bf', 'nf', 'df', 'vf', 'si', 'ti', 'tk',
328                 'dt', 'oi', 'wf', 'sn', 'sc', 'ml', 'sf', 'af',
329                 'bk', 'ck', 'fk', 'fl', 'fg']
330
331PACKET_HEAD_FIELDS = ['ri', 'vn', 'pk', 'pl', 'hk', 'hl',
332               'se', 'de', 'cf', 'bf', 'nf', 'df', 'vf', 'si', 'ti', 'tk',
333               'dt', 'oi', 'wf', 'sn', 'sc', 'ml', 'sf', 'af',
334               'bk', 'bl', 'ck', 'cl', 'fk', 'fl', 'fg']
335
336PACKET_FLAGS = ['vf', 'df', 'nf', 'af', 'sf', 'wf', 'bf', 'cf']
337PACKET_FLAG_FIELDS = ['vf', 'df', 'nf', 'af', 'sf', 'wf', 'bf', 'cf']
338
339PACKET_FIELD_FORMATS = odict([
340                    ('ri', '.4s'),
341                    ('vn', 'x'),
342                    ('pk', 'x'),
343                    ('pl', '04x'),
344                    ('hk', 'x'),
345                    ('hl', '02x'),
346                    ('se', 'x'),
347                    ('de', 'x'),
348                    ('cf', ''),
349                    ('bf', ''),
350                    ('nf', ''),
351                    ('df', ''),
352                    ('vf', ''),
353                    ('si', 'x'),
354                    ('ti', 'x'),
355                    ('tk', 'x'),
356                    ('dt', 'f'),
357                    ('oi', 'x'),
358                    ('wf', ''),
359                    ('sn', 'x'),
360                    ('sc', 'x'),
361                    ('ml', 'x'),
362                    ('sf', ''),
363                    ('af', ''),
364                    ('bk', 'x'),
365                    ('ck', 'x'),
366                    ('fk', 'x'),
367                    ('fl', 'x'),
368                    ('fg', '.2s'),
369              ])
370
371# head fields that may be included in page header if not default value
372PAGE_DEFAULTS = odict([
373                        ('ri', 'RAET'),
374                        ('vn', 0),
375                        ('pk', 0),
376                        ('sn', ''),
377                        ('dn', ''),
378                        ('si', '000000000000000000'),
379                        ('bi', 0),
380                        ('pn', 0),
381                        ('pc', 1),
382                      ])
383
384PAGE_FIELD_FORMATS = odict([
385                            ('ri', '.4s'),
386                            ('vn', 'x'),
387                            ('pk', 'x'),
388                            ('sn', 's'),
389                            ('dn', 's'),
390                            ('si', '.18s'),
391                            ('bi', 'x'),
392                            ('pn', '04x'),
393                            ('pc', '04x'),
394                           ])
395
396PAGE_FIELDS = ['ri', 'vn', 'pk', 'sn', 'dn', 'si', 'bi', 'pn', 'pc']
397
398
399class RaetError(Exception):
400    '''
401    Exceptions in RAET Protocol processing
402
403       usage:
404           emsg = "Invalid unique id '{0}'".format(uid)
405           raise raeting.RaetError(emsg)
406    '''
407    def __init__(self, message=None):
408        self.message = message  # description of error
409        super(RaetError, self).__init__(message)
410
411    def __str__(self):
412        return "{0}: {1}.\n".format(self.__class__.__name__, self.message)
413
414
415class StackError(RaetError):
416    '''
417       Exceptions in RAET stack processing
418
419       Usage:
420            emsg = "Invalid unique id '{0}'".format(uid)
421            raise raeting.StackError(emsg)
422    '''
423    pass
424
425
426class EstateError(RaetError):
427    '''
428       Exceptions in RAET estate processing
429
430       Usage:
431            emsg = "Invalid unique id '{0}'".format(uid)
432            raise raeting.EstateError(emsg)
433    '''
434    pass
435
436
437class TransactionError(RaetError):
438    '''
439       Exceptions in RAET transaction processing
440
441       Usage:
442            emsg = "Invalid uniqu id '{0}'".format(uid)
443            raise raeting.TransactionError(emsg)
444    '''
445    pass
446
447
448class PacketError(RaetError):
449    '''
450       Exceptions in RAET packet processing
451
452       Usage:
453            emsg = "Invalid unique id '{0}'".format(uid)
454            raise raeting.PacketError(emsg)
455    '''
456    pass
457
458
459class PacketSizeError(PacketError):
460    '''
461       Packet too large error needs to be segmented
462
463       Usage:
464            emsg = "Packet size {0} too large needs to be segmented".format(size)
465            raise raeting.PacketSizeError(emsg)
466    '''
467    pass
468
469
470class KeepError(RaetError):
471    '''
472       Exceptions in RAET keep processing
473
474       Usage:
475            emsg = "Invalid unique id '{0}'".format(uid)
476            raise raeting.KeepError(emsg)
477    '''
478    pass
479
480
481class YardError(RaetError):
482    '''
483       Exceptions in RAET yard processing
484
485       Usage:
486            emsg = "Invalid unique id '{0}'".format(uid)
487            raise raeting.YardError(emsg)
488    '''
489    pass
490
491
492class PageError(RaetError):
493    '''
494       Exceptions in RAET page processing
495
496       Usage:
497            emsg = "Invalid page kind '{0}'".format(kind)
498            raise raeting.PageError(emsg)
499    '''
500    pass
501