Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

BASH: Implementacja metod GET i POST protokołu HTTP

System Linux, w bardzo łatwy sposób, umożliwia nam dostęp do protokołu TCP, za pomocą którego możemy zaimplementować wiele tekstowych protokołów wyższych warstw. W tym wpisie zaprezentuje jak uzyskać dostęp do zasobów HTTP przez metody GET i POST, za pomocą języka skryptowego BASH.

Zacznijmy od typowego zapytania GET (RFC 2616). Wygląda następująco:GET / HTTP/1.1 Host: www.wp.pl

Pierwsza linia składa się z:
metody - GET
zasobu - /
wersji - HTTP/1.1

W następnej linii określony został host (serwer), z którego pobieramy zasoby.

Standardowo każdy skrypt w Linuksie uruchamiany jest z trzema strumieniami stdin (0), stdout (1), strerr (2). Dostęp do protokołu TCP otrzymujemy przez wirtualne urządzenie /dev/tcp, HTTP jest protokołem wyższej warstwy pracującym na porcie 80. Aby nawiązać połączenie, należy utworzyć kolejny strumień, który zestawi nam transmisję klient-serwer na porcie 80, w tym celu wpisujemy exec 3<>/dev/tcp/www.wp.pl/80

3 - oznacza nowy strumień, który będzie używany do wysyłania i odbierania komunikatów
<> - to przekierowanie wejścia i wyjścia
/dev/tcp/www.wp.pl/80 - to określenie serwera do jakiego uzyskujemy dostęp

W tym momencie mamy zestawione połączenie klient-serwer. Teraz wyślemy żądanie dostępu do zasobu. Wpiszmy sekwencję:echo "GET / HTTP/1.1" >&3 echo "Host: www.wp.pl" >&3 echo "" >&3

>&3 oznacza przekierowanie danych do strumienia "3", który jest naszym połączeniem (klient-serwer). Pierwsze dwie linie zostały omówione wcześniej, trzecia (pusta) wg standardu stanowi separator/zakończenie komunikatu GET. Zapytanie zostało wysłane do serwera, a jego wynik oczekuje na nas w strumieniu "3". Aby go wyświetlić należy przekierować wyjście na ekran za pomocą polecania cat:cat <&3

Uzyskamy w ten sposób zasób "/" z serwera "www.wp.pl", łącznie z nagłówkami GET/HTTP. Niektóre serwery (np. google.com) po wykonaniu zapytania nie zamkną automatycznie połączenia, dlatego też należy określić, że chcemy je zamknąć po pobraniu zasobu:echo "GET / HTTP/1.1" >&3 echo "Host: www.google.com" >&3 echo "Connection: close" >&3 echo "" >&3

Jako podsumowanie GET zapiszmy całość jako funkcję bash: http_get() { HOST=`echo $1 | sed "s/.*\:\/\///"` PARAM="/"`echo $HOST | sed "s/.*\///"` HOST=`echo $HOST | sed "s/\/.*//"` PORT="80" exec 3<>/dev/tcp/$HOST/$PORT if [ $? -ne 0 ]; then exit fi echo "GET $PARAM HTTP/1.1" >&3 echo "Host: $HOST" >&3 echo "Connection: close" >&3 echo "" >&3 cat <&3 } http_get "http://www.wp.pl/"

Przejdźmy teraz do metody POST, która umożliwia przekazywanie parametrów do serwera. Minimalna postać zapytania wygląda następująco:POST / HTTP/1.1 Host: www.wp.pl Content-Length: 4 Content-Type: application/x-www-form-urlencoded dane

W pierwszej linii POST oznacza metodę przekazywania danych do serwera, reszta jest identycznie jak w GET. Podobnie jest w drugiej linii. Content-Length oznacza długość danych, wysłanych za separatorem (pusta linia), Content-Type - to format przesyłanych danych.

