Ako stiahnuť databázu - #2/3: IP, veľa IP

04.11.2007 17:50 | blackhole_matej

V prvom dieli sme si ukázali, ako stiahnuť úplne jednoduchú databázu bez obmedzení. Pri (nadsadenej) 1 sekunde na záznam by sme jej 5,396,168 riadkov stiahli z jednej mašiny za 62 a pol dňa. Nie vždy to býva také jednoduché...

-----------
2. Obmedzenia ----
Keďže prevádzkovateľ ZvieratkoDB nechce, aby sme pomocou 25 ťahačov stiahli jeho databázu za 2.5 dňa, môže nám klásť do cesty rôzne prekážky. Povieme si, ako väčšinu z nich obísť, počet potrebných ťahačodní (alebo strojových hodín) nám však s každým obmedzením narastá.

2.1 Náhodné id ----
Id v databáze (resp. id, ktoré "žerie" show.cgi) nemusia byť za sebou nasledujúce. Ak sú kroky medzi nimi malé, môžeme ich brute-forcenúť, spomalenie bude úmerné priemernej veľkosti kroku medzi dvoma susednými id.
Ak sú kroky medzi jednotlivými id príliš veľké na brute-force (ak by sme sa vyšplhali na neúnosný počet ťahačodní), musíme nejako získať zoznam id. Pristupujeme k tomu podobne, ako bežný smrteľník, teda cez search.cgi, v skratke:

#!/bin/bash
# priklad c.2, tahanie id podla wordlistu
for n in `cat wordlist.txt`; do
    wget -O - http://zvieratko.db/search.cgi?meno=$n \
    | grep ... | sed ... >> idlist.txt
done

Ako wordlist môžeme použiť všeobecnú encyklopédiu zvieratiek, korpus, rôzne predtým stiahnuté db apod. Takto získaný zoznam id potom stiahneme:

#!/bin/bash
# priklad c.3, tahanie zvieratiek
# podla stiahnuteho idlistu
for i in `cat idlist.txt`; do
    wget -O - http://zvieratko.db/show.cgi?id=$i \
    | grep ... | sed ... >> LokalneZvieratkoDB.txt
done

Rozdelenie na viaceré ťahače pomocou `split -l`, ako aj ošetrovanie chýb, je analogické ku prvému, sekvenčnému príkladu. Jednotlivé ťahače dáme do rôznych adresárov (každý bude mať vlastný idlist.txt, aj výstupný LokalneZvieratkoDB.txt), na rôzne stroje, alebo ich odlíšime nejakým parametrom pri spúšťaní (instance=$1), ktorý pripájame k názvom súborov (idlist-$instance.txt, LokalneZvieratkoDB-$instance.txt).

Dobré na search.cgi môže byť:

  • nemusí byť tak limitovaný ako show.cgi (viď. ďalej "Limit na IP"),

  • pri niektorých db dokonca netreba zadávať celé meno (stačia 2-3 písmená, ktoré vo vnorenom cykle prejdeme -- netreba wordlist)

  • pľuje naraz celé stránky so zoznamom idčiek (ak sú mená podobné, resp. začínajú na rovnaké zadané písmená)
Zlé na search.cgi môže byť vyžadovanie ďalších parametrov, napríklad meno + mesto, alebo meno + okres, čím sa zvyšujú nároky na náš ťahač (pridáme ďalšie cykly), ale aj sa oveľa viac znižuje komfort používateľa.

2.2 Limit na IP ----
Prevádzkovateľ DB si môže dať záležať na tom, koľko kto ťahá. Obmedzenie sa väčšinou týka iba show.cgi; môže sa vzťahovať na cookies (v prípade wgetu zanedbáme), alebo IP adresu.
Môže byť na ľubovoľný časový úsek, typicky na 1 deň (zistíme empiricky pomocou slučky s výpisom `date` do logu). Dajme tomu, že prevádzkovateľ ZvieratkoDB sa rozhodol pre limit 100 záznamov/IP/deň.
Obnova v čase je napríklad:

  • nulovanie o polnoci --- O polnoci (alebo v nejakú inú, presne stanovenú hodinu) sa počítadlá pre všetky IP vynulujú, a môžeme ťahať znovu. Stiahneme teda povolený počet každý deň v ľubovoľnú hodinu.
    V extrémnom prípade, ak máme dostatočný počet IP (viď. nižšie) a vzhľadom na rozmery databázy a nastavený limit by sme vedeli stiahnuť celú DB za 2 dni, urobíme to cez noc (prvá dávka napr. 21:00, druhá po polnoci). Ráno sa prevádzkovateľ môže už iba diviť, ako mu cez noc narástli logy a hit-countery.

  • nulovanie po stanovenom čase neaktivity --- Medzi jednotlivé dávky ťahania vložíme dostatočne dlhý `sleep`.

  • ako token bucket (priebežné znižovanie) --- Medzi jednotlivé wgety vložíme adekvátne dlhý `sleep`.

