На що здатний мозок студента, який пізнає комп'ютерний світ

Доброго вам дня.

Закінчивши писати черговий скрипт на Bash, зрозумів, що все має бути зовсім інакше, але все працювало. Хочу вам показати, які непотреби та милиці написав я, щоб вирішити завдання, але поки що не маючи вагона знань. Інакше висловлюючись, карикатура на програмування.

Завдання


Стало потрібно щось, щоб:

  • Виводило безліч рим для слова, крім квадратів
  • Перетинало безліч рим двох слів

Для чого? Ну ось треба – і все тут.
Хто не знає, квадратна рима (у просторіччі — квадрат) — два слова, у яких збігаються дві останні літери в написанні, що (часто тільки це) і робить їх римою. Наприклад, троянди – морози; шина - машина. Використання квадратів у сучасному віршуванні не особливо схвалюється людьми, через їхню примітивність.

Рішення


Найпростішим рішенням мені здалося написати скрипт на Bash, який використовує вже існуючий генератор рим - HOST, який в першу чергу підбирає їх за співзвуччям, а не написанням. Що це за HOST? Тому що, якщо вказати справжню назву сайту — скажуть, що реклама. Чому б не продовжити користуватися ним? По-перше, незважаючи на його перевагу підбору рим за співзвуччям, він-таки часто видає квадрати. По-друге, все одно доводиться думати мізками, витрачати час на перемикання між вкладками, сили на запам'ятовування слів, що повторюються, у списках для знаходження рими до двох слів.

Отримання сильних рим

Що я знаю? Я знаю про утиліту Wget, яка завантажує сторінку за вказаною URL-адресою. Добре, виконуємо запит — отримуємо HTML-сторінку у файлі, який названий словом для рими. Наприклад, пошукаємо за словом «тут»:

wget https://HOST/rifma/здесь

Але мені ж потрібен лише список слів, як позбудеться всього іншого? Дивимося і бачимо, що список слів оформлений, хоч як це дивно, у вигляді списку, і слова знаходяться в тегах . Що ж, у нас є чудова утиліта sed - так і запишемо:

cat $word | grep '<li>' | sed -e "s%<li>%%" | sed -e "s%</li>%%" | sed -e "s/ //g" | sed -e "/^$/d" 1> $word

Спочатку з файлу word вибираємо рядки, у яких міститься тег - Отримуємо купу порожніх тегів і рядки зі словами. Забираємо сам тег та його закриваючий — тут символи відсотка використані замість слішів тому, що у самому тегу вже є сліш, чому sed трохи вас не розуміє. А з відсотками все гаразд. Забираємо всі прогалини з файлу, видаляємо порожні рядки. Вуаля – готовий список слів.

Для того, щоб прибрати слова, які римуються за рахунок останніх літер, виділимо останні дві літери з вихідного слова та почистимо список:

squad=${word:((${#word}-2)):2}
cat $word | sed -e "/.$squad$/d" 1> $word

Дивимося, пробуємо все працює ... так, а де список для слова «грати»? А для слова "йду"? Файл порожній! А це все тому, що ці слова є дієсловами, і ми знаємо, що роблять з тими, хто римує на дієслова. Дієслівна рима гірша навіть квадратна, бо дієслів у російській мові найбільше, та ще й усе з однаковими закінченнями, через що їх і не виявилося в підсумковому файлі після перевірки закінчень.

Проте не поспішаємо. До кожного слова є не тільки рими, але ще й асонанси, які іноді звучать набагато краще, ніж рима — на те вони й асонанси (фр. assonance, від латів. assono — звучу до ладу).

Отримуємо асонанси

Тут починається найцікавіше: асонанси з'являються по окремому URL, а на цій же сторінці шляхом виконання скрипту, посилання HTTP запиту та отримання відповіді. Як же сказати WgetНатиснути кнопку? А ось ніяк. Сумно.

Помітивши, що URL у рядку все ж таки якось змінюється, я скопіював те, що було там після переходу на асонанси, і вставив у новій вкладці браузера - відкрилися сильні рими. Не те.

По суті, подумав я, для сервера має бути все одно, чи виконується скрипт, що надсилає йому запит, чи людина сама руками набирає його. Так? А хто його знає, підемо перевіряти.

Куди відправляти? Що надсилати? HTTP запит на IP сервера, там щось подібне до GET… там потім щось HTTP/1.1… Треба подивитися, що й куди відправляє браузер. Встановлюємо Wireshark, дивимося трафік:

0040 37 5d a3 84 27 e7 fb 13 6d 93 ed cd 56 04 9d 82 7]£.'çû.m.íÍV...
0050 32 7c fb 67 46 71 dd 36 4d 42 3d f3 62 1b e0 ad 2|ûgFqÝ6MB=ób.à.
0060 ef 87 be 05 6a f9 e1 01 41 fc 25 5b c0 77 d3 94 ï.¾.jùá.Aü%[ÀwÓ.

Ем що? Ах так, у нас же HTTPS. Що робити? Влаштувати MITM атаку на себе? Ідеально, жертва сама нам допомагатиме.

Загалом, здогадавшись полазити по браузеру, я знайшов сам запит, і адресата. Поїхали:

Діалог з терміналом

telnet IP PORT
Trying IP...
Connected to IP.
Escape character is '^]'.
GET /rifma/%D0%BC%D0%B0%D1%82%D1%8C?mode=block&type=asn HTTP/1.1
Host: HOST
Accept-Language: en-US,en;q=0.5
X-Requested-With: XMLHttpRequest
Connection: close

HTTP/1.1 400 Bad Request
Server: nginx/1.8.0
Date: Sun, 03 Nov 2019 20:06:59 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 270
Connection: close

<html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<center>The plain HTTP request was sent to HTTPS port</center>
<hr><center>nginx/1.8.0</center>
</body>
</html>
Connection closed by foreign host.

Хи. Хи-хи. Дійсно, що я очікував, надсилаючи голий HTTP запит на порт HTTPS. Хіба тепер шифрувати? Вся ця метушня з RSA ключами, потім ще з SHA256. А навіщо, є ж OpenSSL для таких справ. Що ж, вже знаємо, що робити, тільки попередньо приберемо поля Referer і Cookie - думаю, вони не сильно вплинуть на справу:

Діалог з терміналом

openssl s_client -connect IP:PORT
{Всякие ключи, сертификаты}
GET /rifma/%D0%B7%D0%B4%D0%B5%D1%81%D1%8C?mode=block&type=asn HTTP/1.1
Host: HOST
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/javascript,text/html,application/xml,text/xml,*/*
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
X-Requested-With: XMLHttpRequest
Connection: keep-alive

HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Status: 200 OK
Date: Sun, 03 Nov 2019 20:34:33 GMT
Set-Cookie: COOKIE
X-Powered-By: Phusion Passenger 5.0.16
Server: nginx/1.8.0 + Phusion Passenger 5.0.16
Expires: Thu, 01 Jan 1970 00:00:01 GMT
Cache-Control: no-cache
Strict-Transport-Security: max-age=31536000
Content-Security-Policy: block-all-mixed-content
Content-Encoding: gzip

На що здатний мозок студента, який пізнає комп'ютерний світ

Це що, мат на серверному? Добре, принаймні, мені відповіли 200 OK, значить, куки і реферер ні на що не впливають. Стиснення gzip, але при копіюванні копіюються символи ASCII. Точно, можна забрати рядок Accept-encoding. Все чудово – отримуємо HTML документ, тепер уже з асонансами. Але два питання: як запускати OpenSSL і передавати йому дані скриптом? І як зчитувати висновок, якщо після отримання відповіді ми залишаємось як би в «оболонці» OpenSSL? Якщо з другим можна щось вигадати, але з першим…

Як добре, що є Хабр, де я прочитав про утиліту очікуватияка автоматизує процес взаємодії з програмами, що очікують взаємодії з людиною. Ще більш привабливою є наявність команди автоочікуйте, що генерує очікувати скрипт на ваші дії. Що ж, запускаємо, робимо все це і готовий скрипт. Тільки дуже він величезний, а все тому, що OpenSSL виводить сертифікати, ключі, а очікувати очікує на виведення цього всього. Чи нам це потрібно? Ні. Зносимо все перше запрошення, залишаючи лише останнє перенесення рядка 'r'. Також з нашого запиту прибираємо поля User-Agent та Accept – ні на що не впливають. Так, запускаємо. Скрипт виконався, але де заповітний HTML документ? Очікувати з'їв його. Щоб змусити його виплюнути, треба покласти:

set results $expect_out(buffer)

перед кінцем скрипта — так буде записано висновок виконуваної очікуватиТому команди і виведений на екран. За підсумками, щось на зразок цього:

Скрипт expect'a

#!/usr/bin/expect -f

set timeout -1
spawn openssl s_client -connect IP:PORT
match_max 100000
expect -exact "
---r
"
send -- "GET /rifma/%d0%b7%d0%b4%d0%b5%d1%81%d1%8c?mode=block&type=asn HTTP/1.1rHost: HOSTrAccept-Language: en-US,en;q=0.5rX-Requested-With: XMLHttpRequestrConnection: close"
expect -exact "GET /rifma/%d0%b7%d0%b4%d0%b5%d1%81%d1%8c?mode=block&type=asn HTTP/1.1r
Host: HOSTr
Accept-Language: en-US,en;q=0.5r
X-Requested-With: XMLHttpRequestr
Connection: close"
send -- "r"
set results $expect_out(buffer)
expect -exact "r
"
send -- "r"
expect eof

Але це ще не все! Як можна було помітити, у всіх прикладах URL-адреса запиту був статичним, однак саме він відповідає за те, до якого слова будуть виведені асонанси. А так виходить, що ми постійно шукатимемо за словом «%d0%b7%d0%b4%d0%b5%d1%81%d1%8c» в ASCII або «тут» в UTF-8. Що робити? Просто щоразу генерувати новий скрипт, друзі! Тільки вже не автоочікуйтеТому, а за допомогою нудьгувати, т.к. у нас у новому не змінюється нічого, окрім слова. І хай живе нова проблема: як нам так розумно перекласти слово з кирилиці в URL формат? Щось і для термінала особливо немає нічого. Ну нічого, ми можемо? Можемо:

Дивись, що можу!

function furl {
furl=$(echo "$word" | sed 's:А:%d0%90:g;s:Б:%d0%91:g;s:В:%d0%92:g;s:Г:%d0%93:g;s:Д:%d0%94:g;s:Е:%d0%95:g;s:Ж:%d0%96:g;s:З:%d0%97:g;s:И:%d0%98:g;s:Й:%d0%99:g;s:К:%d0%9a:g;s:Л:%d0%9b:g;s:М:%d0%9c:g;s:Н:%d0%9d:g;s:О:%d0%9e:g;s:П:%d0%9f:g;s:Р:%d0%a0:g;s:С:%d0%a1:g;s:Т:%d0%a2:g;s:У:%d0%a3:g;s:Ф:%d0%a4:g;s:Х:%d0%a5:g;s:Ц:%d0%a6:g;s:Ч:%d0%a7:g;s:Ш:%d0%a8:g;s:Щ:%d0%a9:g;s:Ъ:%d0%aa:g;s:Ы:%d0%ab:g;s:Ь:%d0%ac:g;s:Э:%d0%ad:g;s:Ю:%d0%ae:g;s:Я:%d0%af:g;s:а:%d0%b0:g;s:б:%d0%b1:g;s:в:%d0%b2:g;s:г:%d0%b3:g;s:д:%d0%b4:g;s:е:%d0%b5:g;s:ж:%d0%b6:g;s:з:%d0%b7:g;s:и:%d0%b8:g;s:й:%d0%b9:g;s:к:%d0%ba:g;s:л:%d0%bb:g;s:м:%d0%bc:g;s:н:%d0%bd:g;s:о:%d0%be:g;s:п:%d0%bf:g;s:р:%d1%80:g;s:с:%d1%81:g;s:т:%d1%82:g;s:у:%d1%83:g;s:ф:%d1%84:g;s:х:%d1%85:g;s:ц:%d1%86:g;s:ч:%d1%87:g;s:ш:%d1%88:g;s:щ:%d1%89:g;s:ъ:%d1%8a:g;s:ы:%d1%8b:g;s:ь:%d1%8c:g;s:э:%d1%8d:g;s:ю:%d1%8e:g;s:я:%d1%8f:g;s:ё:%d1%91:g;s:Ё:%d0%81:g')}

Разом маємо скрипт, який перетворює слово в ASCII текст, що генерує вже інший скрипт, який запитує через OpenSSL у сервера сторінку сайту з асонансами. А далі перенаправляємо виведення останнього скрипта у файл і по-старому пропускаємо його через «фільтри» зайвого, квадратів та дозаписуємо у файл.

Перетин множин. Підсумок

Власне, це саме те, що викликає найменші проблеми. Виконуємо вищевказані процедури для двох слів, потім із двох списків порівнюємо кожне слово з кожним і якщо збіг знайдено – виводимо. Тепер у нас є скрипт, який приймає на вхід два слова і виводить список слів, що римуються і з тим, і з іншим, та ще й з урахуванням асонансів, і це все без ручного перемикання між чотирма вкладками та запам'ятовуванням слів «на око» — все зібрано, враховано та викинуто автоматично. Прекрасно.

Метою цієї публікації було показати, що й людині щось потрібно, він це зробить у разі. Дуже неефективно, криво, моторошно, але щось працюватиме.

Джерело: habr.com

Додати коментар або відгук