Implementacja w postaci funkcji: http_post() { HOST=`echo $1 | sed "s/.*\:\/\///"` PARAM="/"`echo $HOST | sed "s/.*\///"` HOST=`echo $HOST | sed "s/\/.*//"` PORT="80" DATA="$2" SIZE=${#DATA} TYPE="$3" exec 3<>/dev/tcp/$HOST/$PORT if [ $? -ne 0 ]; then exit fi echo "POST $PARAM HTTP/1.1" >&3 echo "Host: $HOST" >&3 echo "Connection: close" >&3 echo "Content-Length: $SIZE" >&3 echo "Content-Type: $TYPE" >&3 echo "" >&3 echo "$DATA" >&3 cat <&3 }

Podobnie jak w metodzie GET wysyłamy dla pewności opcję zakończenia połączenia (Connection: close) po przesłaniu danych. Content-Length zliczany jest z długości przekazanych danych ${#DATA}. Pomiędzy danymi, a nagłówkiem zostawiamy linię wolną - oznacza to zakończenie przesyłania nagłówka i rozpoczęcie przesyłania danych.

Przykład zastosowania:http_post "http://localhost/login.php" "username=nazwa&password=haslo" "application/x-www-form-urlencoded"

Znając podstawy można spróbować zaimplementować inne protokoły warstwy aplikacji. 

linux programowanie

Komentarze

0 nowych
kwpolska   6 #1 14.05.2013 20:33

nie szybciej, łatwiej i wygodniej curlem?

curl http://wp.pl/
curl --data "username=nazwa&password=haslo" http://localhost/login.php

etam   10 #2 14.05.2013 21:09

Jakby się tylko dało zastąpić czymś "cat <&3", to wszystko chodziłoby w procesie basha (bez uruchamiania zewnętrznych programów)

Edit: nie zauważyłem, że tam jest sed, ale chyba dało by się go zamienić na wbudowane "[[ string =~ pattern ]]" i BASH_REMATCH

Autor edytował komentarz.
maciejkaczkowski   6 #3 14.05.2013 21:18

@kwpolska: może szybciej i łatwiej, ale nie zawsze masz odpowiednie narzędzia. Generalnie pisałem to jako wprowadzenie do wysyłania maili bez sendmaila

@etam: cat na razie nie wiem czym zastąpić, z sed nie trzeba korzystać, wystarczy podać nazwę hosta bez http i zasób jako dwie zmienne. sed ma za zadanie rozdzielić http:// od domeny i zasobu

dragonn   11 #4 14.05.2013 23:32

Pliku można wczytywać read zmiena < plik tylko że to nie łapie kolejnych linii, bo cały plik ląduje w jeden linii

kwpolska   6 #5 15.05.2013 16:53

@maciejkaczkowski
to jest jakieś distro bez curla?

dragonn   11 #6 15.05.2013 17:40

@kwpolska distra może nie, ale np. na Androdzie nie masz ;p, o ile uruchomienie bash to kwestia wrzucenie jednej binarki, to z curl może być większy problem. Jak dla mnie rozwiązanie bardzo ciekawe.

maciejkaczkowski   6 #7 15.05.2013 19:20

@kwpolska: np Tiny Core Linux standardowo nie ma, ale można doinstalować. Powyższych rozwiązań używam do SMSAPI przy HP T5300, tam jest tylko 32MB flash. curl zajmuje 250kb, bash 390kb, sendmaila nie ma, a basha można użyć zarówno do http jak i smtp. Ponadto nie chciałem "chomikować" rozwiązań, które mogą się jeszcze komuś przydać ;)

btw zamiast cat można użyć:
while read line
do
echo "$line"
done <&3
wówczas wszystko będzie w bashu

Autor edytował komentarz.
  #8 16.05.2013 10:44

Niezłe. Strumień 3 z przykładu siedzi w RAMie?

maciejkaczkowski   6 #9 16.05.2013 14:01

@anonim: Tak, każde polecenie w dowolnym kierunku przechodzi przez 3, więc wszystko co wraca wrzucane jest do RAMu i tam buforowane. W powyższym przykładzie cała strona trafia do pamięci w momencie wysłania separatora w postaci pustej linii:
echo "" >&3

Jak zachowują się polecenia możesz sobie zobaczyć korzystając z dwóch terminali, w jednym wykonujesz polecania, a w drugim masz uruchomionego tcpdump np:
tcpdump -i wlan0 -nptttt src host 212.77.100.101 or dst host 212.77.100.101
gdzie 212.77.100.101 to wp.pl uzyskane przez dig wp.pl

  #10 16.05.2013 19:38

a można za pomocą geta wyciągnąć wszystkie pliki z rozszerzeniem .swf? materiały są dostępne po zalogowaniu

maciejkaczkowski   6 #11 18.05.2013 09:12

Teoretycznie można. Wystarczy zaimplementować obsługę cookies w metodach GET i POST. Natomiast jeżeli chodzi o pliki binarne to zakładam, że jeśli można skopiować cały dysk do pliku
cat /dev/hda > plik
to analogicznie można użyć do innego źródła.

lck*   3 #12 20.05.2013 13:20

"Pliku można wczytywać read zmiena < plik tylko że to nie łapie kolejnych linii, bo cały plik ląduje w jeden linii"

## Drukuje każdy wiersz łącznie za spacją
## Warunek przestaje być prawdziwy kiedy docieramy do końca pliku. Nie da się już czytać, więc false.
while read line_of_text
do
echo $line_of_text
done < "$file"

## Drukuje każdy ciąg znaków nieoddzielony separatorem.
while read line_of_text
do

for one_word in $line_of_text
do
echo -n "$one_word "
done
echo

done <$file

Każdy znak osobne też się da czyściutkim BASH'em. Działa szybko bo sed i grep globalnie w podpowłoce bardzo spowalnia skrypty.

Autor edytował komentarz.