#!/bin/bash
# priklad c.4, tahanie zvieratiek
# podla idlistu, po davkach
c=0
for i in `cat idlist.txt`; do
    c=$((c+1))
    wget -O - http://zvieratko.db/show.cgi?id=$i \
    | grep ... | sed ... >> LokalneZvieratkoDB.txt
    if [ $c -eq 100 ]; then c=0; sleep 86400; fi
done

Ako získať veľa IP? ----
V IPv4 existuje menej ako 2^32 použiteľných verejných(!) IP adries. Bežný smrteľník môže byť rád, ak má vôbec jednu vlastnú. Kde ich teda zohnať, keď ich treba?

Vezmime prípad ZvieratkoDB s obmedzením 100 záznamov/IP/deň pre show.cgi. Potrebujeme takmer 54,000 ipdní. Ak to chceme stihnúť do mesiaca, treba nám okolo 1800 rôznych IP. Táto na prvý pohľad neuveriteľná požiadavka nemusí byť až tak nesplniteľná, ak uvážime:

  • Dynamicky prideľované IP adresy --- odpojením a pripojením (dialup, ADSL, ...) môžeme získať novú adresu. Ak to vieme za deň úspešne spraviť z jedného miesta 1800-krát (alebo z viacerých miest menejkrát), môžeme byť vychechtaní.
    # v priklade c.4 namiesto sleep restartneme ppp
    Nie vždy nám ale bude pridelená iná IP, preto tento spôsob nie je práve na pomerne veľkú ZvieratkoDB vhodný.

  • Veľké prázdne subnety --- Ak sme na väčšom subnete s verejnými IP adresami a miestne podmienky to umožňujú (napr. ethernet, nie point-to-point), zmenu IP adresy môžeme spraviť jednoducho pomocou ifconfig. Nie je možné aplikovať v sieťach, kde je natvrdo transparentný http proxy redirectovaný pre všetky spojenia na port 80 (teda napríklad niektoré internátne siete, kde by inak použiteľných IP boli tisíce [cez deň keď študenti nedrvia]). Tiež sa nedá v sieťach, kde je väčšina adries obsadených -- jedine ak sedíme na routri a adresu si bez súhlasu na tých pár sekúnd "požičiame". A samozrejme nepodarí sa v sieťach, kde má router napevno ARP tabuľku (nebýva časté).

    Ak nechceme prísť o vlastnú konektivitu, môžeme použiť subinterface, teda napríklad ifconfig eth0:$instance, a následne wget --bind-address. Nezabudnúť povoliť si IP v iptables, OUTPUT chain.


  • Proxy servery --- po internete sa povaľujúce voľne prístupné proxy, zriedka to však býva dostatkový tovar. Zoznam googlime, alebo nmapujeme náhodné stroje na portoch 8080, 3128.
    #!/bin/bash
    # priklad c.5, tahanie zvieratiek
    # so za sebou nasledujucimi id cez proxace
    i=0; L=5389180
    for proxy in `cat zoznamproxy.txt`; do
      c=0
      while [ $c -lt 100 ]; do
        i=$((i+1)); c=$((c+1))
        http_proxy="$proxy" \
        wget -O - http://zvieratko.db/show.cgi?id=$i \
        | grep ... | sed ... >> LokalneZvieratkoDB.txt
        if [ $i -eq $L ]; then exit; fi
      done
    done

  • Tor --- anonymizujúci software, ktorý využíva spleť Tor serverov na nadväzovanie spojenia. Zabudované šifrovanie a 3 hopy sú skôr na obtiaž, nám by totiž stačil koncový bod (na tento účel by bolo možné pokúsiť sa upraviť zdrojáky, dajte vedieť ak sa do toho niekto idete púšťať) -- je preto trochu pomalý, hlavne pre to, že deti cez to ťahajú torrenty, na zväčšenie nášho "IP poolu" je to ale celkom zaujímavý program.

    Tor funguje iba ako SOCKS proxy, čo wget nevie, preto je nutné použiť nejaký "http proxy -> SOCKS proxy" medzičlánok. Napríklad privoxy, ktorý má v dodávanom konfiguráku pripravenú sekciu pre spoluprácu priamo s Tor-om, stačí odkomentovať. Nastavíme rozumné hodnoty pre "NewCircuitPeriod" a "MaxCircuitDirtiness", wget spúšťame s premennou http_proxy="127.0.0.1:9050".


  • Botnety všakovakého druhu --- ideálna voľba pre hardcore ťahačov. Sťahovanie vykonávame z napadnutých mašín, ktorých je veľa. Tí, čo majú veľa peňazí, si botnet kúpia; tí, čo majú veľa času, si to nakódia a rozšíria sami. A my ostatní sa spokojíme so subnetmi, http_proxy a Tor-om (ak ho niekto upraví).

