November 30, 2022

Címzési rendszer

Címzési rendszer

A címzési rendszer kialakításánál azt a valóságos tényt vették figyelembe,
hogy a címzés legyen hierarchikus: azaz vannak hálózatok, és ezen belül gépek
(hosztok). Így célszerű a címet két részre
bontani: egy hálózatot azonosító, és ezen belül egy, a gépet azonosító címre.
Mivel a cím hossza 32 bit, ezért ezt kellett két részre bontani, olyan módon,
hogy a nagy hálózatokban lévő sok gépet is meg lehessen címezni. Négy különböző
formátum használható, ahogy ezt az 21. ábrán bemutatjuk.

21. ábra: IP 
  címek

21. ábra: IP címek

A cím négy bájtját szokásos a közéjük pontokat írva, a bájtok (byte)
decimális megfelelőjével leírni. Az első három címforma 128 hálózatot hálózatonként
16 millió hoszttal (A osztályú cím), 16 384
hálózatot 64 K-nyi hoszttal (B osztályú cím),
illetve 2 millió hálózatot, (amelyek feltételezhetően LAN-ok),
egyenként 254 hoszttal azonosít. Az utolsó
előtti címforma (D osztályú cím) többszörös címek (multicast address) megadását
engedélyezi, amellyel egy datagram egy hosztcsoporthoz
irányítható. Az utolsó címforma (E) fenntartott.
A címzéseknél a hálózat és hoszt címének
szétválasztására cím-maszkokat (netmask) használnak. Alkalmazásakor bitenkénti
ÉS műveletet végezve az IP cím és a cím-maszk
között, a hálózati cím leválasztására. C osztályú címek esetén ezért a maszk:
255.255.255.0, míg B osztálynál: 255.255.0.0, stb.

A címzésnél bizonyos címtartományok nem használhatók.

  • A 127-el kezdődő címek a “loopback” (visszairányítás) címek, nem használhatók
    a hálózaton kívül, a hálózatok belső tesztelésére használhatók.
  • A hoszt címrészbe csak 1-eseket írva lehetséges
    az adott hálózatban lévő összes hosztnak
    üzenetet küldeni (broadcast). Például a 195.13.2.255 IP
    címre küldött üzenetet a 193.13.2 című hálózatban lévő összes gép megkapja.
  • Ha a hoszt címrésze 0, az az aktuális
    hálózatot jelöli. Ha a hálózati cím 0, az az aktuális hálózatot jelöli. Például
    a saját gépről 0.0.0.0 címre küldött üzenet a saját gépre érkezik.

Sajnos a fejlemények azt mutatják, hogy ez a felosztás a tervezés gyenge pontja
volt, és már ma is címhiánnyal küszködik az Internet. Természetesen ez nem azt
jelenti, hogy már 232 számítógép van a hálózatban, hanem a kialakított
címrendszerben nagyon sok címet nem használnak fel. (Hol van a világon 128,
egyenként 65000 gépet tartalmazó hálózat?) Ezért rövidesen új címzést fognak
bevezetni (a tervezet neve: IPv6), ahol a címmező 128 bit hosszú lesz. Az Internetben
a rétegeknek megvan az egyedi azonosítója a címzéshez:

Réteg Címzési módszer

Alkalmazási

hoszt
neve, portja

Internet

IP
cím

Hálózat elérési

Fizikai cím

