1# This file is part of Scapy
2# See http://www.secdev.org/projects/scapy for more information
3# Copyright (C) Philippe Biondi <phil@secdev.org>
4# Copyright (C) Michael Farrell <micolous+git@gmail.com>
5# Copyright (C) Gauthier Sebaux
6# This program is published under a GPLv2 license
7
8"""
9Fields that hold random numbers.
10"""
11
12from __future__ import absolute_import
13import copy
14import random
15import time
16import math
17import re
18import uuid
19import struct
20
21from scapy.base_classes import Net
22from scapy.compat import bytes_encode, chb, plain_str
23from scapy.utils import corrupt_bits, corrupt_bytes
24from scapy.modules.six.moves import range
25
26####################
27#  Random numbers  #
28####################
29
30
31class RandomEnumeration:
32    """iterate through a sequence in random order.
33       When all the values have been drawn, if forever=1, the drawing is done again.  # noqa: E501
34       If renewkeys=0, the draw will be in the same order, guaranteeing that the same  # noqa: E501
35       number will be drawn in not less than the number of integers of the sequence"""  # noqa: E501
36
37    def __init__(self, inf, sup, seed=None, forever=1, renewkeys=0):
38        self.forever = forever
39        self.renewkeys = renewkeys
40        self.inf = inf
41        self.rnd = random.Random(seed)
42        self.sbox_size = 256
43
44        self.top = sup - inf + 1
45
46        n = 0
47        while (1 << n) < self.top:
48            n += 1
49        self.n = n
50
51        self.fs = min(3, (n + 1) // 2)
52        self.fsmask = 2**self.fs - 1
53        self.rounds = max(self.n, 3)
54        self.turns = 0
55        self.i = 0
56
57    def __iter__(self):
58        return self
59
60    def next(self):
61        while True:
62            if self.turns == 0 or (self.i == 0 and self.renewkeys):
63                self.cnt_key = self.rnd.randint(0, 2**self.n - 1)
64                self.sbox = [self.rnd.randint(0, self.fsmask)
65                             for _ in range(self.sbox_size)]
66            self.turns += 1
67            while self.i < 2**self.n:
68                ct = self.i ^ self.cnt_key
69                self.i += 1
70                for _ in range(self.rounds):  # Unbalanced Feistel Network
71                    lsb = ct & self.fsmask
72                    ct >>= self.fs
73                    lsb ^= self.sbox[ct % self.sbox_size]
74                    ct |= lsb << (self.n - self.fs)
75
76                if ct < self.top:
77                    return self.inf + ct
78            self.i = 0
79            if not self.forever:
80                raise StopIteration
81    __next__ = next
82
83
84class VolatileValue(object):
85    def __repr__(self):
86        return "<%s>" % self.__class__.__name__
87
88    def _command_args(self):
89        return ''
90
91    def command(self):
92        return "%s(%s)" % (self.__class__.__name__, self._command_args())
93
94    def __eq__(self, other):
95        x = self._fix()
96        y = other._fix() if isinstance(other, VolatileValue) else other
97        if not isinstance(x, type(y)):
98            return False
99        return x == y
100
101    def __ne__(self, other):
102        # Python 2.7 compat
103        return not self == other
104
105    __hash__ = None
106
107    def __getattr__(self, attr):
108        if attr in ["__setstate__", "__getstate__"]:
109            raise AttributeError(attr)
110        return getattr(self._fix(), attr)
111
112    def __str__(self):
113        return str(self._fix())
114
115    def __bytes__(self):
116        return bytes_encode(self._fix())
117
118    def __len__(self):
119        return len(self._fix())
120
121    def copy(self):
122        return copy.copy(self)
123
124    def _fix(self):
125        return None
126
127
128class RandField(VolatileValue):
129    pass
130
131
132class _RandNumeral(RandField):
133    """Implements integer management in RandField"""
134
135    def __int__(self):
136        return int(self._fix())
137
138    def __index__(self):
139        return int(self)
140
141    def __nonzero__(self):
142        return bool(self._fix())
143    __bool__ = __nonzero__
144
145    def __add__(self, other):
146        return self._fix() + other
147
148    def __radd__(self, other):
149        return other + self._fix()
150
151    def __sub__(self, other):
152        return self._fix() - other
153
154    def __rsub__(self, other):
155        return other - self._fix()
156
157    def __mul__(self, other):
158        return self._fix() * other
159
160    def __rmul__(self, other):
161        return other * self._fix()
162
163    def __floordiv__(self, other):
164        return self._fix() / other
165    __div__ = __floordiv__
166
167    def __lt__(self, other):
168        return self._fix() < other
169
170    def __le__(self, other):
171        return self._fix() <= other
172
173    def __ge__(self, other):
174        return self._fix() >= other
175
176    def __gt__(self, other):
177        return self._fix() > other
178
179    def __lshift__(self, other):
180        return self._fix() << other
181
182    def __rshift__(self, other):
183        return self._fix() >> other
184
185    def __and__(self, other):
186        return self._fix() & other
187
188    def __rand__(self, other):
189        return other & self._fix()
190
191    def __or__(self, other):
192        return self._fix() | other
193
194    def __ror__(self, other):
195        return other | self._fix()
196
197
198class RandNum(_RandNumeral):
199    """Instances evaluate to random integers in selected range"""
200    min = 0
201    max = 0
202
203    def __init__(self, min, max):
204        self.min = min
205        self.max = max
206
207    def _command_args(self):
208        if self.__class__.__name__ == 'RandNum':
209            return "min=%r, max=%r" % (self.min, self.max)
210        return super(RandNum, self)._command_args()
211
212    def _fix(self):
213        return random.randrange(self.min, self.max + 1)
214
215
216class RandFloat(RandNum):
217    def _fix(self):
218        return random.uniform(self.min, self.max)
219
220
221class RandBinFloat(RandNum):
222    def _fix(self):
223        return struct.unpack("!f", bytes(RandBin(4)))[0]
224
225
226class RandNumGamma(_RandNumeral):
227    def __init__(self, alpha, beta):
228        self.alpha = alpha
229        self.beta = beta
230
231    def _command_args(self):
232        return "alpha=%r, beta=%r" % (self.alpha, self.beta)
233
234    def _fix(self):
235        return int(round(random.gammavariate(self.alpha, self.beta)))
236
237
238class RandNumGauss(_RandNumeral):
239    def __init__(self, mu, sigma):
240        self.mu = mu
241        self.sigma = sigma
242
243    def _command_args(self):
244        return "mu=%r, sigma=%r" % (self.mu, self.sigma)
245
246    def _fix(self):
247        return int(round(random.gauss(self.mu, self.sigma)))
248
249
250class RandNumExpo(_RandNumeral):
251    def __init__(self, lambd, base=0):
252        self.lambd = lambd
253        self.base = base
254
255    def _command_args(self):
256        ret = "lambd=%r" % self.lambd
257        if self.base != 0:
258            ret += ", base=%r" % self.base
259        return ret
260
261    def _fix(self):
262        return self.base + int(round(random.expovariate(self.lambd)))
263
264
265class RandEnum(RandNum):
266    """Instances evaluate to integer sampling without replacement from the given interval"""  # noqa: E501
267
268    def __init__(self, min, max, seed=None):
269        self._seed = seed
270        self.seq = RandomEnumeration(min, max, seed)
271        super(RandEnum, self).__init__(min, max)
272
273    def _command_args(self):
274        ret = "min=%r, max=%r" % (self.min, self.max)
275        if self._seed:
276            ret += ", seed=%r" % self._seed
277        return ret
278
279    def _fix(self):
280        return next(self.seq)
281
282
283class RandByte(RandNum):
284    def __init__(self):
285        RandNum.__init__(self, 0, 2**8 - 1)
286
287
288class RandSByte(RandNum):
289    def __init__(self):
290        RandNum.__init__(self, -2**7, 2**7 - 1)
291
292
293class RandShort(RandNum):
294    def __init__(self):
295        RandNum.__init__(self, 0, 2**16 - 1)
296
297
298class RandSShort(RandNum):
299    def __init__(self):
300        RandNum.__init__(self, -2**15, 2**15 - 1)
301
302
303class RandInt(RandNum):
304    def __init__(self):
305        RandNum.__init__(self, 0, 2**32 - 1)
306
307
308class RandSInt(RandNum):
309    def __init__(self):
310        RandNum.__init__(self, -2**31, 2**31 - 1)
311
312
313class RandLong(RandNum):
314    def __init__(self):
315        RandNum.__init__(self, 0, 2**64 - 1)
316
317
318class RandSLong(RandNum):
319    def __init__(self):
320        RandNum.__init__(self, -2**63, 2**63 - 1)
321
322
323class RandEnumByte(RandEnum):
324    def __init__(self):
325        RandEnum.__init__(self, 0, 2**8 - 1)
326
327
328class RandEnumSByte(RandEnum):
329    def __init__(self):
330        RandEnum.__init__(self, -2**7, 2**7 - 1)
331
332
333class RandEnumShort(RandEnum):
334    def __init__(self):
335        RandEnum.__init__(self, 0, 2**16 - 1)
336
337
338class RandEnumSShort(RandEnum):
339    def __init__(self):
340        RandEnum.__init__(self, -2**15, 2**15 - 1)
341
342
343class RandEnumInt(RandEnum):
344    def __init__(self):
345        RandEnum.__init__(self, 0, 2**32 - 1)
346
347
348class RandEnumSInt(RandEnum):
349    def __init__(self):
350        RandEnum.__init__(self, -2**31, 2**31 - 1)
351
352
353class RandEnumLong(RandEnum):
354    def __init__(self):
355        RandEnum.__init__(self, 0, 2**64 - 1)
356
357
358class RandEnumSLong(RandEnum):
359    def __init__(self):
360        RandEnum.__init__(self, -2**63, 2**63 - 1)
361
362
363class RandEnumKeys(RandEnum):
364    """Picks a random value from dict keys list. """
365
366    def __init__(self, enum, seed=None):
367        self.enum = list(enum)
368        RandEnum.__init__(self, 0, len(self.enum) - 1, seed)
369
370    def _command_args(self):
371        # Note: only outputs the list of keys, but values are irrelevant anyway
372        ret = "enum=%r" % self.enum
373        if self._seed:
374            ret += ", seed=%r" % self._seed
375        return ret
376
377    def _fix(self):
378        return self.enum[next(self.seq)]
379
380
381class RandChoice(RandField):
382    def __init__(self, *args):
383        if not args:
384            raise TypeError("RandChoice needs at least one choice")
385        self._choice = list(args)
386
387    def _command_args(self):
388        return ", ".join(self._choice)
389
390    def _fix(self):
391        return random.choice(self._choice)
392
393
394class RandString(RandField):
395    _DEFAULT_CHARS = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"  # noqa: E501
396
397    def __init__(self, size=None, chars=_DEFAULT_CHARS):
398        if size is None:
399            size = RandNumExpo(0.01)
400        self.size = size
401        self.chars = chars
402
403    def _command_args(self):
404        ret = ""
405        if isinstance(self.size, VolatileValue):
406            if self.size.lambd != 0.01 or self.size.base != 0:
407                ret += "size=%r" % self.size.command()
408        else:
409            ret += "size=%r" % self.size
410
411        if self.chars != self._DEFAULT_CHARS:
412            ret += ", chars=%r" % self.chars
413        return ret
414
415    def _fix(self):
416        s = b""
417        for _ in range(self.size):
418            rdm_chr = random.choice(self.chars)
419            s += rdm_chr if isinstance(rdm_chr, str) else chb(rdm_chr)
420        return s
421
422    def __str__(self):
423        return plain_str(self._fix())
424
425    def __bytes__(self):
426        return bytes_encode(self._fix())
427
428    def __mul__(self, n):
429        return self._fix() * n
430
431
432class RandBin(RandString):
433    def __init__(self, size=None):
434        super(RandBin, self).__init__(size=size, chars=b"".join(chb(c) for c in range(256)))  # noqa: E501
435
436    def _command_args(self):
437        if not isinstance(self.size, VolatileValue):
438            return "size=%r" % self.size
439
440        if isinstance(self.size, RandNumExpo) and \
441                self.size.lambd == 0.01 and self.size.base == 0:
442            # Default size for RandString, skip
443            return ""
444        return "size=%r" % self.size.command()
445
446
447class RandTermString(RandBin):
448    def __init__(self, size, term):
449        self.term = bytes_encode(term)
450        super(RandTermString, self).__init__(size=size)
451
452    def _command_args(self):
453        return ", ".join((super(RandTermString, self)._command_args(),
454                          "term=%r" % self.term))
455
456    def _fix(self):
457        return RandBin._fix(self) + self.term
458
459
460class RandIP(RandString):
461    _DEFAULT_IPTEMPLATE = "0.0.0.0/0"
462
463    def __init__(self, iptemplate=_DEFAULT_IPTEMPLATE):
464        RandString.__init__(self)
465        self.ip = Net(iptemplate)
466
467    def _command_args(self):
468        if self.ip.repr == self._DEFAULT_IPTEMPLATE:
469            return ""
470        return "iptemplate=%r" % self.ip.repr
471
472    def _fix(self):
473        return self.ip.choice()
474
475
476class RandMAC(RandString):
477    def __init__(self, template="*"):
478        RandString.__init__(self)
479        self._template = template
480        template += ":*:*:*:*:*"
481        template = template.split(":")
482        self.mac = ()
483        for i in range(6):
484            if template[i] == "*":
485                v = RandByte()
486            elif "-" in template[i]:
487                x, y = template[i].split("-")
488                v = RandNum(int(x, 16), int(y, 16))
489            else:
490                v = int(template[i], 16)
491            self.mac += (v,)
492
493    def _command_args(self):
494        if self._template == "*":
495            return ""
496        return "template=%r" % self._template
497
498    def _fix(self):
499        return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac
500
501
502class RandIP6(RandString):
503    def __init__(self, ip6template="**"):
504        RandString.__init__(self)
505        self.tmpl = ip6template
506        self.sp = self.tmpl.split(":")
507        for i, v in enumerate(self.sp):
508            if not v or v == "**":
509                continue
510            if "-" in v:
511                a, b = v.split("-")
512            elif v == "*":
513                a = b = ""
514            else:
515                a = b = v
516
517            if not a:
518                a = "0"
519            if not b:
520                b = "ffff"
521            if a == b:
522                self.sp[i] = int(a, 16)
523            else:
524                self.sp[i] = RandNum(int(a, 16), int(b, 16))
525        self.variable = "" in self.sp
526        self.multi = self.sp.count("**")
527
528    def _command_args(self):
529        if self.tmpl == "**":
530            return ""
531        return "ip6template=%r" % self.tmpl
532
533    def _fix(self):
534        nbm = self.multi
535        ip = []
536        for i, n in enumerate(self.sp):
537            if n == "**":
538                nbm -= 1
539                remain = 8 - (len(self.sp) - i - 1) - len(ip) + nbm
540                if "" in self.sp:
541                    remain += 1
542                if nbm or self.variable:
543                    remain = random.randint(0, remain)
544                for j in range(remain):
545                    ip.append("%04x" % random.randint(0, 65535))
546            elif isinstance(n, RandNum):
547                ip.append("%04x" % n)
548            elif n == 0:
549                ip.append("0")
550            elif not n:
551                ip.append("")
552            else:
553                ip.append("%04x" % n)
554        if len(ip) == 9:
555            ip.remove("")
556        if ip[-1] == "":
557            ip[-1] = "0"
558        return ":".join(ip)
559
560
561class RandOID(RandString):
562    def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)):  # noqa: E501
563        RandString.__init__(self)
564        self.ori_fmt = fmt
565        if fmt is not None:
566            fmt = fmt.split(".")
567            for i in range(len(fmt)):
568                if "-" in fmt[i]:
569                    fmt[i] = tuple(map(int, fmt[i].split("-")))
570        self.fmt = fmt
571        self.depth = depth
572        self.idnum = idnum
573
574    def _command_args(self):
575        ret = []
576        if self.fmt:
577            ret.append("fmt=%r" % self.ori_fmt)
578
579        if not isinstance(self.depth, VolatileValue):
580            ret.append("depth=%r" % self.depth)
581        elif not isinstance(self.depth, RandNumExpo) or \
582                self.depth.lambd != 0.1 or self.depth.base != 0:
583            ret.append("depth=%s" % self.depth.command())
584
585        if not isinstance(self.idnum, VolatileValue):
586            ret.append("idnum=%r" % self.idnum)
587        elif not isinstance(self.idnum, RandNumExpo) or \
588                self.idnum.lambd != 0.01 or self.idnum.base != 0:
589            ret.append("idnum=%s" % self.idnum.command())
590
591        return ", ".join(ret)
592
593    def __repr__(self):
594        if self.ori_fmt is None:
595            return "<%s>" % self.__class__.__name__
596        else:
597            return "<%s [%s]>" % (self.__class__.__name__, self.ori_fmt)
598
599    def _fix(self):
600        if self.fmt is None:
601            return ".".join(str(self.idnum) for _ in range(1 + self.depth))
602        else:
603            oid = []
604            for i in self.fmt:
605                if i == "*":
606                    oid.append(str(self.idnum))
607                elif i == "**":
608                    oid += [str(self.idnum) for i in range(1 + self.depth)]
609                elif isinstance(i, tuple):
610                    oid.append(str(random.randrange(*i)))
611                else:
612                    oid.append(i)
613            return ".".join(oid)
614
615
616class RandRegExp(RandField):
617    def __init__(self, regexp, lambda_=0.3,):
618        self._regexp = regexp
619        self._lambda = lambda_
620
621    def _command_args(self):
622        ret = "regexp=%r" % self._regexp
623        if self._lambda != 0.3:
624            ret += ", lambda_=%r" % self._lambda
625        return ret
626
627    special_sets = {
628        "[:alnum:]": "[a-zA-Z0-9]",
629        "[:alpha:]": "[a-zA-Z]",
630        "[:ascii:]": "[\x00-\x7F]",
631        "[:blank:]": "[ \t]",
632        "[:cntrl:]": "[\x00-\x1F\x7F]",
633        "[:digit:]": "[0-9]",
634        "[:graph:]": "[\x21-\x7E]",
635        "[:lower:]": "[a-z]",
636        "[:print:]": "[\x20-\x7E]",
637        "[:punct:]": "[!\"\\#$%&'()*+,\\-./:;<=>?@\\[\\\\\\]^_{|}~]",
638        "[:space:]": "[ \t\r\n\v\f]",
639        "[:upper:]": "[A-Z]",
640        "[:word:]": "[A-Za-z0-9_]",
641        "[:xdigit:]": "[A-Fa-f0-9]",
642    }
643
644    @staticmethod
645    def choice_expand(s):
646        m = ""
647        invert = s and s[0] == "^"
648        while True:
649            p = s.find("-")
650            if p < 0:
651                break
652            if p == 0 or p == len(s) - 1:
653                m = "-"
654                if p:
655                    s = s[:-1]
656                else:
657                    s = s[1:]
658            else:
659                c1 = s[p - 1]
660                c2 = s[p + 1]
661                rng = "".join(map(chr, range(ord(c1), ord(c2) + 1)))
662                s = s[:p - 1] + rng + s[p + 1:]
663        res = m + s
664        if invert:
665            res = "".join(chr(x) for x in range(256) if chr(x) not in res)
666        return res
667
668    @staticmethod
669    def stack_fix(lst, index):
670        r = ""
671        mul = 1
672        for e in lst:
673            if isinstance(e, list):
674                if mul != 1:
675                    mul = mul - 1
676                    r += RandRegExp.stack_fix(e[1:] * mul, index)
677                # only the last iteration should be kept for back reference
678                f = RandRegExp.stack_fix(e[1:], index)
679                for i, idx in enumerate(index):
680                    if e is idx:
681                        index[i] = f
682                r += f
683                mul = 1
684            elif isinstance(e, tuple):
685                kind, val = e
686                if kind == "cite":
687                    r += index[val - 1]
688                elif kind == "repeat":
689                    mul = val
690
691                elif kind == "choice":
692                    if mul == 1:
693                        c = random.choice(val)
694                        r += RandRegExp.stack_fix(c[1:], index)
695                    else:
696                        r += RandRegExp.stack_fix([e] * mul, index)
697                        mul = 1
698            else:
699                if mul != 1:
700                    r += RandRegExp.stack_fix([e] * mul, index)
701                    mul = 1
702                else:
703                    r += str(e)
704        return r
705
706    def _fix(self):
707        stack = [None]
708        index = []
709        current = stack
710        i = 0
711        regexp = self._regexp
712        for k, v in self.special_sets.items():
713            regexp = regexp.replace(k, v)
714        ln = len(regexp)
715        interp = True
716        while i < ln:
717            c = regexp[i]
718            i += 1
719
720            if c == '(':
721                current = [current]
722                current[0].append(current)
723            elif c == '|':
724                p = current[0]
725                ch = p[-1]
726                if not isinstance(ch, tuple):
727                    ch = ("choice", [current])
728                    p[-1] = ch
729                else:
730                    ch[1].append(current)
731                current = [p]
732            elif c == ')':
733                ch = current[0][-1]
734                if isinstance(ch, tuple):
735                    ch[1].append(current)
736                index.append(current)
737                current = current[0]
738            elif c == '[' or c == '{':
739                current = [current]
740                current[0].append(current)
741                interp = False
742            elif c == ']':
743                current = current[0]
744                choice = RandRegExp.choice_expand("".join(current.pop()[1:]))
745                current.append(RandChoice(*list(choice)))
746                interp = True
747            elif c == '}':
748                current = current[0]
749                num = "".join(current.pop()[1:])
750                e = current.pop()
751                if "," not in num:
752                    n = int(num)
753                    current.append([current] + [e] * n)
754                else:
755                    num_min, num_max = num.split(",")
756                    if not num_min:
757                        num_min = "0"
758                    if num_max:
759                        n = RandNum(int(num_min), int(num_max))
760                    else:
761                        n = RandNumExpo(self._lambda, base=int(num_min))
762                    current.append(("repeat", n))
763                    current.append(e)
764                interp = True
765            elif c == '\\':
766                c = regexp[i]
767                if c == "s":
768                    c = RandChoice(" ", "\t")
769                elif c in "0123456789":
770                    c = ("cite", ord(c) - 0x30)
771                current.append(c)
772                i += 1
773            elif not interp:
774                current.append(c)
775            elif c == '+':
776                e = current.pop()
777                current.append([current] + [e] * (int(random.expovariate(self._lambda)) + 1))  # noqa: E501
778            elif c == '*':
779                e = current.pop()
780                current.append([current] + [e] * int(random.expovariate(self._lambda)))  # noqa: E501
781            elif c == '?':
782                if random.randint(0, 1):
783                    current.pop()
784            elif c == '.':
785                current.append(RandChoice(*[chr(x) for x in range(256)]))
786            elif c == '$' or c == '^':
787                pass
788            else:
789                current.append(c)
790
791        return RandRegExp.stack_fix(stack[1:], index)
792
793    def __repr__(self):
794        return "<%s [%r]>" % (self.__class__.__name__, self._regexp)
795
796
797class RandSingularity(RandChoice):
798    pass
799
800
801class RandSingNum(RandSingularity):
802    @staticmethod
803    def make_power_of_two(end):
804        sign = 1
805        if end == 0:
806            end = 1
807        if end < 0:
808            end = -end
809            sign = -1
810        end_n = int(math.log(end) / math.log(2)) + 1
811        return {sign * 2**i for i in range(end_n)}
812
813    def __init__(self, mn, mx):
814        self._mn = mn
815        self._mx = mx
816        sing = {0, mn, mx, int((mn + mx) / 2)}
817        sing |= self.make_power_of_two(mn)
818        sing |= self.make_power_of_two(mx)
819        for i in sing.copy():
820            sing.add(i + 1)
821            sing.add(i - 1)
822        for i in sing.copy():
823            if not mn <= i <= mx:
824                sing.remove(i)
825        super(RandSingNum, self).__init__(*sing)
826        self._choice.sort()
827
828    def _command_args(self):
829        if self.__class__.__name__ == 'RandSingNum':
830            return "mn=%r, mx=%r" % (self._mn, self._mx)
831        return super(RandSingNum, self)._command_args()
832
833
834class RandSingByte(RandSingNum):
835    def __init__(self):
836        RandSingNum.__init__(self, 0, 2**8 - 1)
837
838
839class RandSingSByte(RandSingNum):
840    def __init__(self):
841        RandSingNum.__init__(self, -2**7, 2**7 - 1)
842
843
844class RandSingShort(RandSingNum):
845    def __init__(self):
846        RandSingNum.__init__(self, 0, 2**16 - 1)
847
848
849class RandSingSShort(RandSingNum):
850    def __init__(self):
851        RandSingNum.__init__(self, -2**15, 2**15 - 1)
852
853
854class RandSingInt(RandSingNum):
855    def __init__(self):
856        RandSingNum.__init__(self, 0, 2**32 - 1)
857
858
859class RandSingSInt(RandSingNum):
860    def __init__(self):
861        RandSingNum.__init__(self, -2**31, 2**31 - 1)
862
863
864class RandSingLong(RandSingNum):
865    def __init__(self):
866        RandSingNum.__init__(self, 0, 2**64 - 1)
867
868
869class RandSingSLong(RandSingNum):
870    def __init__(self):
871        RandSingNum.__init__(self, -2**63, 2**63 - 1)
872
873
874class RandSingString(RandSingularity):
875    def __init__(self):
876        choices_list = ["",
877                        "%x",
878                        "%%",
879                        "%s",
880                        "%i",
881                        "%n",
882                        "%x%x%x%x%x%x%x%x%x",
883                        "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
884                        "%",
885                        "%%%",
886                        "A" * 4096,
887                        b"\x00" * 4096,
888                        b"\xff" * 4096,
889                        b"\x7f" * 4096,
890                        b"\x80" * 4096,
891                        " " * 4096,
892                        "\\" * 4096,
893                        "(" * 4096,
894                        "../" * 1024,
895                        "/" * 1024,
896                        "${HOME}" * 512,
897                        " or 1=1 --",
898                        "' or 1=1 --",
899                        '" or 1=1 --',
900                        " or 1=1; #",
901                        "' or 1=1; #",
902                        '" or 1=1; #',
903                        ";reboot;",
904                        "$(reboot)",
905                        "`reboot`",
906                        "index.php%00",
907                        b"\x00",
908                        "%00",
909                        "\\",
910                        "../../../../../../../../../../../../../../../../../etc/passwd",  # noqa: E501
911                        "%2e%2e%2f" * 20 + "etc/passwd",
912                        "%252e%252e%252f" * 20 + "boot.ini",
913                        "..%c0%af" * 20 + "etc/passwd",
914                        "..%c0%af" * 20 + "boot.ini",
915                        "//etc/passwd",
916                        r"..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\boot.ini",  # noqa: E501
917                        "AUX:",
918                        "CLOCK$",
919                        "COM:",
920                        "CON:",
921                        "LPT:",
922                        "LST:",
923                        "NUL:",
924                        "CON:",
925                        r"C:\CON\CON",
926                        r"C:\boot.ini",
927                        r"\\myserver\share",
928                        "foo.exe:",
929                        "foo.exe\\", ]
930        super(RandSingString, self).__init__(*choices_list)
931
932    def _command_args(self):
933        return ""
934
935    def __str__(self):
936        return str(self._fix())
937
938    def __bytes__(self):
939        return bytes_encode(self._fix())
940
941
942class RandPool(RandField):
943    def __init__(self, *args):
944        """Each parameter is a volatile object or a couple (volatile object, weight)"""  # noqa: E501
945        self._args = args
946        pool = []
947        for p in args:
948            w = 1
949            if isinstance(p, tuple):
950                p, w = p
951            pool += [p] * w
952        self._pool = pool
953
954    def _command_args(self):
955        ret = []
956        for p in self._args:
957            if isinstance(p, tuple):
958                ret.append("(%s, %r)" % (p[0].command(), p[1]))
959            else:
960                ret.append(p.command())
961        return ", ".join(ret)
962
963    def _fix(self):
964        r = random.choice(self._pool)
965        return r._fix()
966
967
968class RandUUID(RandField):
969    """Generates a random UUID.
970
971    By default, this generates a RFC 4122 version 4 UUID (totally random).
972
973    See Python's ``uuid`` module documentation for more information.
974
975    Args:
976        template (optional): A template to build the UUID from. Not valid with
977                             any other option.
978        node (optional): A 48-bit Host ID. Only valid for version 1 (where it
979                         is optional).
980        clock_seq (optional): An integer of up to 14-bits for the sequence
981                              number. Only valid for version 1 (where it is
982                              optional).
983        namespace: A namespace identifier, which is also a UUID. Required for
984                   versions 3 and 5, must be omitted otherwise.
985        name: string, required for versions 3 and 5, must be omitted otherwise.
986        version: Version of UUID to use (1, 3, 4 or 5). If omitted, attempts to
987                 guess which version to generate, defaulting to version 4
988                 (totally random).
989
990    Raises:
991        ValueError: on invalid constructor arguments
992    """
993    # This was originally scapy.contrib.dce_rpc.RandUUID.
994
995    _BASE = "([0-9a-f]{{{0}}}|\\*|[0-9a-f]{{{0}}}:[0-9a-f]{{{0}}})"
996    _REG = re.compile(
997        r"^{0}-?{1}-?{1}-?{2}{2}-?{2}{2}{2}{2}{2}{2}$".format(
998            _BASE.format(8), _BASE.format(4), _BASE.format(2)
999        ),
1000        re.I
1001    )
1002    VERSIONS = [1, 3, 4, 5]
1003
1004    def __init__(self, template=None, node=None, clock_seq=None,
1005                 namespace=None, name=None, version=None):
1006        self._template = template
1007        self._ori_version = version
1008
1009        self.uuid_template = None
1010        self.node = None
1011        self.clock_seq = None
1012        self.namespace = None
1013        self.name = None
1014        self.node = None
1015        self.version = None
1016
1017        if template:
1018            if node or clock_seq or namespace or name or version:
1019                raise ValueError("UUID template must be the only parameter, "
1020                                 "if specified")
1021            tmp = RandUUID._REG.match(template)
1022            if tmp:
1023                template = tmp.groups()
1024            else:
1025                # Invalid template
1026                raise ValueError("UUID template is invalid")
1027
1028            rnd_f = [RandInt] + [RandShort] * 2 + [RandByte] * 8
1029            uuid_template = []
1030            for i, t in enumerate(template):
1031                if t == "*":
1032                    val = rnd_f[i]()
1033                elif ":" in t:
1034                    mini, maxi = t.split(":")
1035                    val = RandNum(int(mini, 16), int(maxi, 16))
1036                else:
1037                    val = int(t, 16)
1038                uuid_template.append(val)
1039
1040            self.uuid_template = tuple(uuid_template)
1041        else:
1042            if version:
1043                if version not in RandUUID.VERSIONS:
1044                    raise ValueError("version is not supported")
1045                else:
1046                    self.version = version
1047            else:
1048                # No version specified, try to guess...
1049                # This could be wrong, and cause an error later!
1050                if node or clock_seq:
1051                    self.version = 1
1052                elif namespace and name:
1053                    self.version = 5
1054                else:
1055                    # Don't know, random!
1056                    self.version = 4
1057
1058            # We have a version, now do things...
1059            if self.version == 1:
1060                if namespace or name:
1061                    raise ValueError("namespace and name may not be used with "
1062                                     "version 1")
1063                self.node = node
1064                self.clock_seq = clock_seq
1065            elif self.version in (3, 5):
1066                if node or clock_seq:
1067                    raise ValueError("node and clock_seq may not be used with "
1068                                     "version {}".format(self.version))
1069
1070                self.namespace = namespace
1071                self.name = name
1072            elif self.version == 4:
1073                if namespace or name or node or clock_seq:
1074                    raise ValueError("node, clock_seq, node and clock_seq may "
1075                                     "not be used with version 4. If you "
1076                                     "did not specify version, you need to "
1077                                     "specify it explicitly.")
1078
1079    def _command_args(self):
1080        ret = []
1081        if self._template:
1082            ret.append("template=%r" % self._template)
1083        if self.node:
1084            ret.append("node=%r" % self.node)
1085        if self.clock_seq:
1086            ret.append("clock_seq=%r" % self.clock_seq)
1087        if self.namespace:
1088            ret.append("namespace=%r" % self.namespace)
1089        if self.name:
1090            ret.append("name=%r" % self.name)
1091        if self._ori_version:
1092            ret.append("version=%r" % self._ori_version)
1093        return ", ".join(ret)
1094
1095    def _fix(self):
1096        if self.uuid_template:
1097            return uuid.UUID(("%08x%04x%04x" + ("%02x" * 8))
1098                             % self.uuid_template)
1099        elif self.version == 1:
1100            return uuid.uuid1(self.node, self.clock_seq)
1101        elif self.version == 3:
1102            return uuid.uuid3(self.namespace, self.name)
1103        elif self.version == 4:
1104            return uuid.uuid4()
1105        elif self.version == 5:
1106            return uuid.uuid5(self.namespace, self.name)
1107        else:
1108            raise ValueError("Unhandled version")
1109
1110
1111# Automatic timestamp
1112
1113
1114class AutoTime(_RandNumeral):
1115    def __init__(self, base=None, diff=None):
1116        self._base = base
1117        self._ori_diff = diff
1118
1119        if diff is not None:
1120            self.diff = diff
1121        elif base is None:
1122            self.diff = 0
1123        else:
1124            self.diff = time.time() - base
1125
1126    def _command_args(self):
1127        ret = []
1128        if self._base:
1129            ret.append("base=%r" % self._base)
1130        if self._ori_diff:
1131            ret.append("diff=%r" % self._ori_diff)
1132        return ", ".join(ret)
1133
1134    def _fix(self):
1135        return time.time() - self.diff
1136
1137
1138class IntAutoTime(AutoTime):
1139    def _fix(self):
1140        return int(time.time() - self.diff)
1141
1142
1143class ZuluTime(AutoTime):
1144    def __init__(self, diff=0):
1145        super(ZuluTime, self).__init__(diff=diff)
1146
1147    def _fix(self):
1148        return time.strftime("%y%m%d%H%M%SZ",
1149                             time.gmtime(time.time() + self.diff))
1150
1151
1152class GeneralizedTime(AutoTime):
1153    def __init__(self, diff=0):
1154        super(GeneralizedTime, self).__init__(diff=diff)
1155
1156    def _fix(self):
1157        return time.strftime("%Y%m%d%H%M%SZ",
1158                             time.gmtime(time.time() + self.diff))
1159
1160
1161class DelayedEval(VolatileValue):
1162    """ Example of usage: DelayedEval("time.time()") """
1163
1164    def __init__(self, expr):
1165        self.expr = expr
1166
1167    def _command_args(self):
1168        return "expr=%r" % self.expr
1169
1170    def _fix(self):
1171        return eval(self.expr)
1172
1173
1174class IncrementalValue(VolatileValue):
1175    def __init__(self, start=0, step=1, restart=-1):
1176        self.start = self.val = start
1177        self.step = step
1178        self.restart = restart
1179
1180    def _command_args(self):
1181        ret = []
1182        if self.start:
1183            ret.append("start=%r" % self.start)
1184        if self.step != 1:
1185            ret.append("step=%r" % self.step)
1186        if self.restart != -1:
1187            ret.append("restart=%r" % self.restart)
1188        return ", ".join(ret)
1189
1190    def _fix(self):
1191        v = self.val
1192        if self.val == self.restart:
1193            self.val = self.start
1194        else:
1195            self.val += self.step
1196        return v
1197
1198
1199class CorruptedBytes(VolatileValue):
1200    def __init__(self, s, p=0.01, n=None):
1201        self.s = s
1202        self.p = p
1203        self.n = n
1204
1205    def _command_args(self):
1206        ret = []
1207        ret.append("s=%r" % self.s)
1208        if self.p != 0.01:
1209            ret.append("p=%r" % self.p)
1210        if self.n:
1211            ret.append("n=%r" % self.n)
1212        return ", ".join(ret)
1213
1214    def _fix(self):
1215        return corrupt_bytes(self.s, self.p, self.n)
1216
1217
1218class CorruptedBits(CorruptedBytes):
1219    def _fix(self):
1220        return corrupt_bits(self.s, self.p, self.n)
1221