UCSPI
UCSPI: UNIX Client-Server Program Interface (elvileg úgy kéne ejteni, hogy ooks-pie, úkszpáj; mi maradjunk az ú-cé-es-pé-í-nél :)
Tartalomjegyzék |
1 Mire való?
Kliens-szerver architektúrájú programok egymás közötti kommunikációjának néhány konvencióját fekteti le.
Alapötlet: válasszuk szét a programok hálózati részét a nemhálózati résztől. Ezt gyakran meg lehet tenni, mert egy kliensprogram számára igazából csak az a fontos, hogy beszélhessen egy szerverrel, de igazából mindegy neki, hogy ez milyen konkrét átviteltechnikával történik.
Az UCSPI a kliens számára biztosít két fájldeszkriptort: amit az egyikbe ír, azt megkapja a szerver, és amit a szerver mond, azt kiolvashatja a másikból.
Szerveroldalon kb. úgy működik, mint az inetd: figyel egy (csak egy!) porton, és ha kapcsolat jön, elindítja a szerveralkalmazást úgy, hogy a standard inputja és outputja is a kapcsolat socketje legyen.
Előnyök:
- (Majdnem) független a protokolltól; pl. elvileg transzparens a számára, hogy IPv4 vagy IPv6 van alatta.
- A hálózati funkciót elválasztja az alkalmazástól, így egy egyszerű shellscript is lehet szerver vagy kliens.
- Egy csomó információt környezeti változókba rak, így akár scriptekből is könnyen felhasználhatjuk őket.
- A nem kifejezetten UCSPI-hez írt (szerver)programok jó része is működik így.
- Azáltal, hogy egy szerver csak egy porton figyel, a szerverek konfigurációja automatikusan szét van választva; futhatnak különböző chrootban stb.
2 Alapvető működés
Egy UCSPI-eszköz parancssora ilyesmi:
[tool] [options] [address] [application]
- tool: maga az eszköz
- options: a saját opciói, pl. verbozitás
- address: szerver esetén itt kell figyelni, kliens esetén ide kell kapcsolódni
- application: az elindítandó kliens vagy szerver neve és opciói
Ha létrejön a kapcsolat, tool elindítja a kliens- vagy szerveralkalmazást.
Vegyük észre, hogy igazából itt a tool is csak egy olyan program, mint mondjuk a chpst a runitnál: beállít valamilyen kezdeti környezetet, majd exec() hívással ebben a környezetben indít el egy gyermekfolyamatot. Itt a környezet létrehozásához néhány socket megnyitása is hozzá tartozik.
Így amúgy elvileg megfordíthatjuk a kliens-szerver működést: pl. megcsinálhatjuk, hogy az axfrdns-ünk "kapcsolódjon" egy (UCSPI-t támogató) AXFR-kliensre.
Az UCSPI-kliens által indított alkalmazás
- a 6-os fájlleírón olvashatja a szerver üzeneteit,
- a 7-es fájlleiróra pedig írhatja azt, amit a szervernek mondani szeretne.
- Az stdin-t és az stdout-ot a szülőjétől örökli (ha interaktívan futtatjuk, a konzol marad az stdin/stdout).
- Így nem UCSPI-felhasználásra készült program sajnos nem használható UCSPI-kliensként (mert nem tudják, hogy a 6-os és a 7-es fájlleíró különleges). Workaround (nem próbáltam, de mennie kéne):
#!/bin/zsh exec $1 $@ >&7 <&6
(Ez egy olyan script, ami a neki átadott programot úgy futtatja, hogy annak az stdin/stdout párosára rámásolja a saját 6-os ill. 7-es fájlleíróját.)
3 Protokollfüggetlenség
Elvileg az UCSPI "nagyjából" protokollfüggetlen.
Egy-egy tool általában egy protokollt támogat; gondoljunk rájuk úgy, mint hálózati kommunikációs backendekre.
Egy protokoll a következő dolgokat határozza meg:
- address formátumát és jelentését;
- azt, hogy milyen környezeti változókat kell beállítani.
Így persze lehet olyan kliens- vagy szerveralkalmazást írni, ami protokollfüggő, mert olyan környezeti változót szeretne, ami csak az egyik protokollra jellemző, de nem muszáj.
A toolok a következő környezeti változókat állítják be:
- PROTO: a protokoll, amit használunk. TCP esetén pl. TCP.
- PROTOLOCAL*: a kapcsolat helyi végére jellemző adatok, pl:
- TCPLOCALIP: a kapcsolathoz tartozó helyi IP-cím;
- TCPLOCALPORT: a kapcsolathoz tartozó helyi port;
- TCPLOCALHOST: a helyi IP-hez tartozó név (vagy üres);
- PROTOREMOTE*: a kapcsolat túlvégére jellemző adatok, pl:
- TCPREMOTEIP: a távoli IP (szervernél a kliens IP-je, kliensnél a szerver IP-je);
- TCPREMOTEPORT, TCPREMOTEHOST: értelemszerű;
- TCPREMOTEINFO: távoli usernév (ident/auth szolgáltatás révén), vagy üres.
A szervertoolok általában a kliens IP-jének/nevének függvényében egyéb környezeti változókat is be tudnak állítani.
4 Példa
Minden ilyesmit példákon keresztül a legegyszerűbb megérteni.
4.1 qmail-smtpd
Nézzük pl. a qmail SMTP-daemonját, a qmail-smtpd-t. Ez egy elég egyszerű kis program, ami a konfigurációja jelentős részét környezeti változókból veszi. Saját hálózatkezelés nincs benne; az ucspi-tcp csomag tcpserver programja (ami egy UCSPI-szervertool) nyújtja neki a TCP-porton való figyelést, mint szolgáltatást.
Egy qmail-smtpd-parancssor pl. a következőképpen festhet:
#!/bin/sh exec 2>&1 exec env - PATH="$PATH" \ /usr/bin/softlimit -d 10485760 \ envuidgid qmaild \ /usr/bin/tcpserver -U -c20 -v -h -p -t3 -x /etc/tcp.smtp.cdb -X 0 smtp \ /usr/bin/rblsmtpd \ -rcn-kr.blackholes.us \ -rblackholes.wirehub.net \ /usr/sbin/qmail-smtpd
Lássuk, mi minden van itt:
- env: környezettisztítás; egyedül a PATH változót örökítjük át a gyermekfolyamatainkra.
- softlimit: a daemontools csomag egyik chpst-szerű segédprogramja, erőforráskorlátokat állít be, majd exec-kel elindít egy gyermekfolyamatot.
- envuidgid: szintén a daemontoolsból van; ez az UID és a GID környezeti változókat állítja be a megadott user (itt qmaild) user- ill. group ID-jára.
- Így külön programba szerveződött ki az NSS (Name Service Switch) kezelése; a gyermekfolyamat már numerikus ID-kat kap az UID/GID változókban.
- tcpserver: egy TCP-porton figyel. A paraméterek jelentése:
- -U: a port megnyitása után válts az UID változóban megadott user- és a GID változóban megadott group ID-ra.
- -c20: legfeljebb 20 párhuzamos kapcsolatot fogadj el (legfeljebb ennyi példány futhat a qmail-smtpd-ből).
- -v: légy szószátyár (mindenféle státusz-üzenetekkel szórakoztat minket).
- -h: oldd fel a kliens nevét a DNS-ben és állítsd be a TCPREMOTEHOST változót a qmail-smtpd elindítása előtt.
- -p: paranoid működés: az imént kapott neved oldd fel a DNS-ben, és ha a kapott A rekordok egyike sem egyezik meg a kliens IP-jével, töröld a TCPREMOTEHOST változót.
- -t3: legfeljebb 3 másodpercig próbálkozz a TCPREMOTEINFO (távoli usernév) megszerzésével.
- -x /etc/tcp.smtp.cdb: a megadott cdb-fájlban felsorolt szabályok szerint egyes kliensek számára állíts be további környezeti változókat is.
- -X: akkor is fogadd el a kapcsolatokat, ha a megadott cdb-fájl nem létezik.
- 0: a 0.0.0.0-ás IP-n (vagyis a gép összes interface-ének összes IP-jén) nyújtsd a szolgáltatást.
- smtp: az "smtp" nevű szolgáltatás portján figyelj (általában a /etc/services fájlban vannak a szolgáltatásnév-portszám-párosok).
- rblsmtpd: ez egy minimális smtp-szerver, ami megnézi, hogy a kliens (TCPREMOTEIP) szerepel-e a megadott RBL-ekben, és ha igen, mond neki egy 451-et; ha nem, elindítja a megadott programot, ami itt a qmail-smtpd, és átadja neki a klienst.
- qmail-smtpd: végül elindul maga a qmail-smtpd és lebonyolítja az SMTP-tranzakciót.
A qmail-smtpd működését az alábbi környezeti változók befolyásolják (ezeknek egy része a SPAMCONTROL nevű patch-csel jön):
- DATABYTES: legfeljebb mekkora levelet küldhet a kliens.
- HELODNSCHECK: ellenőrizze-e, hogy a HELO-ban átadott névnek van-e MX vagy A rekordja (noha elvileg csak A kéne hogy legyen, az MX irreleváns); ha nincs, 451-es kódot mond.
- RELAYCLIENT: ha be van állítva, a kliens bárhova küldhet levelet (olyat is, ami nem a mi gépünknek szól).
- MAXRECIPIENTS: legfeljebb hány címzettje lehet a levélnek.
- MFDNSCHECK: ha be van állítva, visszautasítja a levelet (451), ha a MAIL FROM-ban szereplő domainnek sem MX, sem A rekordja nincs.
- stb.
Látható, hogy ezeknek az értéke kliensfüggő kéne, hogy legyen. Az egyes kliensekre vonatkozó szabályokat a tcp.smtp.cdb fájlban adhatjuk meg.
A tcp.smtp.cdb egy szövegfájlból készül, amelyben ilyesmik vannak (a formátum neve amúgy tcprules, az után a program után, ami cdb-vé fordítja):
127.0.0.1:allow,RELAYCLIENT="",RBLSMTPD="",MAXRECIPIENTS="100" 1.2.3.:allow,HELODNSCHECK="",MFDNSCHECK="",MAXRECIPIENTS="100" 1.2.3.4:deny 1.2.5-15.:allow,RBLSMTPD=/-mail blocked. Keep your spam./ :allow,HELODNSCHECK="",MFDNSCHECK="",MAXRECIPIENTS="10"
(Ez valószínűleg részben átkerül majd az ucspi-tcp ill. a qmail szócikkbe, ha elkészülnek.)
4.2 axfr-get
Az axfr-get egy egyszerű AXFR-kliens, amivel egy domain zónafájlját tudjuk letölteni egy szerverről.
Futtatása:
tcpclient -R -v a.root-servers.net 53 axfr-get . rootzone rootzone.tmp
5 Potenciális ZH-kérdések
- Vázolja fel egy UCSPI-s szolgáltatás architektúráját! Milyen elemekből áll? Ezek hogyan kommunikálnak egymással?