A hoszt neve, — amely valamilyen szimbolikus
név — azonosítja a felhasználó számára a gépet — “így hívja”. Pl.: “clmc-denes”.
Az Internet használata során két, egymástól akár sok ezer kilométerre lévő számítógép
között alakul ki kapcsolat. Nyilvánvalóan ezért minden egyes gépet azonosíthatóvá,
címezhetővé kell tenni. Erre két, egymással egyenértékű módszer áll rendelkezésre.
Az elsődleges módszer az amit IP címzésként
már megismertünk, míg a másodlagos — a felhasználók által szinte kizárólagosan
használt módszer az azonosító domén (domain)
nevek
rendszere.
Amikor a hálózathoz újabb gép csatlakozik, egy — az adott hálózatnak adott címtartományból
— négy tagból (bájtból) álló azonosító számot, Internet
címet (IP-address) kap. A címtartományok kiosztását az Internet központi
adminisztrációja, az INTERNIC (Internet Network
Information Center) végzi. A körzeti központok az adott gépet ezen a számon
tartják nyilván. A tényleges címeket általában decimális alakban, pl. 193.230.175.1
használják.
A címben szereplő egyes címrészeket ma már nem véletlenszerűen határozzák meg,
hanem hierarchikusan felosztott földrajzi terület, domének alapján. Így a cím
egyes oktetjei (8 bites csoprtjai) a domént, az ezen belüli aldomént és hosztot,
azaz a címzett számítógép helyét jelölik ki. A domén általában egy ország globális
hálózati egysége vagy hálózati kategóriája, az aldomén ezen belül egy különálló
hálózatrész, a hoszt pedig az adott hálózatrészen
belüli felhasználókat kiszolgáló gép azonosító száma.

A felhasználó számára könyebben használható a név alapján történő címzés, ahol
a sok számjegyből álló IP cím helyett egy
karakterlánc, az FQDN (Fully Qualified Domain
Name) használható. Az FQDN, azaz a teljes domén-név, amelyet a DNS
(Domain Name System), vagyis a domén-név rendszer szerint képeznek, ugyanúgy
hierarchikus felépítésű, mint az IP cím, formailag
pedig több, egymástól ponttal elválasztott tagból áll.

Például az denes.clmc.topnet.ro címben az egyes tagok sorrendben a kiszolgáló
gépet, a hosztgépet , az aldomént azaz hálózati
altartományt (clmc.topnet), végül pedig a domént, vagyis az adott ország globális
hálózati tartományát (ro) határozzák meg. A hálózati altartomány, az aldomén
több tagot is tartalmazhat, de akár hiányozhat is a cím domén-név részéből.
A domén-név egyes részeit néha eltérő
kifejezéssel adják meg: a hálózati tartomány domén vagy network,
az altartomány aldomén vagy subnet, a kiszolgáló
gép a hoszt vagy hoszt-address.

A domén-nevek használata az Internet számára némi járulékos munkát ad, hiszen
egy adatcsomag továbbítás előtt a hosztcímből
meg kell határozni a vele egyenértékű IP címet,
és a küldemény hosztcímét ezzel kell helyettesítenie.
Az összetartozó IP címeket és hosztcímeket
a hosztgép először a helyi címtáblázatban
(host table) keresi. Ha a keresés
eredménytelen, a hosztgép az Internet valamelyik
speciális szolgáltató-gépéhez, a névszolgáltatóhoz
(Name Server-hez) fordul, amely
az Internet gépeinek adatait tartalmazó, szabályos időközönként frissített sokszor
hatalmas címtáblázatot kezeli. A címtáblázatokban a host.aldomén.domén
alakú hostcímhez a vele egyenértékű IP cím,
esetleg hivatkozási (alias) alak is tartozhat. Az IP
cím kérésekor azt is közölni kell a névszolgáltatóval, hogy az mire kell.
Ha levelezéshez kérjük, akkor a névszolgáltató a névhez tartozó MX (Mail Exchange)
adatrekordot adja vissza, különben a tényleges IP
címet.
Az esetek nagy többségében szerencsére nem kell a fenti lépések mindegyikét
végrehajtani. A legfelső kiszolgáló (gyökér) ugyanis egyben a legfelső szinten
lévő tartományok (pl. hu) névkiszolgálójaként is szerepel. Tehát a gyökér
kiszolgáló felé irányuló egyetlen kérdéssel a MIT névkiszolgálójához lehet eljutni.
Az alkalmazott szoftverek pedig a már feltett kérdésekre kapott válaszokra emlékeznek,
az így megkapott domén név és a hozzá tartozó IP
cím eltárolódik. Persze minden ilyen információnak van egy megfelelő élettartama,
ami tipikusan pár napnak felel meg. Az élettartam lejárta után az információkat
fel kell frissíteni, amivel az esetleges változások is nyomon követhetők.
Az IP címhosztcím
átalakítást a TCP/IP automatikusan végzi, de a host operációs rendszer
parancs kiadásával mi is lekérdezhetjük egy ismert felhasználó számát.
Az egyes hosztgépekhez nemcsak IP
cím vagy az azzal egyenértékű domén cím tartozik, hanem a hosztgépek
a rajtuk futó alkalmazások eléréséhez tartozó portcímet (Application Selection
Address) is használnak. Ezért a címeket ki kell egészíteni az alkalmazás elérésére
szolgáló portcímmel is:

hostcím:portcím

Míg az egyes hosztokat a hosztcímük
egyértelműen meghatározzák, addig a hosztokat
több felhasználó használja, tehát a hozzájuk kapcsolódó felhasználókat is meg
kell különböztetnünk egymástól. Erre azok felhasználói neve (login
vagy felhasználónév), vagyis az
adott hoszton egyedi azonosító-név szolgál.
Egy személy Internet elérhető levelezési (E-mail) címe tehát két főrészből áll,
és a következő alakú:

felhasználónév@host.aldomén.domén

Hálózat elérési réteg  

Jelenleg a legtöbb hálózat fizikai és adatkapcsolati szinten Ethernet kártyákat
használ. Mivel az Ethernet keretnek saját fejléce van, saját egyedi, 48 bites
címzéssel rendelkezik, ezért az IP csomagokat ilyen hálózaton közvetlenül nem
lehet átvinni, be kell csomagolni. Minden Ethernet keretnek egy 14 oktetes fejléce
van, amely a forrás- és a célgép Ethernet címét, valamint egy típuskódot tartalmaz.
A hálózaton lévő gépek csak az olyan kereteket figyelik, amelyek célmezőjében
a saját Ethernet címüket, vagy a mindenkinek szóló körözvénycímet találnak.
Minden számítógépnek van egy táblázata, amelyben felsorolja, hogy milyen Ethernet
cím milyen Internet címnek felel meg.
Ennek a táblázatnak a karbantartását a rendszer egy protokoll, az ARP
(Address Resolution Protocol – címleképezési protokoll) segítségével végzi.

Egy alhálózatban, amelyik Ethernet összeköttetést használ, tegyük fel, hogy
a 193.230.175.196 IP című hosztról
a 193.230.175.75 hoszttal szeretnénk kapcsolatba
lépni. A kezdeményező 193.230.175.196 című hoszt
megnézi, hogy szerepel-e a saját ARP táblázatában a 193.230.175.75 címhez tartozó
Ethernet cím bejegyzés. Ha igen, akkor a datagramhoz egy Ethernet fejlécet csatol,
és elküldi. Ha azonban ilyen bejegyzés az ARP táblázatban nincsen, akkor a csomagot
nem lehet elküldeni, hiszen nincs meg az Ethernet cím. Ekkor lép működésbe az
ARP protokoll. A 193.230.175.196 hoszt egy “a
193.230.175.75 Ethernet cím kellene” tartalmú ARP kérést ad ki az Ethernet hálózatra.
Az adott hálózaton minden hoszt figyeli az
ARP kéréseket. Ha egy hoszt egy rá vonatkozó
ARP kérést kap, akkor válaszol rá. Ebben az esetben tehát a 193.230.175.75 hallja
a kérést, és egy ARP üzenetet küld válaszul a kérdezőnek, amelyben megadja a
193.230.175.75 IP című gépben lévő kártya Ethernet
címét, pl.: 12:3:44:12:52:11. A kérést adó rendszer a kapott információt bejegyzi
az ARP táblázatába.
Abban az esetben, ha a kért IP cím nincs a
közös Ethernet hálózatba kapcsolt hosztok
között, akkor a külvilág felé kapcsolatot biztosító átjáróban (routerben) lévő
Ethernet kártyacímet felhasználva, oda kell küldeni az adott keretet.
A fentiekből nyilvánvaló, hogy az ARP kéréseket tartalmazó kereteket üzenetszórás
formájában kell a hálózatra kiadni. A kérés megfogalmazásához a csupa egyes
bitből álló FF:FF:FF:FF:FF:FF Ethernet címet használják. Megállapodás szerint
az Ethernet alapú hálózatok minden gépe figyeli az ilyen címre küldött kereteket.
Ez azt jelenti, hogy az ARP kérést is látja mindegyikük. Minden egyes gép ellenőrzi,
hogy a kérés rá vonatkozik-e. Ha igen, akkor választ küld. Ha nem, akkor egyszerűen
nem veszi figyelembe. Az üzenetszórást jelző IP
című csomagokat (pl. 255.255.255.255 vagy 193.230.175.255) is csupa egyes
bitből álló Ethernet címre kell küldeni.
A címek mellett a fejlécben szerepel még egy, a használt protokollt azonosító
típuskód is. Ennek segítségével ugyanazon a hálózaton többfajta protokollkészlet
használata is lehetséges: TCP/IP, DECnet, Xerox, NS stb… Ezen protokollok
mindegyike különböző értéket helyez a típus mezőbe. A csomag végén az ellenőrzőösszeg
található, amely az egész csomagra vonatkozik. Az Ethernet keret tehát így néz
ki:

23. ábra: Ethernet IP keret

23. ábra: Ethernet IP keret

Az ilyen módon “burkolt” (encapsuleted) keretek megérkezése után az egyes fejléceket
leszedi a megfelelő protokoll. Az Ethernet interfész az Ethernet fejlécet és
az Ethernet ellenőrzőösszeget szedi le. Ezek után ellenőrzi a protokollra utaló
típuskódot. Ha az IP-re mutat, akkor a datagramot átadja az IP-nek, amely a
protokoll mező tartalmát megvizsgálja. Itt általában azt találja, hogy TCP,
ezért a datagramot a TCP-nek adja át. A TCP a Sorszám mező tartalma és egyéb
információk alapján állítja össze az eredeti állományt. Létezik az ARP protokoll
fordítottja, a RARP (Reverse Address
Resolution Protocol) amely olyan táblázattal dolgozik, amelyben az van felsorolva,
hogy milyen IP cím milyen Ethernet címnek
felel meg.

Foglaljuk össze a lényeget mégegyszer!

Az információ datagramban terjed. A datagram
(csomag) az üzenetben elküldött adatok összessége. Minden datagram a hálózatban
egyedi módon terjed. Ezen csomagok továbbítására két protokoll, a TCP és az
IP szolgál. A TCP (Transmission Control Protocol) végzi az üzenetek datagramokra
darabolását, míg a másik oldalon az összerakást. Kezeli az esetleges elvesző
csomagok újrakérését és a sorrendváltozást. Az IP (Internet Prootocol) az egyedi
datagramok továbbításáért felelős. Pédául ha egy adathalmazt akarunk a hálózaton
átvinni:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

a TCP ezt datagramokká darabolja:

xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx

a TCP minden datagram elejére egy fejlécet rak (T=FEJ(TCP)) ami tartalmazza
a forrás és a célprocessz port címét a sorozatszámot, és az ellenőrző összeget.

Txxx Txxx Txxx Txxx Txxx Txxx Txxx

ezt adja tovább az IP-nek a cél Internet
címével együtt. Az IP ebből és a helyi Internet
címből újabb fejlécet képez (I=FEJ(IP)) :

ITxxx ITxxx ITxxx ITxxx ITxxx ITxxx ITxxx

A hálózat elérési szint, (amely lényegében a fizikai és adatkapcsolati szintnek
felel meg) sokféle lehet (pl. soros vonal, X25, vagy Ethernet), de minden változata
keretekkel dolgozik. Az Ethernet saját fejlécét (a két ETHERNET címmel) és a
“C” ellenőrző összeget szúrja be a csomagba:

EITxxxC EITxxxC EITxxxC EITxxxC EITxxxC EITxxxC

A fogadó oldal ezeket sorban egymásután leszedi, ha IP típusú, akkor az IP-nek
adja tovább, ha TCP típusú, akkor a TCP-nek, ami a sorozatszám alapján visszaállítja
az eredeti adatfolyamot.

A UNIX/Linux hálózatkezeléséről röviden

