From 223a7034ce463907c006a3cae58c9b101218877f Mon Sep 17 00:00:00 2001 From: stevefoxru <53931856+stevefoxru@users.noreply.github.com> Date: Thu, 27 Feb 2025 23:41:43 +0300 Subject: [PATCH] Create awg-decode.py --- awg/awg-decode.py | 140 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 awg/awg-decode.py diff --git a/awg/awg-decode.py b/awg/awg-decode.py new file mode 100644 index 0000000..627aec7 --- /dev/null +++ b/awg/awg-decode.py @@ -0,0 +1,140 @@ +import sys +import struct +import zlib +import base64 +import argparse +import socket +import ipaddress +import re + +def qCompress(data, level=-1): + compressed = zlib.compress(data, level) + header = struct.pack('>I', len(data)) + return header + compressed + +def qUncompress(data): + if len(data) < 4: + return b'' + uncompressed_size = struct.unpack('>I', data[:4])[0] + compressed_data = data[4:] + try: + uncompressed_data = zlib.decompress(compressed_data) + except zlib.error: + return b'' + if len(uncompressed_data) != uncompressed_size: + return b'' + return uncompressed_data + +def base64url_encode(data): + encoded = base64.urlsafe_b64encode(data) + return encoded.rstrip(b'=') + +def base64url_decode(data): + padding_needed = (4 - len(data) % 4) % 4 + data += b'=' * padding_needed + return base64.urlsafe_b64decode(data) + +def is_ip_address(address): + try: + ipaddress.ip_address(address) + return True + except ValueError: + return False + +def resolve_dns_to_ip(dns_name): + try: + ip_address = socket.gethostbyname(dns_name) + return ip_address + except socket.gaierror: + return None + +def process_conf_data(data): + def replace_endpoint(match): + full_line = match.group(0) + prefix = match.group(1) + address = match.group(2) + port = match.group(3) + suffix = match.group(4) + if not is_ip_address(address): + resolved_ip = resolve_dns_to_ip(address) + if resolved_ip: + print(f"Resolved DNS '{address}' to IP '{resolved_ip}'", file=sys.stderr) + return f"{prefix}{resolved_ip}:{port}{suffix}" + else: + print(f"Error: Could not resolve DNS name '{address}'", file=sys.stderr) + sys.exit(1) + else: + return full_line + pattern = r'^(.*Endpoint\s*=\s*)([^\s:]+)(?::(\d+))(.*)$' + return re.sub(pattern, replace_endpoint, data, flags=re.MULTILINE) + +def encode(data): + data_bytes = data.encode('utf-8') + compressed = qCompress(data_bytes, level=8) + base64_encoded = base64url_encode(compressed) + s = 'vpn://' + base64_encoded.decode('ascii') + return s + +def decode(s): + data = s.replace('vpn://', '') + data_bytes = data.encode('ascii') + compressed = base64url_decode(data_bytes) + uncompressed = qUncompress(compressed) + if uncompressed: + result = uncompressed + else: + result = compressed + return result.decode('utf-8') + +def main(): + parser = argparse.ArgumentParser(description='Encode and decode VPN configuration files to/from vpn:// format.') + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-e', '--encode', action='store_true', help='Encode a .conf file to vpn:// format.') + group.add_argument('-d', '--decode', action='store_true', help='Decode a vpn:// string to configuration data.') + parser.add_argument('input', help='Input file for encoding or vpn:// string for decoding.') + parser.add_argument('-o', '--output', help='Output file. If not specified, output will be printed to console.') + + args = parser.parse_args() + + if args.encode: + try: + with open(args.input, 'r', encoding='utf-8') as f: + data = f.read() + except FileNotFoundError: + print(f'Error: File {args.input} not found.') + sys.exit(1) + except Exception as e: + print(f'Error reading file {args.input}: {e}') + sys.exit(1) + + processed_data = process_conf_data(data) + + encoded_string = encode(processed_data) + + if args.output: + try: + with open(args.output, 'w', encoding='utf-8') as f: + f.write(encoded_string) + print(f'Encoded vpn:// string written to {args.output}') + except Exception as e: + print(f'Error writing to file {args.output}: {e}') + else: + print(encoded_string) + + elif args.decode: + vpn_string = args.input + + decoded_data = decode(vpn_string) + + if args.output: + try: + with open(args.output, 'w', encoding='utf-8') as f: + f.write(decoded_data) + print(f'Decoded configuration data written to {args.output}') + except Exception as e: + print(f'Error writing to file {args.output}: {e}') + else: + print(decoded_data) + +if __name__ == '__main__': + main()