-----------
V treťom dieli sa pokúsime poriešiť obrázkové captcha, ktoré všetci máme tak radi.
-----------
Update 2007/11/08, aby nedošlo k ďalším nedorozumeniam:
Uvedené skripty sú ilustračné. Je potrebné ich upraviť tak, aby sme korektne vyparsovali požadované dáta (čo už bolo povedané v prvom dieli), a prípadne aby posielali ďalšie hodnoty, ktoré cieľ požaduje (napr. category=xyz&do=show&bla). V prípade, ak cieľový formulár "nežerie" hodnoty cez GET, ale cez POST v http hlavičke, je možné jednoducho použiť prepínač --post-data, viď. wget(1). Napríklad:

wget  --post-data="id=$i" -O - <a href="http://zvieratko.db/show.cgi" title="http://zvieratko.db/show.cgi">http://zvieratko.db/show.cgi</a>

----------
#1: Úvod, Disclaimer, Ilustračná DB, 1. Jednoduchý prípad, 1.1 Ide to pomaly..., 1.2 Ošetrovanie chýb
#2: 2. Obmedzenia, 2.1 Náhodné id, 2.2 Limit na IP, Ako získať veľa IP
#3: 2.3 Captcha, 2.4 Ďalšie obmedzenia, 3. Iné vychytávky, 4. Záver

    • Re: Ako stiahnuť databázu - #2/3: IP, veľa IP 07.11.2007 | 19:37
      Avatar blackhole   Návštevník

      Zkoušel jsi to někdy ? Nebo to píšeš čistě teorericky ?

      • Re: Ako stiahnuť databázu - #2/3: IP, veľa IP 07.11.2007 | 21:18
        Avatar blackhole_matej   Používateľ

        Práve že prakticky. A nie len raz. Pravidelne (raz za rok-dva-tri) si ťahávam aktualizácie :)
        Preto viem napríklad napísať, že Tor je pomalý a bolo by ho treba upraviť (čo som neskúšal). Aj to, že proxy sú nedostatkovým tovarom. A dokonca aj to, že dobrý subnet nikdy nie je zlý... :) Botnet sa mi písať samozrejme nechce.
        Mám odskúšané sekvenčné ťahanie, rýchlostne rovnaké kvázi-random ťahanie (primitive numbers rule), aj o čosi málo pomalšie ťahanie podľa search query. Najhoršie je na tom search, pri ktorom treba zadávať celé slová (ak nestačia prvé 2-3 pismená), ale wordlist nie je až taký nedostupný tovar (hlavne z predtým stiahnutých db).
        V závislosti od denného limitu a IP poolu sa dajú v pohode stiahnuť aj db veľkosti "ZvieratkoDB".

        Btw, neznášam teoretické žvásty :) Len pre úplnosť je vždy treba spomenúť aj alternatívne spôsoby.

    • Re: Ako stiahnuť databázu - #2/3: IP, veľa IP 08.11.2007 | 17:59
      Avatar blackhole   Návštevník

      Dik za clanok :)
      uz som si aj nieco cvicne vyhliadol... :)