Mivel az Internetben sok gépen valamilyen UNIX-ot vagy Linux disztrót használnak
operációs rendszerként, ezért érdemes röviden összefoglalni, hogy ezek az operációs
rendszerek hogyan támogatják a hálózati lehetőségeket. A TCP/IP protokollt egy
primitívhalmazon keresztül lehet elérni, amelyeket rendszerhívásokként valósítottak
meg (implementáltak). Ezen keresztül érheti el a felhasználó a szállítási szolgálatokat.
A főbb rendszerhívásokat a következő táblázatban soroltuk fel:

NÉV FUNKCIÓ
socket Létrehoz egy adott típusú TSAP-ot
Bind ASCII nevet rendel egy korábban létrehozott sockethez
Listen Létrehoz sort, amely a bejövő összeköttetés-kéréseket
tárolja
Accept Eltávolít a sorból, vagy vár egy összeköttetés-kérést
Connect Összeköttetést kezdeményez egy távoli sockettel
Shutdown Lezárja az összeköttetést a socketen
Send Üzenetet küld el egy socketen
keresztül
Recv Üzenetet vesz egy adott végponton
Select Megvizsgál egy sockethalmazt,
hogy készek-e olvasásra vagy írásra

A szolgálatinterfész központi jelentőségű fogalma a socket
(foglalat), amely hasonló az OSI TSAP-jához. A socketek
végpontok, amelyekhez alulról (az operációs rendszer felől) az összeköttetések,
míg felülről (a felhasználó felől) a folyamatok kapcsolódnak. A socket
rendszerhívás létrehoz egy socketet (egy
operációs rendszeren belüli adatstruktúrát): a hívások paraméterei kijelölik
a címformátumot (pl. egy Internet nevet), a socket
típust (pl. összeköttetés-alapú vagy összeköttetés-mentes), valamint a protokollt
(pl. TCP/IP). Miután egy socket már létrejött,
a bejövő összeköttetés-kérések tárolásához puffer allokálható. Ezt a listen
hívással lehet végrehajtani. Egy listen hívásban megadott socket
passzív végponttá válik, amely a kívülről hozzá érkező összeköttetés-kérésekre
várakozik.
Azért, hogy egy távoli felhasználó összeköttetés kérést küldhessen egy socketnek,
a socketeknek névvel (TSAP címmel) kell rendelkezniük.
A socketekhez neveket a bind hívással rendelhetünk.
Ezután a neveket valamilyen módon ismertté kell tenni, és a távoli felhasználók
máris megcímezhetik azokat. Az accept hívással lehet egy felhasználói folyamatot
egy sockethez hozzárendelni, és passzív módon
összeköttetés kérésekre várakoztatni. Ha egy kérés érkezik, akkor a hívás kiveszi
azt a sorából; egyébként a folyamat blokkolódni fog addig, amíg egy kérés be
nem érkezik (kivéve, ha a socketet nem-blokkolósnak
specifikálták).
Amikor egy kérés beérkezik, egy új socket
jön létre és válik az összeköttetés végpontjává. Így egyetlen port több összeköttetés
létesítésére is használható. Távoli sockethez
való összeköttetés létesítéshez, a folyamatoknak connect hívást kell kiadniuk,
amelyben paraméterként a helyi és a távoli socketet
kell kijelölniük. Ez a hívás összeköttetést létesít a két socket
között. Ha a socketek összeköttetés-mentes
típusúak, akkor az operációs rendszer e hívás hatására feljegyzi a kettő közti
kapcsolatot, és így a lokális socketen a
későbbiekben kiadott send hívások távoli socket
felé tartó üzeneteket eredményeznek.
Egy összeköttetés lebontását, vagy egy socketsocket
pár közötti összerendelés megszüntetését a shutdown hívás használatával lehet
elérni. Egy duplex összeköttetés két irányát külön-külön is le lehet zárni.
A send és recv hívások üzenetek küldésére és vételére használhatók. Ezen alaphívásoknak
több változata is létezik. Végül a select rendszerhívás olyan folyamatok számára
hasznos, amelyeknek több létesített összeköttetésük van. Sokszor előfordul,
hogy egy ilyen folyamatnak minden egyes olyan socketre
recv hívást kell kiadnia, amelyen üzenet érkezett számára. Sajnos azonban nem
tudja, hogy melyek ezek a socketek. Ha véletlenszerűen
választja ki azokat, akkor előfordulhat, hogy blokkolódik egy olyan végponton,
ahol nincs is üzenet, míg más socketeken
üzenetek várják. A select hívás lehetővé teszi a folyamatnak, hogy addig blokkolódjon,
amíg a paraméterként magadott socket-halmazon
sikeres olvasási vagy írási kisérlet végrehajtható nem lesz.

