From bd4c7b8fef4f7d03309933eda67e48dacbf0ee46 Mon Sep 17 00:00:00 2001 From: darkclip Date: Sat, 27 Jan 2024 03:17:59 +0800 Subject: [PATCH] update --- init.sh | 5 ++- sort_known_hosts.py | 97 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 2 deletions(-) create mode 100755 sort_known_hosts.py diff --git a/init.sh b/init.sh index 654089c..8dc371e 100755 --- a/init.sh +++ b/init.sh @@ -66,8 +66,9 @@ deb https://$sec_site/debian-security $OS_CODE-security main contrib non-free no curl -fsSL "https://packagecloud.io/ookla/speedtest-cli/gpgkey" | \ gpg --dearmor | tee /etc/apt/trusted.gpg.d/ookla_speedtest-cli-archive-keyring.gpg > /dev/null - cp "$SCRIPTPATH/.zshrc" $HOME usermod -s /usr/bin/zsh $(whoami) + cp "$SCRIPTPATH/.zshrc" $HOME + cp "$SCRIPTPATH/*.py" $HOME } install_pkg(){ @@ -110,4 +111,4 @@ done; if [ $INSTALL_ONLY -eq 0 ]; then configure; fi; -install_pkg; \ No newline at end of file +install_pkg; diff --git a/sort_known_hosts.py b/sort_known_hosts.py new file mode 100755 index 0000000..192bc32 --- /dev/null +++ b/sort_known_hosts.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import sys +import os +from pathlib import Path +from collections import namedtuple + + +class Entry(namedtuple('Entry', ['host', 'algorithm', 'hash'])): + separator: str = '.' + + def __str__(self): + return f'{self.host} {self.algorithm} {self.hash}' + + @property + def key(self): + return f'{self.algorithm}{self.separator}{self.hash}' + + +def main(args): + filename: Path | None = getattr(args, 'known_hosts', None) + file_path = filename.resolve() + if not file_path.exists() or not file_path.is_file(): + sys.exit(1) + entryMap = {} + + with file_path.open(encoding='utf-8') as fp: + for lineNumber, line in enumerate(fp): + content = line.strip().split() + if len(content) == 0: + continue # empty line -> skip + if line[0].startswith('#') or len(content) != 3: + if not line[0].startswith('#'): + sys.stderr.write(f'could not parse a line {lineNumber}: "{line}"\n') + sys.stdout.write(line) + continue # comment or erroneous line -> skip + entry = Entry(*content) + entryMap.setdefault(entry.key, []).append(entry) + + compactEntryList = [] + + for key, entryList in entryMap.items(): + domainList = [] + ipList = [] + entry: Entry + for entry in entryList: + for domainOrIp in entry.host.split(','): + if len(domainOrIp) < 2: + sys.stderr.write(f'could not parse {domainOrIp}\n') + continue + start_loc = 0 + if domainOrIp[0] == '[': + start_loc = 1 + if 'a' <= domainOrIp[start_loc].lower() <= 'z': + domainList.append(domainOrIp) + else: + ipList.append(domainOrIp) + host = ','.join(sorted(domainList) + sorted(ipList)) + compactEntryList.append(Entry(host, *key.split(Entry.separator))) + + output: Path | None = getattr(args, 'output', None) + output_lines = '\n'.join( + map( + str, + sorted( + compactEntryList, + key=lambda entry: entry.host[1:] + if entry.host.startswith('[') + else entry.host, + ), + ) + ) + if output is None: + sys.stdout.write(output_lines) + else: + output_file = output.resolve() + if not output_file.parent.exists(): + os.makedirs(output_file.parent) + if output_file.is_dir(): + print('Can not write to directory.') + with output_file.open('w', encoding='utf-8') as wfp: + wfp.writelines(output_lines) + + +if __name__ == '__main__': + import argparse + + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + parser.add_argument('known_hosts', type=Path, help='known_hosts file to read') + parser.add_argument('-o', '--output', type=Path, help='output file to write') + parser.set_defaults(func=main) + args = parser.parse_args() + args.func(args)