github.com/iDigitalFlame/xmt@v0.5.4/tools/config.py (about) 1 #!/usr/bin/python3 2 # Copyright (C) 2020 - 2023 iDigitalFlame 3 # 4 # This program is free software: you can redistribute it and/or modify 5 # it under the terms of the GNU General Public License as published by 6 # the Free Software Foundation, either version 3 of the License, or 7 # any later version. 8 # 9 # This program is distributed in the hope that it will be useful, 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 # GNU General Public License for more details. 13 # 14 # You should have received a copy of the GNU General Public License 15 # along with this program. If not, see <https://www.gnu.org/licenses/>. 16 # 17 18 from zlib import crc32 19 from shlex import split 20 from io import StringIO 21 from hashlib import sha512 22 from datetime import datetime 23 from json import dumps, loads 24 from secrets import token_bytes 25 from traceback import format_exc 26 from argparse import ArgumentParser 27 from base64 import b64decode, b64encode 28 from sys import argv, exit, stderr, stdin, stdout 29 30 HELP_TEXT = """XMT cfg.Config Builder v1.5 Release 31 32 Usage: {binary} [add|append] <options> 33 34 GROUP MODE: 35 To enable adding multiple Profiles under a config (Group Mode), 36 add the "add" or "append" word before the command options. This 37 will append the options as a separate group in the Profile. 38 39 To overrite the group, omit the "add" or "append" value (the 40 default option). 41 42 In Group Mode, the -f/--file/-o/--out arguments may behave 43 differently. If no output file is specified and "input" points to 44 a valid file, the "input" WILL ALSO be treated as the "output". The 45 same behavior occurs if no input is specified but "output" points 46 to a valid file, then the output file will be read in as input 47 before being appended to. 48 49 To disable this behavior, just specify the output and input files 50 when using Group Mode. 51 52 BASIC ARGUMENTS: 53 -h Show this help message and exit. 54 --help 55 56 INPUT/OUTPUT ARGUMENTS: 57 -f <file> 58 --in <file> Input file path. Use '-' for stdin. 59 See the 'Group Mode' section for the 60 modifiers to this argument. 61 -o <file> 62 --out <file> Output file path. Stdout is used if 63 empty. See the 'Group Mode' section 64 for the modifiers to this argument. 65 -j 66 --json Output in JSON format. Omit for raw 67 binary. (Or base64 when output to 68 stdout.) 69 -I Accept stdin input as commands. Each 70 --stdin line from stdin will be treated as a 71 'append' line to the supplied config. 72 Input and Output are ignored and are 73 only set via the command line. 74 This option disables using stdin for 75 Config data. 76 77 OPERATION ARGUMENTS: 78 -p 79 --print List values contained in the file 80 input. Fails if no input is found or 81 invalid. Output format can be modified 82 using -j/-p. 83 84 BUILD ARGUMENTS: 85 SYSTEM: 86 --host <hostname> Hostname hint, used for implants only. 87 --sleep <secs|mod> Sleep time period. Defaults to seconds 88 for integers, but can take modifiers 89 such as 's', 'h', 'm'. (ex: 2m or 3s). 90 --jitter <jitter %> Jitter as a percentage [0-100]. Values 91 greater than 100 fail. The '%' symbol 92 may be included or omitted. 93 --weight <weight> Group weight value [0-100]. Takes effect 94 only if multiple Profiles exist. Only 95 affects the Group it is used in. 96 --selector <selector> Use the specified selector to indicate 97 which order the Profile Groups should 98 be used in. Takes effect globally and 99 only needs to be used once in ANY 100 group. 101 102 See SELECTOR VALUES for more info. 103 --killdate <ISO-8601> Specify a time in ISO-8601 format that 104 be used to indicate when the implant 105 will cease to function. The time should 106 be specified in ISO-8601 format which is 107 "YYYY-MM-DDTHH:MM:SS". The seconds may 108 be omitted. 109 --wh-days <S-M-S str> Specify a Working hours value that targets 110 specific days. This may be used in combonation 111 with the "--wh-start" and "--wh-end" arguments. 112 The accepted values are "SMTWRFS". Note: 113 Sunday is the first day and must be the first 114 'S' to be parsed correctly. All other chars 115 may be out of order if needed. 116 --wh-start <HH:MM> Specify a time in an HOURS:MINS format that 117 specifies when the implant may start connecting 118 with the C2 Server. This setting takes affect 119 if there is a day or end value set. 120 --wh-end <HH:MM> Specify a time in an HOURS:MINS format that 121 specifies when the implant will stop connecting 122 with the C2 Server. This setting will apply 123 regardless of day or start setting and if a 124 day or start time does NOT exist, this will 125 instruct the implant to wait until midnight to 126 try again. 127 128 CONNECTION HINTS (Max 1 per Profile Group): 129 --tcp Use the TCP Connection hint. 130 --tls Use the TLS Connection hint. 131 --udp Use the UDP Connection hint. 132 --icmp Use the ICMP (Ping) Connection hint. 133 --pipe Use the Windows Named Pipes or UNIX file 134 pipes Connection hint. 135 --tls-insecure 136 -K Use the TLSNoVerify Connection hint. 137 138 --ip <protocol> Use the IP Connection hint with the 139 specified protocol number [0-255]. 140 --wc2-url <url> Use the WC2 Connection hint with the 141 URL expression or static string. 142 This can be used with other WC2 143 arguments without an error. 144 --wc2-host <host> Use the WC2 Connection hint with the 145 Host expression or static string. 146 This can be used with other WC2 147 arguments without an error. 148 --wc2-agent <agent> Use the WC2 Connection hint with the 149 User-Agent expression or static string. 150 This can be used with other WC2 151 arguments without an error. 152 --wc2-header <key>=<val> 153 -H <key>=<val> Use the WC2 Connection hint with the 154 HTTP header expression or static string in 155 a key=value formnat. This value will be 156 parsed and will fail if 'key' is empty or 157 no '=' is present in the string. This may be 158 specified multiple times. This can be used 159 with other WC2 arguments without an error. 160 161 --mtls Use Mutual TLS Authentication (mTLS) with a TLS 162 Connection hint. This just enables the flag for 163 client auth and will fail if '--tls-pem' and 164 '--tls-key' are empty or not specified. 165 --tls-ver <version> Use the TLS version specified when using a TLS 166 Connection hint. This will set the version 167 required and can be used by itself. A value of 168 zero means TLSv1. Can be used with other TLS 169 options. 170 --tls-ca <file|pem> Use the provided certificate to verify the server 171 (for clients) or verify clients (for the server). 172 Can be used on it's own and with '--mtls'. 173 This argument can take a file path to a PEM 174 formatted certificate or raw base64 encoded PEM 175 data. 176 --tls-pem <file|pem> Use the provided certificate for the generated 177 TLS socket. This can be used for client or 178 server listeners. Requires '--tls-key'. 179 This argument can take a file path to a PEM 180 formatted certificate or raw base64 encoded PEM 181 data. 182 --tls-key <file|pem> Use the provided certificate key for the generated 183 TLS socket. This can be used for client or 184 server listeners. Requires '--tls-pem'. 185 This argument can take a file path to a PEM 186 formatted certificate private key or a raw 187 base64 encoded PEM private key. 188 189 WRAPPERS (Multiple different types may be used): 190 --hex Use the HEX Wrapper. 191 --zlib Use the Zlib compression Wrapper. 192 --gzip Use the Gzip compression Wrapper. 193 --b64 Use the Base64 encoding Wrapper. 194 --xor [key] Encrypt with the XOR Wrapper using the provided 195 key string. If omitted the key will be a 196 randomly generated 64 byte array. 197 --cbk [key] Encrypt with the XOR Wrapper using the provided 198 key string. If omitted the key will be a 199 randomly seeded ABCD from a 64 byte array. 200 --aes [key] Encrypt with the AES Wrapper using the provided 201 key string. If omitted the key will be a 202 randomly generated 32 byte array. The AES IV 203 may be supplied using the '--aes-iv' argument. 204 If not specified a 16 byte IV will be generated. 205 --aes-iv [iv] Encrypt with the AES Wrapper using the provided 206 IV string. If omitted the IV will be a 207 randomly generated 16 byte array. The AES key 208 may be supplied using the '--aes-key' argument. 209 If not specified a 32 byte key will be generated. 210 211 TRANSFORMS (Max 1 per Profile Group): 212 --b64t [shift] Transform the data using a Base64 Transform. An 213 option shift value [0-255] may be specified, but 214 if omitted will not shift. 215 --dns [domain,*] Use the DNS Packet Transform. optional DNS 216 domain names may be specified (seperated by space) 217 that will be used in the packets. This option may 218 be used more than once to specify more domains. 219 -D [domain,*] 220 221 SELECTOR VALUES 222 last Switch Profile Group ONLY if the last 223 attempt failed, the default setting. 224 random Switch Profile Group in a random order 225 on EVERY connection attempt. 226 round-robin Switch Profile Group in a weighted order 227 on EVERY connection attempt. Affected 228 by Group Weights (lower is better). 229 semi-random Switch Profile Group in a random order 230 dependent on a 25% chance. If the 231 chance fails (75%), do not switch. 232 semi-round-robin Switch Profile Group in a weighted order 233 dependent on a 25% chance. If the chance 234 fails (75%), do not switch. Affected by 235 Group Weights (lower is better). 236 """ 237 238 239 class Cfg: 240 class Const: 241 HOST = 0xA0 242 SLEEP = 0xA1 243 JITTER = 0xA2 244 WEIGHT = 0xA3 245 KEYPIN = 0xA6 246 KILLDATE = 0xA4 247 WORKHOURS = 0xA5 248 SEPARATOR = 0xFA 249 250 LAST_VALID = 0xAA 251 ROUND_ROBIN = 0xAB 252 RANDOM = 0xAC 253 SEMI_ROUND_ROBIN = 0xAD 254 SEMI_RANDOM = 0xAE 255 256 TCP = 0xC0 257 TLS = 0xC1 258 UDP = 0xC2 259 ICMP = 0xC3 260 PIPE = 0xC4 261 TLS_INSECURE = 0xC5 262 263 IP = 0xB0 264 WC2 = 0xB1 265 TLS_EX = 0xB2 266 MTLS = 0xB3 267 TLS_CA = 0xB4 268 TLS_CERT = 0xB5 269 270 HEX = 0xD0 271 ZLIB = 0xD1 272 GZIP = 0xD2 273 B64 = 0xD3 274 XOR = 0xD4 275 CBK = 0xD5 276 AES = 0xD6 277 278 B64T = 0xE0 279 DNS = 0xE1 280 B64S = 0xE2 281 282 NAMES = { 283 HOST: "host", 284 SLEEP: "sleep", 285 JITTER: "jitter", 286 WEIGHT: "weight", 287 KEYPIN: "keypin", 288 KILLDATE: "killdate", 289 WORKHOURS: "workhours", 290 SEPARATOR: "|", 291 LAST_VALID: "select-last", 292 ROUND_ROBIN: "select-round-robin", 293 RANDOM: "select-random", 294 SEMI_ROUND_ROBIN: "select-semi-round-robin", 295 SEMI_RANDOM: "select-semi-random", 296 TCP: "tcp", 297 TLS: "tls", 298 UDP: "udp", 299 ICMP: "icmp", 300 PIPE: "pipe", 301 TLS_INSECURE: "tls-insecure", 302 IP: "ip", 303 WC2: "wc2", 304 TLS_EX: "tls-ex", 305 MTLS: "mtls", 306 TLS_CA: "tls-ca", 307 TLS_CERT: "tls-cert", 308 HEX: "hex", 309 ZLIB: "zlib", 310 GZIP: "gzip", 311 B64: "base64", 312 XOR: "xor", 313 CBK: "cbk", 314 AES: "aes", 315 B64T: "b64t", 316 DNS: "dns", 317 B64S: "b64s", 318 } 319 NAMES_TO_ID = { 320 "host": HOST, 321 "sleep": SLEEP, 322 "jitter": JITTER, 323 "weight": WEIGHT, 324 "keypin": KEYPIN, 325 "killdate": KILLDATE, 326 "workhours": WORKHOURS, 327 "|": SEPARATOR, 328 "select-last": LAST_VALID, 329 "select-round-robin": ROUND_ROBIN, 330 "select-random": RANDOM, 331 "select-semi-round-robin": SEMI_ROUND_ROBIN, 332 "select-semi-random": SEMI_RANDOM, 333 "tcp": TCP, 334 "tls": TLS, 335 "udp": UDP, 336 "icmp": ICMP, 337 "pipe": PIPE, 338 "tls-insecure": TLS_INSECURE, 339 "ip": IP, 340 "wc2": WC2, 341 "tls-ex": TLS_EX, 342 "mtls": MTLS, 343 "tls-ca": TLS_CA, 344 "tls-cert": TLS_CERT, 345 "hex": HEX, 346 "zlib": ZLIB, 347 "gzip": GZIP, 348 "base64": B64, 349 "xor": XOR, 350 "cbk": CBK, 351 "aes": AES, 352 "b64t": B64T, 353 "dns": DNS, 354 "b64s": B64S, 355 } 356 WRAPPERS = ["hex", "zlib", "gzip", "b64", "xor", "aes", "cbk"] 357 358 @staticmethod 359 def as_single(v): 360 if not isinstance(v, int): 361 raise ValueError("single: invalid bit") 362 s = Setting(1) 363 s[0] = v 364 return s 365 366 @staticmethod 367 def host(v): 368 if not Utils.nes(v): 369 raise ValueError("host: invalid name object") 370 f = v.encode("UTF-8") 371 n = len(f) 372 if n > 0xFFFF: 373 n = 0xFFFF 374 s = Setting(3 + n) 375 s[0] = Cfg.Const.HOST 376 s[1] = (n >> 8) & 0xFF 377 s[2] = n & 0xFF 378 for x in range(0, n): 379 s[x + 3] = f[x] 380 del n, f 381 return s 382 383 @staticmethod 384 def sleep(t): 385 if Utils.nes(t): 386 t = Utils.str_to_dur(t) 387 if not isinstance(t, int) or t <= 0: 388 raise ValueError("sleep: invalid duration") 389 s = Setting(9) 390 s[0] = Cfg.Const.SLEEP 391 s[1] = (t >> 56) & 0xFF 392 s[2] = (t >> 48) & 0xFF 393 s[3] = (t >> 40) & 0xFF 394 s[4] = (t >> 32) & 0xFF 395 s[5] = (t >> 24) & 0xFF 396 s[6] = (t >> 16) & 0xFF 397 s[7] = (t >> 8) & 0xFF 398 s[8] = t & 0xFF 399 return s 400 401 @staticmethod 402 def jitter(p): 403 if Utils.nes(p): 404 if "%" in p: 405 p = p.replace("%", "") 406 try: 407 p = int(p) 408 except ValueError: 409 raise ValueError("jitter: invalid percentage") 410 if not isinstance(p, int) or p < 0 or p > 100: 411 raise ValueError("jitter: invalid percentage") 412 s = Setting(2) 413 s[0] = Cfg.Const.JITTER 414 s[1] = p & 0xFF 415 return s 416 417 @staticmethod 418 def weight(p): 419 if Utils.nes(p): 420 try: 421 p = int(p) 422 except ValueError: 423 raise ValueError("weight: invalid value") 424 if not isinstance(p, int) or p < 0 or p > 100: 425 raise ValueError("weight: invalid value") 426 s = Setting(2) 427 s[0] = Cfg.Const.WEIGHT 428 s[1] = p & 0xFF 429 return s 430 431 @staticmethod 432 def wrap_hex(): 433 return Cfg.Const.as_single(Cfg.Const.HEX) 434 435 @staticmethod 436 def wrap_b64(): 437 return Cfg.Const.as_single(Cfg.Const.B64) 438 439 @staticmethod 440 def key_pin(v): 441 if Utils.nes(v): 442 v = Utils.public_key_hash(v) 443 if not isinstance(v, int): 444 raise ValueError("key_pin: invalid hash") 445 s = Setting(5) 446 s[0] = Cfg.Const.KEYPIN 447 s[1] = (v >> 24) & 0xFF 448 s[2] = (v >> 16) & 0xFF 449 s[3] = (v >> 8) & 0xFF 450 s[4] = v & 0xFF 451 return s 452 453 @staticmethod 454 def wrap_zlib(): 455 return Cfg.Const.as_single(Cfg.Const.ZLIB) 456 457 @staticmethod 458 def wrap_gzip(): 459 return Cfg.Const.as_single(Cfg.Const.GZIP) 460 461 @staticmethod 462 def separator(): 463 return Cfg.Const.as_single(Cfg.Const.SEPARATOR) 464 465 @staticmethod 466 def killdate(v): 467 if v is None: 468 t = 0 469 elif isinstance(v, datetime): 470 t = int(v.timestamp()) 471 elif not isinstance(v, str): 472 raise ValueError("killdate: invalid date value type") 473 elif len(v) == 0: 474 t = 0 475 else: 476 t = int(datetime.fromisoformat(v).timestamp()) 477 s = Setting(9) 478 s[0] = Cfg.Const.KILLDATE 479 s[1] = (t >> 56) & 0xFF 480 s[2] = (t >> 48) & 0xFF 481 s[3] = (t >> 40) & 0xFF 482 s[4] = (t >> 32) & 0xFF 483 s[5] = (t >> 24) & 0xFF 484 s[6] = (t >> 16) & 0xFF 485 s[7] = (t >> 8) & 0xFF 486 s[8] = t & 0xFF 487 del t 488 return s 489 490 @staticmethod 491 def connect_ip(p): 492 if not isinstance(p, int) or p <= 0 or p > 0xFF: 493 raise ValueError("ip: invalid protocol") 494 s = Setting(2) 495 s[0] = Cfg.Const.IP 496 s[1] = p & 0xFF 497 return s 498 499 @staticmethod 500 def connect_tcp(): 501 return Cfg.Const.as_single(Cfg.Const.TCP) 502 503 @staticmethod 504 def connect_tls(): 505 return Cfg.Const.as_single(Cfg.Const.TLS) 506 507 @staticmethod 508 def connect_udp(): 509 return Cfg.Const.as_single(Cfg.Const.UDP) 510 511 @staticmethod 512 def connect_icmp(): 513 return Cfg.Const.as_single(Cfg.Const.ICMP) 514 515 @staticmethod 516 def connect_pipe(): 517 return Cfg.Const.as_single(Cfg.Const.PIPE) 518 519 @staticmethod 520 def transform_b64(): 521 return Cfg.Const.as_single(Cfg.Const.B64T) 522 523 @staticmethod 524 def transform_dns(n): 525 if not isinstance(n, list): 526 raise ValueError("dns: invalid names list") 527 s = Setting(2) 528 s[0] = Cfg.Const.DNS 529 s[1] = len(n) & 0xFF 530 for i in n: 531 if not Utils.nes(i): 532 raise ValueError("dns: invalid name value") 533 if len(i) > 0xFF: 534 i = i[:0xFF] 535 s.append(len(i) & 0xFF) 536 s += i.encode("UTF-8") 537 return s 538 539 @staticmethod 540 def selector_random(): 541 return Cfg.Const.as_single(Cfg.Const.RANDOM) 542 543 @staticmethod 544 def connect_tls_ex(v): 545 if not isinstance(v, int) or v <= 0 or v > 0xFF: 546 raise ValueError("tls-ex: invalid version") 547 s = Setting(2) 548 s[0] = Cfg.Const.TLS_EX 549 s[1] = v & 0xFF 550 return s 551 552 @staticmethod 553 def wrap_xor(key=None): 554 if isinstance(key, list) and len(key) > 0: 555 key = key[0] 556 if key is None: 557 key = token_bytes(64) 558 elif Utils.nes(key): 559 key = key.encode("UTF-8") 560 elif not isinstance(key, (bytes, bytearray)): 561 raise ValueError("xor: invalid KEY value") 562 n = len(key) 563 if n > 0xFFFF: 564 n = 0xFFFF 565 s = Setting(3 + n) 566 s[0] = Cfg.Const.XOR 567 s[1] = (n >> 8) & 0xFF 568 s[2] = n & 0xFF 569 for x in range(0, n): 570 s[x + 3] = key[x] 571 del n 572 return s 573 574 @staticmethod 575 def connect_tls_ca(v, ca): 576 if isinstance(ca, (bytes, bytearray)): 577 f = ca 578 elif Utils.nes(ca): 579 f = ca.encode("UTF-8") 580 else: 581 raise ValueError("tls-ca: invalid CA") 582 if not isinstance(v, int) or v <= 0 or v > 0xFF: 583 raise ValueError("tls-ca: invalid version") 584 n = len(f) 585 if n > 0xFFFF: 586 n = 0xFFFF 587 s = Setting(4 + n) 588 s[0] = Cfg.Const.TLS_CA 589 s[1] = v & 0xFF 590 s[2] = (n >> 8) & 0xFF 591 s[3] = n & 0xFF 592 for x in range(0, n): 593 s[x + 4] = f[x] 594 del f, n 595 return s 596 597 @staticmethod 598 def selector_last_valid(): 599 return Cfg.Const.as_single(Cfg.Const.LAST_VALID) 600 601 @staticmethod 602 def transform_b64_shift(v): 603 if not isinstance(v, int) or v <= 0 or v > 0xFF: 604 raise ValueError("base64s: invalid shift") 605 s = Setting(2) 606 s[0] = Cfg.Const.B64S 607 s[1] = v & 0xFF 608 return s 609 610 @staticmethod 611 def selector_round_robin(): 612 return Cfg.Const.as_single(Cfg.Const.ROUND_ROBIN) 613 614 @staticmethod 615 def selector_semi_random(): 616 return Cfg.Const.as_single(Cfg.Const.SEMI_RANDOM) 617 618 @staticmethod 619 def connect_tls_insecure(): 620 return Cfg.Const.as_single(Cfg.Const.TLS_INSECURE) 621 622 @staticmethod 623 def select_semi_round_robin(): 624 return Cfg.Const.as_single(Cfg.Const.SEMI_ROUND_ROBIN) 625 626 @staticmethod 627 def wrap_aes(key=None, iv=None): 628 if isinstance(iv, list) and len(iv) > 0: 629 iv = iv[0] 630 if isinstance(key, list) and len(key) > 0: 631 key = key[0] 632 if key is None: 633 key = token_bytes(32) 634 elif Utils.nes(key): 635 key = key.encode("UTF-8") 636 elif not isinstance(key, (bytes, bytearray)): 637 raise ValueError("aes: invalid KEY value") 638 if iv is None: 639 iv = token_bytes(16) 640 elif Utils.nes(iv): 641 iv = iv.encode("UTF-8") 642 elif not isinstance(iv, (bytes, bytearray)): 643 raise ValueError("aes: invalid IV value") 644 if len(key) > 32: 645 raise ValueError("aes: invalid KEY size") 646 if len(iv) != 16: 647 raise ValueError("aes: invalid IV size") 648 s = Setting(3 + len(key) + len(iv)) 649 s[0] = Cfg.Const.AES 650 s[1] = len(key) & 0xFF 651 s[2] = len(iv) & 0xFF 652 for x in range(0, len(key)): 653 s[x + 3] = key[x] 654 for x in range(0, len(iv)): 655 s[x + len(key) + 3] = iv[x] 656 return s 657 658 @staticmethod 659 def workhours(days, start, end): 660 if not Utils.nes(days) and not Utils.nes(start) and not Utils.nes(end): 661 raise ValueError("workhours: empty values specified") 662 d = 0 663 if Utils.nes(days): 664 d = Utils.parse_weekdays(days) 665 h, j = 0, 0 666 if Utils.nes(start): 667 if ":" not in start: 668 raise ValueError("workhours: invalid start format") 669 x = start.split(":") 670 if len(x) != 2: 671 raise ValueError("workhours: invalid start format") 672 try: 673 h, j = int(x[0]), int(x[1]) 674 except ValueError: 675 raise ValueError("workhours: invalid start format") 676 del x 677 if h > 23 or j > 59: 678 raise ValueError("workhours: invalid start format") 679 n, m = 0, 0 680 if Utils.nes(end): 681 if ":" not in end: 682 raise ValueError("workhours: invalid end format") 683 x = end.split(":") 684 if len(x) != 2: 685 raise ValueError("workhours: invalid end format") 686 try: 687 n, m = int(x[0]), int(x[1]) 688 except ValueError: 689 raise ValueError("workhours: invalid end format") 690 del x 691 if n > 23 or m > 59: 692 raise ValueError("workhours: invalid end format") 693 s = Setting(6) 694 s[0] = Cfg.Const.WORKHOURS 695 s[1] = d 696 s[2] = h 697 s[3] = j 698 s[4] = n 699 s[5] = m 700 del d, h, j, n, m 701 return s 702 703 @staticmethod 704 def connect_mtls(v, ca, pem, key): 705 if isinstance(ca, (bytes, bytearray)): 706 f = ca 707 elif Utils.nes(ca): 708 f = ca.encode("UTF-8") 709 else: 710 raise ValueError("mtls: invalid CA") 711 if isinstance(pem, (bytes, bytearray)): 712 if len(pem) == 0: 713 raise ValueError("mtls: invalid PEM") 714 p = pem 715 elif Utils.nes(pem): 716 p = pem.encode("UTF-8") 717 else: 718 raise ValueError("mtls: invalid PEM") 719 if isinstance(key, (bytes, bytearray)): 720 if len(key) == 0: 721 raise ValueError("mtls: invalid KEY") 722 k = key 723 elif Utils.nes(key): 724 k = key.encode("UTF-8") 725 else: 726 raise ValueError("mtls: invalid KEY") 727 if not isinstance(v, int) or v <= 0 or v > 0xFF: 728 raise ValueError("mtls invalid version") 729 if len(p) == 0 or len(k) == 0: 730 raise ValueError("mtls: invalid PEM or KEY version") 731 o = len(f) 732 if o > 0xFFFF: 733 o = 0xFFFF 734 n = len(p) 735 if n > 0xFFFF: 736 n = 0xFFFF 737 m = len(k) 738 if m > 0xFFFF: 739 m = 0xFFFF 740 s = Setting(8 + o + n + m) 741 s[0] = Cfg.Const.MTLS 742 s[1] = v & 0xFF 743 s[2] = (o >> 8) & 0xFF 744 s[3] = o & 0xFF 745 s[2] = (n >> 8) & 0xFF 746 s[3] = n & 0xFF 747 s[4] = (m >> 8) & 0xFF 748 s[5] = m & 0xFF 749 for x in range(0, n): 750 s[x + 8] = f[x] 751 for x in range(0, n): 752 s[x + o + 8] = p[x] 753 for x in range(0, m): 754 s[x + o + n + 8] = k[x] 755 del f, p, k, o, n, m 756 return s 757 758 @staticmethod 759 def connect_tls_cert(v, pem, key): 760 if isinstance(pem, (bytes, bytearray)): 761 if len(pem) == 0: 762 raise ValueError("tls-cert: invalid PEM") 763 p = pem 764 elif Utils.nes(pem): 765 p = pem.encode("UTF-8") 766 else: 767 raise ValueError("tls-cert: invalid PEM") 768 if isinstance(key, (bytes, bytearray)): 769 if len(key) == 0: 770 raise ValueError("tls-cert: invalid KEY") 771 k = key 772 elif Utils.nes(key): 773 k = key.encode("UTF-8") 774 else: 775 raise ValueError("tls-cert: invalid KEY") 776 if not isinstance(v, int) or v <= 0 or v > 0xFF: 777 raise ValueError("tls-cert: invalid version") 778 if len(p) == 0 or len(k) == 0: 779 raise ValueError("tls-cert: invalid PEM or KEY version") 780 n = len(p) 781 if n > 0xFFFF: 782 n = 0xFFFF 783 m = len(k) 784 if m > 0xFFFF: 785 m = 0xFFFF 786 s = Setting(6 + n + m) 787 s[0] = Cfg.Const.TLS_CA 788 s[1] = v & 0xFF 789 s[2] = (n >> 8) & 0xFF 790 s[3] = n & 0xFF 791 s[4] = (m >> 8) & 0xFF 792 s[5] = m & 0xFF 793 for x in range(0, n): 794 s[x + 6] = p[x] 795 for x in range(0, m): 796 s[x + n + 6] = k[x] 797 del p, k, n, m 798 return s 799 800 @staticmethod 801 def connect_wc2(u, h, a, head=None): 802 if Utils.nes(u): 803 c = u.encode("UTF-8") 804 else: 805 c = bytearray() 806 if Utils.nes(h): 807 v = h.encode("UTF-8") 808 else: 809 v = bytearray() 810 if Utils.nes(a): 811 b = a.encode("UTF-8") 812 else: 813 b = bytearray() 814 j = len(c) 815 if j > 0xFFFF: 816 j = 0xFFFF 817 k = len(v) 818 if k > 0xFFFF: 819 k = 0xFFFF 820 n = len(b) 821 if n > 0xFFFF: 822 n = 0xFFFF 823 s = Setting(8 + j + k + n) 824 s[0] = Cfg.Const.WC2 825 s[1] = (j >> 8) & 0xFF 826 s[2] = j & 0xFF 827 s[3] = (k >> 8) & 0xFF 828 s[4] = k & 0xFF 829 s[5] = (n >> 8) & 0xFF 830 s[6] = n & 0xFF 831 for x in range(0, j): 832 s[x + 8] = c[x] 833 for x in range(0, k): 834 s[x + j + 8] = v[x] 835 for x in range(0, n): 836 s[x + j + k + 8] = b[x] 837 del j, k, n, c, v, b 838 if not isinstance(head, dict): 839 s[7] = 0 840 return s 841 i = 0 842 s[7] = len(head) & 0xFF 843 for k, v in head.items(): 844 if i >= 0xFF: 845 break 846 if not Utils.nes(k): 847 raise ValueError("wc2: invalid header") 848 if Utils.nes(v): 849 z = v.encode("UTF-8") 850 else: 851 z = bytearray() 852 o = k.encode("UTF-8") 853 f = len(o) 854 if f > 0xFF: 855 f = 0xFF 856 g = len(z) 857 if g > 0xFF: 858 g = 0xFF 859 s.append(f & 0xFF) 860 s.append(g & 0xFF) 861 s.extend(o) 862 s.extend(z) 863 i += 1 864 del o, z, f, g 865 return s 866 867 @staticmethod 868 def wrap_cbk(a=None, b=None, c=None, d=None, size=128, key=None): 869 if ( 870 not isinstance(a, int) 871 and not isinstance(b, int) 872 and not isinstance(c, int) 873 and not isinstance(d, int) 874 ): 875 if Utils.nes(key): 876 v = key.encode("UTF-8") 877 elif isinstance(key, (bytes, bytearray)): 878 v = key 879 else: 880 v = token_bytes(64) 881 if len(v) == 0: 882 v - token_bytes(64) 883 h = sha512() 884 for _ in range(0, 256): 885 h.update(v) 886 del v 887 n = crc32(h.digest()).to_bytes(4, byteorder="big", signed=False) 888 del h 889 a = n[0] 890 b = n[1] 891 c = n[2] 892 d = n[3] 893 del n 894 if ( 895 not isinstance(a, int) 896 or not isinstance(b, int) 897 or not isinstance(c, int) 898 or not isinstance(d, int) 899 or a < 0 900 or a > 0xFF 901 or b < 0 902 or b > 0xFF 903 or c < 0 904 or c > 0xFF 905 or d < 0 906 or d > 0xFF 907 ): 908 raise ValueError("cbk: invalid ABCD keys") 909 if not isinstance(size, int) or size not in [16, 32, 64, 128]: 910 raise ValueError("cbk: invalid size") 911 s = Setting(6) 912 s[0] = Cfg.Const.CBK 913 s[1] = size & 0xFF 914 s[2] = a 915 s[3] = b 916 s[4] = c 917 s[5] = d 918 return s 919 920 921 class Utils: 922 UNITS = { 923 "ns": 1, 924 "us": 1000, 925 "µs": 1000, 926 "μs": 1000, 927 "ms": 1000000, 928 "s": 1000000000, 929 "m": 60000000000, 930 "h": 3600000000000, 931 } 932 933 @staticmethod 934 def dur_to_str(v): 935 b = bytearray(32) 936 n = len(b) - 1 937 b[n] = ord("s") 938 n, v = Utils._fmt_frac(b, n, v) 939 n = Utils._fmt_int(b, n, v % 60) 940 v /= 60 941 if int(v) > 0: 942 n -= 1 943 b[n] = ord("m") 944 n = Utils._fmt_int(b, n, v % 60) 945 v /= 60 946 if int(v) > 0: 947 n -= 1 948 b[n] = ord("h") 949 n = Utils._fmt_int(b, n, v) 950 return b[n:].decode("UTF-8") 951 952 @staticmethod 953 def str_to_dur(s): 954 if not Utils.nes(s): 955 raise ValueError("str2dur: invalid duration") 956 if s == "0": 957 return 0 958 d = 0 959 while len(s) > 0: 960 v = 0 961 f = 0 962 z = 1 963 if not (s[0] == "." or (ord("0") <= ord(s[0]) and ord(s[0]) <= ord("9"))): 964 raise ValueError("str2dur: invalid duration") 965 p = len(s) 966 v, s = Utils._leading_int(s) 967 r = p != len(s) 968 y = False 969 if len(s) > 0 and s[0] == ".": 970 s = s[1:] 971 p = len(s) 972 f, z, s = Utils._leading_fraction(s) 973 y = p != len(s) 974 if not r and not y: 975 raise ValueError("str2dur: invalid duration") 976 del r, y 977 i = 0 978 while i < len(s): 979 c = ord(s[i]) 980 if c == ord(".") or (ord("0") <= c and c <= ord("9")): 981 break 982 i += 1 983 if i == 0: 984 u = "s" 985 else: 986 u = s[:i] 987 s = s[i:] 988 del i 989 if u not in Utils.UNITS: 990 raise ValueError("str2dur: unknown unit") 991 e = Utils.UNITS[u] 992 del u 993 if v > (((1 << 63) - 1) / e): 994 raise ValueError("str2dur: invalid duration") 995 v *= int(e) 996 if f > 0: 997 v += int(float(f) * float(float(e) / float(z))) 998 if v < 0: 999 raise ValueError("str2dur: invalid duration") 1000 del e 1001 d += v 1002 if d < 0: 1003 raise ValueError("str2dur: invalid duration") 1004 del v, f, z 1005 return d 1006 1007 @staticmethod 1008 def to_weekdays(v): 1009 if not isinstance(v, (int, float)) or v == 0 or v > 126: 1010 return "SMTWRFS" 1011 r = "" 1012 if v & 1 != 0: 1013 r += "S" 1014 if v & 2 != 0: 1015 r += "M" 1016 if v & 4 != 0: 1017 r += "T" 1018 if v & 8 != 0: 1019 r += "W" 1020 if v & 16 != 0: 1021 r += "R" 1022 if v & 32 != 0: 1023 r += "F" 1024 if v & 64 != 0: 1025 r += "S" 1026 return r 1027 1028 @staticmethod 1029 def _leading_int(s): 1030 i = 0 1031 x = 0 1032 while i < len(s): 1033 c = ord(s[i]) 1034 if c < ord("0") or c > ord("9"): 1035 break 1036 if x > (((1 << 63) - 1) / 10): 1037 raise OverflowError() 1038 x = int(x * 10) + int(c) - ord("0") 1039 if x < 0: 1040 raise OverflowError() 1041 i += 1 1042 return x, s[i:] 1043 1044 @staticmethod 1045 def parse_weekdays(v): 1046 if not Utils.nes(v): 1047 return 0 1048 d = 0 1049 for x in range(0, len(v)): 1050 if v[x] == "s" or v[x] == "S": 1051 if x == 0: 1052 d |= 1 1053 else: 1054 d |= 64 1055 elif v[x] == "m" or v[x] == "M": 1056 d |= 2 1057 elif v[x] == "t" or v[x] == "T": 1058 d |= 4 1059 elif v[x] == "w" or v[x] == "W": 1060 d |= 8 1061 elif v[x] == "r" or v[x] == "R": 1062 d |= 16 1063 elif v[x] == "f" or v[x] == "F": 1064 d |= 32 1065 else: 1066 raise ValueError("bad weekday char") 1067 return d 1068 1069 @staticmethod 1070 def _fmt_int(b, s, v): 1071 if int(v) == 0: 1072 s -= 1 1073 b[s] = ord("0") 1074 return s 1075 while int(v) > 0: 1076 s -= 1 1077 b[s] = int(v % 10) + ord("0") 1078 v /= 10 1079 return s 1080 1081 @staticmethod 1082 def split_dns_names(v): 1083 n = list() 1084 for e in v: 1085 if not isinstance(e, list): 1086 raise ValueError("dns: invalid argument") 1087 if len(e) == 0: 1088 continue 1089 for s in e: 1090 if not Utils.nes(s): 1091 raise ValueError("dns: invalid value") 1092 if "," not in s: 1093 n.append(s) 1094 continue 1095 for x in s.split(","): 1096 h = x.strip() 1097 if len(h) > 0: 1098 n.append(h) 1099 del h 1100 return n 1101 1102 @staticmethod 1103 def _fmt_frac(b, s, v): 1104 p = False 1105 for _ in range(0, 9): 1106 d = v % 10 1107 p = p or d != 0 1108 if p: 1109 s -= 1 1110 b[s] = int(d) + ord("0") 1111 v /= 10 1112 if p: 1113 s -= 1 1114 b[s] = ord(".") 1115 del p 1116 return s, v 1117 1118 @staticmethod 1119 def read_file_input(v): 1120 if v.strip() == "-" and not stdin.isatty(): 1121 if hasattr(stdin, "buffer"): 1122 b = stdin.buffer.read() 1123 else: 1124 b = stdin.read() 1125 stdin.close() 1126 else: 1127 with open(v, "rb") as f: 1128 b = f.read() 1129 if len(b) == 0: 1130 raise ValueError("input: empty input data") 1131 return Config(b) 1132 1133 @staticmethod 1134 def public_key_hash(s): 1135 if ":" not in s: 1136 return int(s, 16) 1137 if (len(s) + 1) % 3 != 0: 1138 raise ValueError("public_key_hash: invalid padded key length") 1139 v, h = s.split(":"), 2166136261 1140 for i in v: 1141 if len(i) != 2: 1142 raise ValueError(f'public_key_hash: invalid key entry "{i}"') 1143 x = int(i, 16) 1144 h *= 16777619 1145 h = h & 0xFFFFFFFF 1146 h ^= x 1147 h = h & 0xFFFFFFFF 1148 del x 1149 del v 1150 return h 1151 1152 @staticmethod 1153 def _leading_fraction(s): 1154 i = 0 1155 x = 0 1156 v = 1 1157 o = False 1158 while i < len(s): 1159 c = ord(s[i]) 1160 if c < ord("0") or c > ord("9"): 1161 break 1162 if o: 1163 continue 1164 if x > (((1 << 63) - 1) / 10): 1165 o = True 1166 continue 1167 y = int(x * 10) + int(c) - ord("0") 1168 if y < 0: 1169 o = True 1170 continue 1171 x = y 1172 v *= 10 1173 i += 1 1174 del y 1175 del o 1176 return x, v, s[1:] 1177 1178 @staticmethod 1179 def parse_wc2_headers(v): 1180 if not isinstance(v, list) or len(v) == 0: 1181 return None 1182 d = dict() 1183 for e in v: 1184 Utils._parse_wc2_header(d, e, False) 1185 if len(d) == 0: 1186 return None 1187 return d 1188 1189 @staticmethod 1190 def nes(s, min=0, max=-1): 1191 if max > min: 1192 return isinstance(s, str) and len(s) < max and len(s) > min 1193 return isinstance(s, str) and len(s) > min 1194 1195 @staticmethod 1196 def _parse_wc2_header(d, e, r): 1197 if isinstance(e, str): 1198 if len(e) == 0 or "=" not in e: 1199 raise ValueError("wc2: invalid header") 1200 p = e.find("=") 1201 if p == 0 or p == len(e) - 1: 1202 raise ValueError("wc2: empty header") 1203 d[e[:p].strip()] = e[p + 1 :].strip() 1204 return 1205 if isinstance(e, list) and len(e) > 0: 1206 if r: 1207 raise ValueError("wc2: too many nested lists") 1208 for v in e: 1209 Utils._parse_wc2_header(d, v, True) 1210 return 1211 raise ValueError("wc2: Invalid header") 1212 1213 @staticmethod 1214 def parse_tls(ca, pem, key, mtls, ver): 1215 a = None 1216 p = None 1217 k = None 1218 if isinstance(ca, str) and len(ca) > 0: 1219 try: 1220 a = b64decode(ca, validate=True) 1221 except ValueError: 1222 with open(ca, "rb") as f: 1223 a = f.read() 1224 if isinstance(pem, str) and len(pem) > 0: 1225 try: 1226 a = b64decode(pem, validate=True) 1227 except ValueError: 1228 with open(pem, "rb") as f: 1229 p = f.read() 1230 if isinstance(key, str) and len(key) > 0: 1231 try: 1232 a = b64decode(key, validate=True) 1233 except ValueError: 1234 with open(key, "rb") as f: 1235 k = f.read() 1236 if mtls and (p is None or k is None or a is None): 1237 raise ValueError("mtls: CA, PEM and KEY must be provided") 1238 if (p is not None and k is None) or (k is not None and p is None): 1239 raise ValueError("tls-cert: PEM and KEY must be provided") 1240 if not isinstance(ver, int): 1241 ver = 0 1242 if a is None and p is None and k is None: 1243 return Cfg.connect_tls_ex(ver) 1244 if a is not None and p is None and k is None: 1245 return Cfg.connect_tls_ca(ver, a) 1246 if a is None: 1247 return Cfg.connect_tls_certs(ver, p, k) 1248 return Cfg.connect_mtls(ver, a, p, k) 1249 1250 @staticmethod 1251 def write_file_output(c, v, pretty, json): 1252 f = stdout 1253 if Utils.nes(v) and v != "-": 1254 if not pretty and not json: 1255 f = open(v, "wb") 1256 else: 1257 f = open(v, "w") 1258 try: 1259 if pretty or json: 1260 return print( 1261 dumps(c.json(), sort_keys=False, indent=(4 if pretty else None)), 1262 file=f, 1263 ) 1264 if f == stdout and not f.isatty(): 1265 return f.buffer.write(c) 1266 if f.mode == "wb": 1267 return f.write(c) 1268 f.write(b64encode(c).decode("UTF-8")) 1269 finally: 1270 if f == stdout: 1271 print(end="") 1272 else: 1273 f.close() 1274 del f 1275 1276 1277 class Config(bytearray): 1278 __slots__ = ("_connector", "_transform") 1279 1280 def __init__(self, b=None): 1281 self._connector = False 1282 self._transform = False 1283 if isinstance(b, str) and len(b) > 0: 1284 if b[0] == "[" and b[-1].strip() == "]": 1285 return self.parse(b) 1286 return self.extend(b64decode(b, validate=True)) 1287 if isinstance(b, (bytes, bytearray)) and len(b) > 0: 1288 if b[0] == 91 and b.decode("UTF-8", "ignore").strip()[-1] == "]": 1289 return self.parse(b.decode("UTF-8")) 1290 return self.extend(b) 1291 1292 def json(self): 1293 i = 0 1294 n = 0 1295 e = list() 1296 r = list() 1297 while n >= 0 and n < len(self): 1298 n = self.next(i) 1299 if self[i] not in Cfg.Const.NAMES: 1300 raise ValueError(f"json: invalid setting id {self[i]}") 1301 if self[i] == Cfg.Const.SEPARATOR: 1302 i = n 1303 if len(e) == 0: 1304 i = n 1305 continue 1306 if n == len(self): 1307 break 1308 r.append(e) 1309 e = list() 1310 continue 1311 o = None 1312 if Setting.is_single(self[i]): 1313 pass 1314 elif self[i] == Cfg.Const.HOST: 1315 if i + 3 >= n: 1316 raise ValueError("host: invalid setting") 1317 v = (int(self[i + 2]) | int(self[i + 1]) << 8) + i 1318 if v > n or v < i: 1319 raise ValueError("host: invalid setting") 1320 o = self[i + 3 : v + 3].decode("UTF-8") 1321 del v 1322 elif self[i] == Cfg.Const.SLEEP: 1323 if i + 8 >= n: 1324 raise ValueError("sleep: invalid setting") 1325 o = Utils.dur_to_str( 1326 ( 1327 int(self[i + 8]) 1328 | int(self[i + 7]) << 8 1329 | int(self[i + 6]) << 16 1330 | int(self[i + 5]) << 24 1331 | int(self[i + 4]) << 32 1332 | int(self[i + 3]) << 40 1333 | int(self[i + 2]) << 48 1334 | int(self[i + 1]) << 56 1335 ) 1336 ) 1337 elif self[i] == Cfg.Const.KILLDATE: 1338 if i + 8 >= n: 1339 raise ValueError("killdate: invalid setting") 1340 u = ( 1341 int(self[i + 8]) 1342 | int(self[i + 7]) << 8 1343 | int(self[i + 6]) << 16 1344 | int(self[i + 5]) << 24 1345 | int(self[i + 4]) << 32 1346 | int(self[i + 3]) << 40 1347 | int(self[i + 2]) << 48 1348 | int(self[i + 1]) << 56 1349 ) 1350 if u == 0: 1351 o = "" 1352 else: 1353 o = datetime.fromtimestamp(u).isoformat() 1354 elif self[i] == Cfg.Const.KEYPIN: 1355 u = ( 1356 int(self[i + 4]) 1357 | int(self[i + 3]) << 8 1358 | int(self[i + 2]) << 16 1359 | int(self[i + 1]) << 24 1360 ) 1361 o = hex(u)[2:].upper() 1362 elif self[i] == Cfg.Const.WORKHOURS: 1363 if i + 5 >= n: 1364 raise ValueError("workhours: invalid setting") 1365 o = { 1366 "start_hour": self[i + 2], 1367 "start_min": self[i + 3], 1368 "end_hour": self[i + 4], 1369 "end_min": self[i + 5], 1370 "days": Utils.to_weekdays(self[i + 1]), 1371 } 1372 elif ( 1373 self[i] == Cfg.Const.IP 1374 or self[i] == Cfg.Const.B64S 1375 or self[i] == Cfg.Const.JITTER 1376 or self[i] == Cfg.Const.WEIGHT 1377 or self[i] == Cfg.Const.TLS_EX 1378 ): 1379 if i + 1 >= n: 1380 raise ValueError("invalid setting") 1381 o = int(self[i + 1]) 1382 elif self[i] == Cfg.Const.WC2: 1383 if i + 7 >= n: 1384 raise ValueError("wc2: invalid setting") 1385 z = i + 8 1386 v = (int(self[i + 2]) | int(self[i + 1]) << 8) + i + 8 1387 if v > n or z > n or z < i or v < i: 1388 raise ValueError("wc2: invalid setting") 1389 o = dict() 1390 if v > z: 1391 o["url"] = self[z:v].decode("UTF-8") 1392 z = v 1393 v = (int(self[i + 4]) | int(self[i + 3]) << 8) + v 1394 if v > z: 1395 if v > n or z > n or v < z or z < i or v < i: 1396 raise ValueError("wc2: invalid setting") 1397 o["host"] = self[z:v].decode("UTF-8") 1398 z = v 1399 v = (int(self[i + 6]) | int(self[i + 5]) << 8) + v 1400 if v > z: 1401 if v > n or z > n or v < z or z < i or v < i: 1402 raise ValueError("wc2: invalid setting") 1403 o["agent"] = self[z:v].decode("UTF-8") 1404 if self[i + 7] > 0: 1405 o["headers"] = dict() 1406 j = 0 1407 while v < n and z < n and j < n: 1408 j = int(self[v]) + v + 2 1409 z = v + 2 1410 v = int(self[v + 1]) + j 1411 if ( 1412 z == j 1413 or z > n 1414 or j > n 1415 or v > n 1416 or v < j 1417 or j < z 1418 or z < i 1419 or j < i 1420 or v < i 1421 ): 1422 raise ValueError("wc2: invalid header") 1423 o["headers"][self[z:j].decode("UTF-8")] = self[j:v].decode( 1424 "UTF-8" 1425 ) 1426 del j 1427 del z, v 1428 elif self[i] == Cfg.Const.MTLS: 1429 if i + 7 >= n: 1430 raise ValueError("mtls: invalid setting") 1431 a = (int(self[i + 3]) | int(self[i + 2]) << 8) + i + 8 1432 p = (int(self[i + 5]) | int(self[i + 4]) << 8) + a 1433 k = (int(self[i + 7]) | int(self[i + 6]) << 8) + p 1434 if a > n or p > n or k > n or p < a or k < p or a < i or p < i or k < i: 1435 raise ValueError("mtls: invalid setting") 1436 o = {"version": int(self[i + 1])} 1437 o["ca"] = b64encode(self[i + 8 : a]).decode("UTF-8") 1438 o["pem"] = b64encode(self[a:p]).decode("UTF-8") 1439 o["key"] = b64encode(self[p:k]).decode("UTF-8") 1440 del a, p, k 1441 elif self[i] == Cfg.Const.TLS_CA: 1442 if i + 4 >= n: 1443 raise ValueError("tls-ca: invalid setting") 1444 a = (int(self[i + 3]) | int(self[i + 2]) << 8) + i + 4 1445 if a > n or a < i: 1446 raise ValueError("tls-ca: invalid setting") 1447 o = {"version": int(self[i + 1])} 1448 o["ca"] = b64encode(self[i + 4 : a]).decode("UTF-8") 1449 del a 1450 elif self[i] == Cfg.Const.TLS_CERT: 1451 if i + 6 >= n: 1452 raise ValueError("tls-cert: invalid setting") 1453 p = (int(self[i + 3]) | int(self[i + 2]) << 8) + i + 6 1454 k = (int(self[i + 5]) | int(self[i + 4]) << 8) + p 1455 if p > n or k > n or p < i or k < i or k < p: 1456 raise ValueError("tls-cert: invalid setting") 1457 o = {"version": int(self[i + 1])} 1458 o["pem"] = b64encode(self[i + 6 : p]).decode("UTF-8") 1459 o["key"] = b64encode(self[p:k]).decode("UTF-8") 1460 del p, k 1461 elif self[i] == Cfg.Const.XOR: 1462 if i + 3 >= n: 1463 raise ValueError("xor: invalid setting") 1464 k = (int(self[i + 2]) | int(self[i + 1]) << 8) + i 1465 if k > n or k < i: 1466 raise ValueError("xor: invalid setting") 1467 o = b64encode(self[i + 3 : k + 3]).decode("UTF-8") 1468 del k 1469 elif self[i] == Cfg.Const.CBK: 1470 if i + 5 >= n: 1471 raise ValueError("cbk: invalid setting") 1472 o = { 1473 "size": int(self[i + 1]), 1474 "A": int(self[i + 2]), 1475 "B": int(self[i + 3]), 1476 "C": int(self[i + 4]), 1477 "D": int(self[i + 5]), 1478 } 1479 elif self[i] == Cfg.Const.AES: 1480 if i + 3 >= n: 1481 raise ValueError("aes: invalid setting") 1482 v = int(self[i + 1]) + i + 3 1483 z = int(self[i + 2]) + v 1484 if v == z or i + 3 == v or v > n or z > n or z < i or v < i or z < v: 1485 raise ValueError("aes: invalid KEY/IV values") 1486 o = { 1487 "key": b64encode(self[i + 3 : v]).decode("UTF-8"), 1488 "iv": b64encode(self[v:z]).decode("UTF-8"), 1489 } 1490 del v, z 1491 elif self[i] == Cfg.Const.DNS: 1492 if i + 1 >= n: 1493 raise ValueError("dns: invalid setting") 1494 o = list() 1495 v = i + 2 1496 z = v 1497 for _ in range(self[i + 1], 0, -1): 1498 v += int(self[v]) + 1 1499 if ( 1500 z + 1 > v 1501 or z + 1 == v 1502 or v < z 1503 or v > n 1504 or z > n 1505 or z < i 1506 or v < i 1507 ): 1508 raise ValueError("dns: invalid name") 1509 o.append(self[z + 1 : v].decode("UTF-8")) 1510 z = v 1511 del v, z 1512 y = {"type": Cfg.Const.NAMES[self[i]]} 1513 if o is not None: 1514 y["args"] = o 1515 del o 1516 e.append(y) 1517 i = n 1518 if len(e) > 0: 1519 r.append(e) 1520 return r 1521 1522 def add(self, s): 1523 if not isinstance(s, Setting): 1524 raise ValueError("add: cannot add a non-Settings object") 1525 if not s._is_valid(): 1526 raise ValueError("add: invalid Settings object") 1527 if s[0] == Cfg.Const.SEPARATOR: 1528 self._connector = False 1529 self._transform = False 1530 if s._is_connector(): 1531 if self._connector: 1532 raise ValueError("add: attempted to add multiple Connection hints") 1533 self._connector = True 1534 if s._is_transform(): 1535 if self._transform: 1536 raise ValueError("add: attempted to add multiple Transforms") 1537 self._transform = True 1538 if s.single(): 1539 return self.append(s[0]) 1540 self += s 1541 # for i in s: 1542 # self.append(i) 1543 1544 def __str__(self): 1545 i = 0 1546 n = 0 1547 b = StringIO() 1548 while n >= 0 and n < len(self): 1549 n = self.next(i) 1550 if i > 0: 1551 b.write(",") 1552 b.write(Setting(self[i:n]).__str__()) 1553 i = n 1554 s = b.getvalue() 1555 b.close() 1556 del b 1557 return s 1558 1559 def read(self, b): 1560 if not isinstance(b, (bytes, bytearray)): 1561 raise ValueError("read: invalid raw type") 1562 self.extend(b) 1563 1564 def next(self, i): 1565 if i > len(self) or i < 0: 1566 return -1 1567 if Setting.is_single(self[i]): 1568 return i + 1 1569 if ( 1570 self[i] == Cfg.Const.IP 1571 or self[i] == Cfg.Const.B64S 1572 or self[i] == Cfg.Const.JITTER 1573 or self[i] == Cfg.Const.WEIGHT 1574 or self[i] == Cfg.Const.TLS_EX 1575 ): 1576 return i + 2 1577 if self[i] == Cfg.Const.CBK or self[i] == Cfg.Const.WORKHOURS: 1578 return i + 6 1579 if self[i] == Cfg.Const.SLEEP or self[i] == Cfg.Const.KILLDATE: 1580 return i + 9 1581 if self[i] == Cfg.Const.KEYPIN: 1582 return i + 5 1583 if self[i] == Cfg.Const.WC2: 1584 if i + 7 >= len(self): 1585 return -1 1586 n = ( 1587 i 1588 + 8 1589 + (int(self[i + 2]) | int(self[i + 1]) << 8) 1590 + (int(self[i + 4]) | int(self[i + 3]) << 8) 1591 + (int(self[i + 6]) | int(self[i + 5]) << 8) 1592 ) 1593 if self[i + 7] == 0 or n >= len(self): 1594 return n 1595 for _ in range(self[i + 7], 0, -1): 1596 if n >= len(self) or n < 0: 1597 return -1 1598 n += int(self[n]) + int(self[n + 1]) + 2 1599 return n 1600 if self[i] == Cfg.Const.XOR or self[i] == Cfg.Const.HOST: 1601 if i + 3 >= len(self): 1602 return -1 1603 return i + 3 + int(self[i + 2]) | int(self[i + 1]) << 8 1604 if self[i] == Cfg.Const.AES: 1605 if i + 2 >= len(self): 1606 return -1 1607 return i + 3 + int(self[i + 1]) + int(self[i + 2]) 1608 if self[i] == Cfg.Const.MTLS: 1609 if i + 7 >= len(self): 1610 return -1 1611 return ( 1612 i 1613 + 8 1614 + (int(self[i + 3]) | int(self[i + 2]) << 8) 1615 + (int(self[i + 5]) | int(self[i + 4]) << 8) 1616 + (int(self[i + 7]) | int(self[i + 6]) << 8) 1617 ) 1618 if self[i] == Cfg.Const.TLS_CA: 1619 if i + 4 >= len(self): 1620 return -1 1621 return i + 4 + int(self[i + 3]) | int(self[i + 2]) << 8 1622 if self[i] == Cfg.Const.TLS_CERT: 1623 if i + 6 >= len(self): 1624 return -1 1625 return ( 1626 i 1627 + 6 1628 + (int(self[i + 3]) | int(self[i + 2]) << 8) 1629 + (int(self[i + 5]) | int(self[i + 4]) << 8) 1630 ) 1631 if self[i] == Cfg.Const.DNS: 1632 if i + 1 >= len(self): 1633 return -1 1634 n = i + 2 1635 for _ in range(self[i + 1], 0, -1): 1636 n += int(self[n]) + 1 1637 return n 1638 return -1 1639 1640 def parse(self, j): 1641 v = loads(j) 1642 if not isinstance(v, list): 1643 raise ValueError("parse: invalid JSON value") 1644 if len(v) == 0: 1645 return 1646 for x in range(0, len(v)): 1647 if not isinstance(v[x], list): 1648 raise ValueError("parse: invalid JSON value") 1649 for e in v[x]: 1650 self._parse_inner(e) 1651 if x + 1 < len(v): 1652 self.add(Cfg.separator()) 1653 del v 1654 1655 @staticmethod 1656 def from_file(file): 1657 with open(file, "rb") as f: 1658 return Config(f.read()) 1659 1660 def _parse_inner(self, x): 1661 if not isinstance(x, dict) or len(x) == 0: 1662 raise ValueError("parse: invalid JSON value") 1663 if "type" not in x or x["type"].lower() not in Cfg.Const.NAMES_TO_ID: 1664 raise ValueError("parse: invalid JSON value") 1665 m = Cfg.Const.NAMES_TO_ID[x["type"].lower()] 1666 if m == Cfg.Const.SEPARATOR: 1667 raise ValueError("parse: unexpected separator") 1668 if Setting.is_single(m): 1669 return self.add(Cfg.Const.as_single(m)) 1670 if "args" not in x: 1671 raise ValueError("parse: invalid JSON payload") 1672 p = x["args"] 1673 if m == Cfg.Const.HOST: 1674 if not Utils.nes(p): 1675 raise ValueError("host: invalid JSON value") 1676 return self.add(Cfg.host(p)) 1677 if m == Cfg.Const.SLEEP: 1678 if not Utils.nes(p): 1679 raise ValueError("sleep: invalid JSON value") 1680 return self.add(Cfg.sleep(p)) 1681 if m == Cfg.Const.JITTER: 1682 if not isinstance(p, int): 1683 raise ValueError("jitter: invalid JSON value") 1684 if p < 0 or p > 100: 1685 raise ValueError("jitter: invalid JSON value") 1686 return self.add(Cfg.jitter(p)) 1687 if m == Cfg.Const.WEIGHT: 1688 if not isinstance(p, int): 1689 raise ValueError("weight: invalid JSON value") 1690 if p < 0: 1691 raise ValueError("weight: invalid JSON value") 1692 return self.add(Cfg.weight(p)) 1693 if m == Cfg.Const.KILLDATE: 1694 if not Utils.nes(p): 1695 raise ValueError("killdate: invalid JSON value") 1696 return self.add(Cfg.killdate(p)) 1697 if m == Cfg.Const.KEYPIN: 1698 if not Utils.nes(p): 1699 raise ValueError("keypin: invalid JSON value") 1700 return self.add(Cfg.key_pin(int(p, 16))) 1701 if m == Cfg.Const.WORKHOURS: 1702 if not isinstance(p, dict): 1703 raise ValueError("workhours: invalid JSON value") 1704 h, j = p.get("start_hour"), p.get("start_min") 1705 n, m = p.get("start_hour"), p.get("start_min") 1706 if h is None: 1707 h = 0 1708 elif not isinstance(h, (int, float)): 1709 raise ValueError("workhours: invalid JSON value") 1710 if j is None: 1711 j = 0 1712 elif not isinstance(j, (int, float)): 1713 raise ValueError("workhours: invalid JSON value") 1714 if n is None: 1715 m = 0 1716 elif not isinstance(n, (int, float)): 1717 raise ValueError("workhours: invalid JSON value") 1718 if m is None: 1719 m = 0 1720 elif not isinstance(m, (int, float)): 1721 raise ValueError("workhours: invalid JSON value") 1722 if h > 23 or j > 59 or n > 23 or m > 59: 1723 raise ValueError("workhours: invalid JSON value") 1724 d = Utils.parse_weekdays(p.get("days", "")) 1725 s = Setting(6) 1726 s[0] = Cfg.Const.WORKHOURS 1727 s[1] = d 1728 s[2] = h 1729 s[3] = j 1730 s[4] = n 1731 s[5] = m 1732 del d, h, j, n, m 1733 return self.add(s) 1734 if m == Cfg.Const.IP: 1735 if not isinstance(p, int): 1736 raise ValueError("ip: invalid JSON value") 1737 if p < 0: 1738 raise ValueError("ip: invalid JSON value") 1739 return self.add(Cfg.connect_ip(p)) 1740 if m == Cfg.Const.WC2: 1741 if not isinstance(p, dict): 1742 raise ValueError("wc2: invalid JSON value") 1743 u = p.get("url") 1744 h = p.get("host") 1745 a = p.get("agent") 1746 j = p.get("headers") 1747 if j is not None and not isinstance(j, dict): 1748 raise ValueError("wc2: invalid JSON header value") 1749 self.add(Cfg.connect_wc2(u, h, a, j)) 1750 del u, h, a, j 1751 return 1752 if m == Cfg.Const.TLS_EX: 1753 if not isinstance(p, int) and p > 0: 1754 raise ValueError("tls-ex: invalid JSON value") 1755 if p < 0: 1756 raise ValueError("tls-ex: invalid JSON value") 1757 return self.add(Cfg.connect_tls_ex(p)) 1758 if m == Cfg.Const.MTLS: 1759 if not isinstance(p, dict): 1760 raise ValueError("mtls: invalid JSON value") 1761 a = p.get("ca") 1762 y = p.get("pem") 1763 k = p.get("key") 1764 n = p.get("version", 0) 1765 if not Utils.nes(y) or not Utils.nes(k): 1766 raise ValueError("mtls: invalid JSON PEM/KEY values") 1767 if n is not None and not isinstance(n, int): 1768 raise ValueError("mtls: invalid JSON version value") 1769 self.add( 1770 Cfg.connect_mtls( 1771 n, 1772 b64decode(a, validate=True), 1773 b64decode(y, validate=True), 1774 b64decode(k, validate=True), 1775 ) 1776 ) 1777 del a, y, k, n 1778 return 1779 if m == Cfg.Const.TLS_CA: 1780 if not isinstance(p, dict): 1781 raise ValueError("tls-ca: invalid JSON value") 1782 a = p.get("ca") 1783 n = p.get("version", 0) 1784 if n is not None and not isinstance(n, int): 1785 raise ValueError("tls-ca: invalid JSON version value") 1786 self.add(Cfg.connect_tls_ca(n, b64decode(a, validate=True))) 1787 del a, n 1788 return 1789 if m == Cfg.Const.TLS_CERT: 1790 if not isinstance(p, dict): 1791 raise ValueError("tls-cert: invalid JSON value") 1792 y = p.get("pem") 1793 k = p.get("key") 1794 n = p.get("version", 0) 1795 if not Utils.nes(y) or not Utils.nes(k): 1796 raise ValueError("tls-cert: invalid JSON PEM/KEY values") 1797 if n is not None and not isinstance(n, int): 1798 raise ValueError("tls-cert: invalid JSON version value") 1799 self.add( 1800 Cfg.connect_tls_certs( 1801 n, b64decode(y, validate=True), b64decode(k, validate=True) 1802 ) 1803 ) 1804 del y, k, n 1805 return 1806 if m == Cfg.Const.XOR: 1807 if not Utils.nes(p): 1808 raise ValueError("xor: invalid JSON value") 1809 return self.add(Cfg.wrap_xor(b64decode(p, validate=True))) 1810 if m == Cfg.Const.AES: 1811 if not isinstance(p, dict): 1812 raise ValueError("aes: invalid JSON value") 1813 y = p.get("iv") 1814 k = p.get("key") 1815 if not Utils.nes(y) or not Utils.nes(k): 1816 raise ValueError("aes: invalid JSON KEY/IV values") 1817 self.add( 1818 Cfg.wrap_aes(b64decode(k, validate=True), b64decode(y, validate=True)) 1819 ) 1820 del y, k 1821 return 1822 if m == Cfg.Const.CBK: 1823 if not isinstance(p, dict): 1824 raise ValueError("aes: invalid JSON value") 1825 A = p.get("A") 1826 B = p.get("B") 1827 C = p.get("C") 1828 D = p.get("D") 1829 z = p.get("size", 128) 1830 if not isinstance(A, int): 1831 raise ValueError("cbk: invalid JSON A value") 1832 if not isinstance(B, int): 1833 raise ValueError("cbk: invalid JSON B value") 1834 if not isinstance(C, int): 1835 raise ValueError("cbk: invalid JSON C value") 1836 if not isinstance(D, int): 1837 raise ValueError("cbk: invalid JSON D value") 1838 self.add(Cfg.wrap_cbk(a=A, b=B, c=C, d=D, size=z)) 1839 del z, A, B, C, D 1840 return 1841 if m == Cfg.Const.DNS: 1842 if not isinstance(p, list): # or len(p) == 0: Omit to allow empty DNS 1843 raise ValueError("dns: invalid JSON value") 1844 return self.add(Cfg.transform_dns(p)) 1845 if m == Cfg.Const.B64S: 1846 if not isinstance(p, int): 1847 raise ValueError("b64s: invalid JSON value") 1848 if p < 0: 1849 raise ValueError("b64s: invalid JSON value") 1850 self.add(Cfg.transform_b64_shift(p)) 1851 del p 1852 return 1853 raise ValueError(f'unhandled value type: {x["type"].lower()}') 1854 1855 1856 class Setting(bytearray): 1857 def __str__(self): 1858 if len(self) == 0 or self[0] == 0: 1859 return "<invalid>" 1860 if self[0] not in Cfg.Const.NAMES: 1861 return "<invalid>" 1862 return Cfg.Const.NAMES[self[0]] 1863 1864 @staticmethod 1865 def is_single(v): 1866 if v == 0: 1867 return False 1868 if v == Cfg.Const.B64T or v == Cfg.Const.SEPARATOR: 1869 return True 1870 if v >= Cfg.Const.LAST_VALID and v <= Cfg.Const.SEMI_RANDOM: 1871 return True 1872 if v >= Cfg.Const.TCP and v <= Cfg.Const.TLS_INSECURE: 1873 return True 1874 if v >= Cfg.Const.HEX and v <= Cfg.Const.B64: 1875 return True 1876 return False 1877 1878 def single(self): 1879 if len(self) == 0 or self[0] == 0: 1880 return False 1881 return Setting.is_single(self[0]) 1882 1883 def _is_valid(self): 1884 return len(self) > 0 and self[0] > 0 1885 1886 def _is_connector(self): 1887 if len(self) == 0 or self[0] == 0: 1888 return False 1889 if self[0] >= Cfg.Const.IP and self[0] <= Cfg.Const.TLS_CERT: 1890 return True 1891 return self[0] >= Cfg.Const.TCP and self[0] <= Cfg.Const.TLS_INSECURE 1892 1893 def _is_transform(self): 1894 if len(self) == 0 or self[0] == 0: 1895 return False 1896 return self[0] >= Cfg.Const.B64T and self[0] <= Cfg.Const.B64S 1897 1898 1899 class _Builder(ArgumentParser): 1900 def __init__(self): 1901 ArgumentParser.__init__(self, description="XMT c2.Config Tool") 1902 self.add_argument( 1903 nargs="?", 1904 dest="action", 1905 default="", 1906 metavar="action", 1907 choices=["", "add", "append"], 1908 ) 1909 self.add_argument("-j", "--json", dest="json", action="store_true") 1910 self.add_argument("-p", "--print", dest="print", action="store_true") 1911 self.add_argument("-I", "--stdin", dest="stdin", action="store_true") 1912 1913 self.add_argument("-f", "--in", type=str, dest="input") 1914 self.add_argument("-o", "--out", type=str, dest="output") 1915 1916 self.add_argument("-T", "--host", type=str, dest="host") 1917 self.add_argument("-S", "--sleep", type=str, dest="sleep") 1918 self.add_argument("-J", "--jitter", type=int, dest="jitter") 1919 self.add_argument("-W", "--weight", type=int, dest="weight") 1920 self.add_argument( 1921 "-X", 1922 "--selector", 1923 type=str, 1924 dest="selector", 1925 default=None, 1926 choices=[ 1927 "last", 1928 "random", 1929 "round-robin", 1930 "semi-random", 1931 "semi-round-robin", 1932 ], 1933 ) 1934 1935 self.add_argument("--killdate", type=str, dest="killdate") 1936 self.add_argument("--wh-days", type=str, dest="workhours_days") 1937 self.add_argument("--wh-start", type=str, dest="workhours_start") 1938 self.add_argument("--wh-end", type=str, dest="workhours_end") 1939 self.add_argument( 1940 "-P", 1941 "--keypin", 1942 nargs="+", 1943 type=str, 1944 dest="key_pin", 1945 action="append", 1946 default=None, 1947 ) 1948 1949 c = self.add_mutually_exclusive_group(required=False) 1950 c.add_argument("--tcp", dest="tcp", action="store_true") 1951 c.add_argument("--tls", dest="tls", action="store_true") 1952 c.add_argument("--udp", dest="udp", action="store_true") 1953 c.add_argument("--ip", type=int, dest="ip", default=None) 1954 c.add_argument("--icmp", dest="icmp", action="store_true") 1955 c.add_argument("--pipe", dest="pipe", action="store_true") 1956 c.add_argument("-K", "--tls-insecure", dest="tls_insecure", action="store_true") 1957 del c 1958 1959 self.add_argument("--wc2-url", type=str, dest="wc2_url", default=None) 1960 self.add_argument("--wc2-host", type=str, dest="wc2_host", default=None) 1961 self.add_argument("--wc2-user", type=str, dest="wc2_agent", default=None) 1962 self.add_argument("--wc2-server", dest="wc2_server", action="store_true") 1963 self.add_argument( 1964 "-H", 1965 "--wc2_header", 1966 nargs="+", 1967 type=str, 1968 dest="wc2_headers", 1969 action="append", 1970 default=None, 1971 ) 1972 self.add_argument("--mtls", dest="mtls", action="store_true") 1973 self.add_argument("--tls-ca", type=str, dest="tls_ca", default=None) 1974 self.add_argument("--tls-ver", type=int, dest="tls_ver", default=None) 1975 self.add_argument("--tls-pem", type=str, dest="tls_pem", default=None) 1976 self.add_argument("--tls-key", type=str, dest="tls_key", default=None) 1977 1978 self.add_argument("--hex", dest="hex", action="store_true") 1979 self.add_argument("--b64", dest="b64", action="store_true") 1980 self.add_argument("--zlib", dest="zlib", action="store_true") 1981 self.add_argument("--gzip", dest="gzip", action="store_true") 1982 self.add_argument("--xor", nargs="?", type=str, dest="xor", default=None) 1983 self.add_argument("--cbk", nargs="?", type=str, dest="cbk", default=None) 1984 self.add_argument("--aes", nargs="?", type=str, dest="aes", default=None) 1985 1986 self.add_argument("--aes-iv", nargs="?", type=str, dest="aes_iv", default=None) 1987 self.add_argument( 1988 "--b64t", nargs="?", type=int, dest="b64t", action="append", default=None 1989 ) 1990 self.add_argument( 1991 "-D", 1992 "--dns", 1993 nargs="*", 1994 type=str, 1995 dest="dns", 1996 action="append", 1997 default=None, 1998 ) 1999 2000 def run(self): 2001 a = self.parse_args() 2002 e = Utils.nes(a.action) and a.action[0] == "a" 2003 if e and Utils.nes(a.input) and not Utils.nes(a.output) and a.input != "-": 2004 a.output = a.input 2005 if e and not Utils.nes(a.input) and Utils.nes(a.output): 2006 a.input = a.output 2007 if a.input: 2008 c = Utils.read_file_input(a.input) 2009 else: 2010 c = Config() 2011 if a.stdin and a.input != "-": 2012 if stdin.isatty(): 2013 raise ValueError("stdin: no input found") 2014 if hasattr(stdin, "buffer"): 2015 b = stdin.buffer.read().decode("UTF-8") 2016 else: 2017 b = stdin.read() 2018 stdin.close() 2019 for v in b.split("\n"): 2020 x = split(v) 2021 _Builder.build(c, super(__class__, self).parse_args(x), True, x) 2022 del x 2023 elif not Utils.nes(a.output) or (not a.print and not a.json): 2024 _Builder.build(c, a, e, argv) 2025 if len(c) == 0: 2026 return 2027 Utils.write_file_output(c, a.output, a.print, a.json) 2028 del e, a, c 2029 2030 @staticmethod 2031 def _organize(args): 2032 a = False 2033 w = list() 2034 d = dict() 2035 for i in range(0, len(args)): 2036 if len(args[i]) < 3: 2037 continue 2038 if args[i][0] != "-": 2039 continue 2040 if args[i].lower() == "--aes": 2041 if a: 2042 continue 2043 a = True 2044 if args[i].lower() == "--aes-iv": 2045 if a: 2046 continue 2047 v = "aes" 2048 a = True 2049 else: 2050 v = args[i].lower()[2:] 2051 if v not in Cfg.Const.WRAPPERS: 2052 continue 2053 if v in d: 2054 raise ValueError('duplicate argument "--{v}" found') 2055 w.append(v) 2056 d[v] = len(w) - 1 2057 e = [None] * len(w) 2058 del w, a 2059 return d, e 2060 2061 def parse_args(self): 2062 if len(argv) <= 1: 2063 return self.print_help() 2064 return super(__class__, self).parse_args() 2065 2066 def print_help(self, file=None): 2067 print(HELP_TEXT.format(binary=argv[0]), file=file) 2068 exit(2) 2069 2070 @staticmethod 2071 def build(config, args, add, arv): 2072 if add and len(config) > 0: 2073 config.add(Cfg.separator()) 2074 p, w = _Builder._organize(arv) 2075 if args.host: 2076 config.add(Cfg.host(args.host)) 2077 if args.sleep: 2078 config.add(Cfg.sleep(args.sleep)) 2079 if isinstance(args.jitter, int): 2080 config.add(Cfg.jitter(args.jitter)) 2081 if isinstance(args.weight, int): 2082 config.add(Cfg.weight(args.weight)) 2083 if args.killdate: 2084 config.add(Cfg.killdate(args.killdate)) 2085 if args.workhours_days or args.workhours_start or args.workhours_end: 2086 config.add( 2087 Cfg.workhours( 2088 args.workhours_days, args.workhours_start, args.workhours_end 2089 ) 2090 ) 2091 if args.key_pin and len(args.key_pin) > 0: 2092 for i in args.key_pin: 2093 if isinstance(i, str): 2094 config.add(Cfg.key_pin(i)) 2095 if not isinstance(i, list): 2096 continue 2097 for x in i: 2098 config.add(Cfg.key_pin(x)) 2099 if args.selector: 2100 if args.selector == "last": 2101 config.add(Cfg.selector_last_valid()) 2102 elif args.selector == "random": 2103 config.add(Cfg.selector_random()) 2104 elif args.selector == "round-robin": 2105 config.add(Cfg.selector_round_robin()) 2106 elif args.selector == "semi-random": 2107 config.add(Cfg.selector_semi_random()) 2108 elif args.selector == "semi-round-robin": 2109 config.add(Cfg.selector_semi_round_robin()) 2110 else: 2111 raise ValueError("selector: invalid value") 2112 if args.tcp: 2113 config.add(Cfg.connect_tcp()) 2114 if args.tls: 2115 config.add(Cfg.connect_tls()) 2116 if args.udp: 2117 config.add(Cfg.connect_udp()) 2118 if args.icmp: 2119 config.add(Cfg.connect_icmp()) 2120 if args.pipe: 2121 config.add(Cfg.connect_pipe()) 2122 if args.tls_insecure: 2123 config.add(Cfg.connect_tls_insecure()) 2124 if isinstance(args.ip, int): 2125 config.add(Cfg.connect_ip(args.ip)) 2126 if args.wc2_url or args.wc2_host or args.wc2_agent or args.wc2_headers: 2127 config.add( 2128 Cfg.connect_wc2( 2129 args.wc2_url, 2130 args.wc2_host, 2131 args.wc2_agent, 2132 Utils.parse_wc2_headers(args.wc2_headers), 2133 ) 2134 ) 2135 if args.tls_ca or args.tls_pem or args.tls_key: 2136 config.add( 2137 Utils.parse_tls( 2138 args.tls_ca, args.tls_pem, args.tls_key, args.mtls, args.tls_ver 2139 ) 2140 ) 2141 elif args.mtls: 2142 raise ValueError("mtls: missing CA, PEM and KEY values") 2143 elif isinstance(args.tls_ver, int): 2144 config.add(Cfg.connect_tls_ex(args.tls_ver)) 2145 if args.b64t and len(args.b64t) == 1: 2146 if args.b64t[0] is None: 2147 config.add(Cfg.transform_b64()) 2148 else: 2149 config.add(Cfg.transform_b64_shift(int(args.b64t[0]))) 2150 if args.dns and len(args.dns) > 0: 2151 if len(args.dns) == 1 and len(args.dns[0]) == 0: 2152 config.add(Cfg.transform_dns([])) 2153 else: 2154 config.add(Cfg.transform_dns(Utils.split_dns_names(args.dns))) 2155 if args.hex: 2156 w[p["hex"]] = Cfg.wrap_hex() 2157 if args.zlib: 2158 w[p["zlib"]] = Cfg.wrap_zlib() 2159 if args.gzip: 2160 w[p["gzip"]] = Cfg.wrap_gzip() 2161 if args.b64: 2162 w[p["b64"]] = Cfg.wrap_b64() 2163 if args.xor: 2164 w[p["xor"]] = Cfg.wrap_xor(args.xor[0]) 2165 elif "xor" in p: 2166 w[p["xor"]] = Cfg.wrap_xor() 2167 if args.cbk: 2168 w[p["cbk"]] = Cfg.wrap_cbk(key=args.cbk[0]) 2169 elif "cbk" in p: 2170 w[p["cbk"]] = Cfg.wrap_cbk() 2171 if args.aes or args.aes_iv: 2172 w[p["aes"]] = Cfg.wrap_aes(args.aes, args.aes_iv) 2173 elif "aes" in p: 2174 w[p["aes"]] = Cfg.wrap_aes() 2175 for i in w: 2176 config.add(i) 2177 del w, p 2178 2179 2180 if __name__ == "__main__": 2181 try: 2182 _Builder().run() 2183 except Exception as err: 2184 print(f"Error: {err}\n{format_exc(3)}", file=stderr) 2185 exit(1)