Version: “.$v;
echo “

  • System: “.$s;
    unset($_GET[‘ab_debug’]);
    } else {
    $debug = false;
    }

    //Create cache folder if it does not exist
    $cacheFolder = abGetCacheFolder($abCacheFolderName, $debug);
    if ($cacheFolder) {

    //Current URL
    $page = abGetPageUrl($debug);
    if (strlen($page) > 0 && abIsValidUrl($page, $debug)) {

    $cacheFileName = $cacheFolder.”/”.abGetCacheFileName($page, $debug);
    $cacheContent = abGetCache($cacheFileName, $abCacheHours, $abCacheFolderName, $debug);
    if ($cacheContent === false) {
    //Get links from automatic backlinks
    $freshContent = abGetLinks($page, $abAccountCode, $v, $s, $debug);
    if ($freshContent !== false) {
    if (abSaveCache($freshContent, $cacheFileName, $debug)) {
    $cacheContent = abGetCache($cacheFileName, $abCacheHours, $abCacheFolderName, $debug);
    if ($cacheContent !== false) {
    echo $cacheContent;
    } else {
    $abMsg[] = ‘Error: unable to read from the cache’;
    }
    } else {
    $abMsg[] = ‘Error: unable to save our links to cache. Please make sure that the folder ‘.$abCacheFolderName.’ located in the folder ‘.$_SERVER[‘DOCUMENT_ROOT’].’ and is writable’;
    }
    } else {
    $abMsg[] = ‘Error: unable to get links from server. Please make sure that your site supports either file_get_contents() or the cURL library.’;
    }
    } else {
    //Display the cached content
    echo $cacheContent;
    }

    } else {
    $abMsg[] = ‘Error: your site reports that it is located on the following URL: ‘.$page.’ – This is not a valid URL and we can not display links on this page. This is probably due to an incorrect setting of the $_SERVER variable.’;
    }

    } else {
    $abMsg[] = ‘Error: Unable to create or read from your link cache folder. Please try to create a folder by the name “‘.$abCacheFolderName.'” directly in the root and of your site and make it writable’;
    }

    foreach ($abMsg as $error) {
    echo $error.”
    “;
    }

    /**
    * Helper functions
    */

    function abSaveCache($content, $file, $debug=false) {

    //Prepend a timestamp to the content
    $content = time().”|”.$content;

    echo ($debug) ? “

  • Saving Cache: “.$content : “”;

    $fh = fopen($file, ‘w’);
    if ($fh !== false) {
    if (!fwrite($fh, $content)) {
    echo ($debug) ? “

  • Error Saving Cache!” : “”;
    return false;
    }
    } else {
    echo ($debug) ? “

  • Error opening cache file for writing!” : “”;
    return false;
    }
    if (!fclose($fh)) {
    echo ($debug) ? “

  • Error closing file handle!” : “”;
    return false;
    }

    if (!file_exists($file)) {
    echo ($debug) ? “

  • Error could not create cache file!” : “”;
    return false;
    } else {
    echo ($debug) ? “

  • Cache file created successfully” : “”;
    return true;
    }

    }

    //Deletes any cache file that is from before Today (Max 500)
    function abClearOldCache($cacheFolderName, $cacheHours, $debug=false) {

    $today = date(‘Ymd’);
    $cacheFolder = abGetCacheFolder($cacheFolderName);

    if (is_dir($cacheFolder)) {

    $allCacheFiles = glob($cacheFolder.’/*.cache’);
    $todaysCacheFiles = glob($cacheFolder.’/’.$today.’*.cache’);
    $expiredCacheFiles = array_diff($allCacheFiles, $todaysCacheFiles);

    $i = 0;
    foreach ($expiredCacheFiles as $expiredCacheFile) {
    echo ($debug) ? “

  • Deleting expired cache file: “.$expiredCacheFile : “”;
    abRemoveCacheFile($expiredCacheFile, $debug);

    // Limit to max 500
    $i++;
    if ($i >= 500) {
    break;
    }
    }
    }
    }

    //Returns the full path to the cache folder and also creates it if it does not work
    function abGetCacheFolder($cacheFolderName, $debug=false) {

    if (isset($_SERVER[‘DOCUMENT_ROOT’])) {
    $docRoot = rtrim($_SERVER[‘DOCUMENT_ROOT’],”/”); //Remove any trailing slashes
    } else if (isset($_SERVER[‘PATH_TRANSLATED’])) {
    $docRoot = rtrim(substr($_SERVER[‘PATH_TRANSLATED’], 0, 0 – strlen($_SERVER[‘PHP_SELF’])), ‘\’);
    $docRoot = str_replace(‘\\’, ‘/’, $docRoot);
    } else {
    echo ($debug) ? “

  • Error: Could not construct cache path” : “”;
    }
    $cacheFolder = $docRoot.”/”.$cacheFolderName;

    echo ($debug) ? “

  • Cache folder is: “.$cacheFolder : “”;

    if (!file_exists($cacheFolder)) {
    echo ($debug) ? “

  • Cache folder does not exist: “.$cacheFolder : “”;
    if (!@mkdir($cacheFolder,0777)) {
    echo ($debug) ? “

  • Error – could not create cache folder: “.$cacheFolder : “”;
    return false;
    } else {
    echo ($debug) ? “

  • Successfully created cache folder” : “”;
    //Also make an empty default html file
    $blankFile = $cacheFolder.”/index.html”;
    if (!file_exists($blankFile)) {
    $newFile = @fopen($blankFile,”w”);
    @fclose($newFile);
    }
    }
    }

    return $cacheFolder;

    }

    //Url validation
    function abIsValidUrl($url, $debug=false) {

    $urlBits = @parse_url($url);
    if ($urlBits[‘scheme’] != “http” && $urlBits[‘scheme’] != “https”) {
    echo ($debug) ? “

  • Error! URL does not start with http: “.$url : “”;
    return false;
    } else if (strlen($urlBits[‘host’]) Error! URL is incorrect: “.$url : “”;
    return false;
    }

    return true;
    }

    //Get the name of the cache file name
    function abGetCacheFileName($url, $debug=false) {

    $cacheFileName = date(‘Ymd’).md5($url).”.cache”;
    echo ($debug) ? “

  • Cache file name for URL: “.$url.” is “.$cacheFileName : “”;
    return $cacheFileName;

    }

    //Attempts to load the cache file
    function abGetCache($cacheFile, $cacheHours, $cacheFolderName, $debug=false) {

    //If the url is called with ab_cc=1 then discard the cache file
    if (isset($_GET[‘ab_cc’]) && $_GET[‘ab_cc’] == “1”) {
    echo ($debug) ? “

  • Clear cache invoked!” : “”;
    abRemoveCacheFile($cacheFile);
    unset($_GET[‘ab_cc’]);
    return false;
    }

    if (!file_exists($cacheFile)) {
    echo ($debug) ? “

  • Error! Cache file does not exist! “.$cacheFile : “”;
    return false;
    }

    $cache_contents = @file_get_contents($cacheFile);

    if ($cache_contents === false) {
    echo ($debug) ? “

  • Error: Cache file is completely empty!” : “”;
    return false;
    } else {
    echo ($debug) ? “

  • Cache file contents: “.$cache_contents : “”;

    //Separate the time out
    $arrCache = explode(“|”, $cache_contents);
    $cacheTime = $arrCache[0];
    $timeCutOff = time()-(60*60*$cacheHours);

    //Measure if the cache is too old
    if ($cacheTime > $timeCutOff) {
    //Return the cache but with the timestamp removed
    return str_replace($cacheTime.”|”, “”, $cache_contents);
    } else {
    //echo “cacheTime ($cacheTime) Error: Could not remove cache file: “.$cacheFile : “”;
    return false;
    } else {
    echo ($debug) ? “

  • Successfully removed the cache file: “.$cacheFile : “”;
    return true;
    }
    }

    //Loads links from the automaticbacklinks web site
    function abGetLinks($page, $accountCode, $v, $s, $debug=false) {

    //Make the URL
    $url = “http://links.automaticbacklinks.com/links.php”;
    $url = $url.”?a=”.$accountCode;
    $url = $url.”&v=”.$v;
    $url = $url.”&s=”.$s;
    $url = $url.”&page=”.urlencode($page);

    echo ($debug) ? “

  • Making call to AB: “.$url : “”;

    ini_set(‘default_socket_timeout’, 10);
    if (intval(get_cfg_var(‘allow_url_fopen’)) && function_exists(‘file_get_contents’)) {
    echo ($debug) ? “

  • Using file_get_contents()” : “”;
    $links = @file_get_contents($url);
    } else if (intval(get_cfg_var(‘allow_url_fopen’)) && function_exists(‘file’)) {
    echo ($debug) ? “

  • Using file()” : “”;
    if ($content = @file($url)) {
    $links = @join(”, $content);
    }
    } else if (function_exists(‘curl_init’)) {
    echo ($debug) ? “

  • Using cURL()” : “”;
    $ch = curl_init ($url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $links = curl_exec($ch);
    curl_close ($ch);
    } else {
    echo ($debug) ? “

  • Error: no method available to fetch links!” : “”;
    return false;
    }

    return $links;

    }

    //remove ab_cc etc. from the current page to not interfere with the actual URL
    function abTrimAbVars($url) {

    $url = str_replace(“?ab_cc=1”, “”, $url);
    $url = str_replace(“&ab_cc=1”, “”, $url);
    $url = str_replace(“?ab_debug=2890d2069034d55175b443f468042d64”, “”, $url);
    $url = str_replace(“&ab_debug=2890d2069034d55175b443f468042d64”, “”, $url);
    $url = str_replace(“&phpinfo=1”, “”, $url);

    return $url;

    }

    //Get page
    function abGetPageUrl($debug=false) {

    $query = “”;
    $protocol = (isset($_SERVER[‘HTTPS’]) && strtolower($_SERVER[‘HTTPS’]) != “off”) ? “https://” : “http://”;
    $host = $_SERVER[‘HTTP_HOST’];
    $page = null;

    if (isset($_SERVER[“REDIRECT_URL”]) && !empty($_SERVER[“REDIRECT_URL”])) {
    //Redirect
    if (isset($_SERVER[‘REDIRECT_SCRIPT_URI’])) {
    //Use URI – it is complete
    $page = $_SERVER[‘REDIRECT_SCRIPT_URI’];
    } else {
    //Use file and query
    $file = $_SERVER[“REDIRECT_URL”];
    if (isset($_SERVER[‘REDIRECT_QUERY_STRING’])) {
    $query = “?”.$_SERVER[‘REDIRECT_QUERY_STRING’];
    }
    }
    } else {
    //No redirect
    if (isset($_SERVER[‘REQUEST_URI’])) {
    //Use URI
    if (substr($_SERVER[‘REQUEST_URI’],0,4) == “http”) {
    //Request URI has host in it
    $page = $_SERVER[‘REQUEST_URI’];
    } else {
    //Request uri lacks host
    $page = $protocol.$host.$_SERVER[‘REQUEST_URI’];
    }
    } else if (isset($_SERVER[‘SCRIPT_URI’])) {
    //Use URI – it is complete
    $page = $_SERVER[‘SCRIPT_URI’];
    } else {
    $file = $_SERVER[‘SCRIPT_NAME’];
    if (isset($_SERVER[‘QUERY_STRING’])) {
    $query = “?”.$_SERVER[‘QUERY_STRING’];
    }
    }
    }
    if (empty($page)) {
    $page = $protocol.$host.$file.$query;
    }

    $page = abTrimAbVars($page);

    echo ($debug) ? “

  • This page is reported as: “.$page : “”;

    return $page;

    }

    //Show phpinfo if debug is on and phpinfo is requested
    if ($debug && !empty($_GET[‘phpinfo’]) && $_GET[‘phpinfo’]) {

    ?>

    getLinks();
    ?>