На што здольны мозг студэнта, які спазнае кампутарны свет

Добрага часу сутак.

Скончыўшы пісаць чарговы скрыпт на 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 знакі. Дакладна, можна прыбраць радок Прыняць-кадаванне. Усё выдатна - атрымліваем HTML дакумент, зараз ужо з асанансамі. Але вось два пытанні: як запускаць OpenSSL і перадаваць яму дадзеныя скрыптам? І як счытваць выснову, калі пасля атрымання адказу мы застаемся як бы ў "абалонцы" OpenSSL? Калі з другім можна нешта прыдумаць, але вось з першым...

Як добра, што ёсць хабр, дзе я прачытаў пра ўтыліту чакаць, якая аўтаматызуе працэс узаемадзеяння з праграмамі, якія чакаюць узаемадзеяння з чалавекам. Яшчэ больш прывабна наяўнасць каманды autoexpect, якая генеруе чакаць скрыпт па вашых дзеяннях. Што ж, запускаем, які робіцца ўсё гэта і вось гатовы скрыпт. Толькі ўжо вельмі ён вялізны, а ўсё таму, што 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. Што рабіць? Вядома ж проста кожны раз генераваць новы скрыпт, сябры! Толькі ўжо не autoexpectТым, а з дапамогай сумаваць, т.я. у нас у новым не мяняецца нічога, акрамя слова. І хай жыве новая праблема: як бы нам гэтак разумна перавесці слова з кірыліцы ў 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

Дадаць каментар