Kokeellinen PostgreSQL-tietokantatuki Sendmailiin

Olen viime aikoina pörrännyt jonkin verran Sendmail MTA:n C-koodin parissa. Tarkoitus on lisätä Sendmailiin tuki PostgreSQL-tietokannalle.

Nyt olen jo sen verran pitkällä, että ensimmäinen proof of concept julkaisu toimii. Otin kehityksen kohteeksi Sendmailin testiversion 8.16.0.29. Kuten huomaat, Compiled with: sisältää oman lisäykseni PGMAP muiden tietokantatyyppien lisäksi:

[root@localhost ~]# /tmp/obj.Linux.4.19.13-200.fc28.x86_64.x86_64/sendmail/sendmail -bt -d0.1 < /dev/null
Version 8.16.0.29
Compiled with: DNSMAP IPV6_FULL LDAPMAP LOG MAP_REGEX MATCHGECOS MILTER
MIME7TO8 MIME8TO7 NAMED_BIND NETINET NETUNIX NEWDB PGMAP
PIPELINING SASLv2 SCANF SOCKETMAP STARTTLS TLS_VRFY_PER_CTX
USERDB USE_LDAP_INIT XDEBUG

Sendmailin /etc/mail/sendmail.mc konfiguraatiotiedostossa käytetään PostgreSQL:n URL-syntaksia -H valitsimen kanssa ja -k valitsimella valitaan mikä tietokantataulu PostgreSQL:n kannassa on hakujen kohteena:

[root@localhost ~]# grep pg /etc/mail/sendmail.mc
FEATURE(`virtusertable', `pg -H postgresql://kalevi:pw777@127.0.0.1:5432/kalevidb -k vtable')dnl
FEATURE(`mailertable', `pg -H postgresql://kalevi:pw777@127.0.0.1:5432/kalevidb -k mtable')dnl

Olen kovakoodannut hakuavaimen kentän nimeksi SQL-kantaan key ja arvoksi value. Kannoissa ei ole juuri yhtään dataa, vaan ainoastaan sen verran, että pystyy testaamaan. Tässä näytteeksi psql-komennon tulostetta:

kalevidb=# select * from vtable;
id | key | value
------+------------------------+--------------------------
1973 | david.bowie@leary.org | possuekaarvo@leary.org
2019 | possuekaarvo@leary.org | bowie@heaven.example.com
(2 rows)

kalevidb=# select * from mtable;
id | key | value
-----+--------------+--------------------
777 | .example.com | esmtp:[127.0.0.73]
(1 row)

Sendmail ei käytä GNU Autoconf-työkaluja käännösaikaa ennen, vaan siinä on oma m4-makroihin perustuva käännössysteeminsä. Käänsin Sendmailin laittamalla lähdekoodipuun tiedostoon devtools/Site/site.config.m4 seuraavat asetukset:

define(`confBLDVARIANT', `DEBUG')
define(`confMAPDEF', `-DNEWDB -DMAP_REGEX -DLDAPMAP -DLDAP_DEPRECATED -DSOCKETMAP -DNAMED_BIND=1 -DSASL=2 -DNIS=0')
define(`confCCOPTS', `-W -Wall -g3')
APPENDDEF(`confLIBDIRS', `-L/usr/lib64/libdb4')
APPENDDEF(`confENVDEF',`-DSTARTTLS')
APPENDDEF(`confLIBS', `-lssl -lcrypto -ldb -lldap -llber -lresolv -lpq -lsasl2')
APPENDDEF(`confINCDIRS', `-I/usr/include/openssl -I/usr/include/libdb4 -I/usr/include/sasl')
APPENDDEF(`confMAPDEF',`-DPGMAP')

Pistin upouuden -DPGMAP määrittelyni omalle APPENDDEF riville loppuun, mutta se voisi toki yhtä hyvin olla confMAPDEF rivillä. Sendmailin käänsin lähdekoodipuun päätasolla antamalla komennon:

./Build -O /tmp

Olin laittanut domainin leary.org tiedostoon /etc/mail/virtuserdomains, jotta Sendmail käyttäisi virtusertablea sen domainin osoitteiden kohdalla. Virtusertable on siis nyt määritelty olemaan uutta tyyppiä pg eli PostgreSQL-tietokanta.

Lopuksi testasin miten uusi Sendmailin testiversio resolvoi osoitteen david.bowie@leary.org.

[root@localhost ~]# /tmp/obj.Linux.4.19.13-200.fc28.x86_64.x86_64/sendmail/sendmail -d38.2 -bv david.bowie@leary.org
seq_map_parse(aliases.files, )
pg_map_open(virtuser, 0): opening new connection
sm_pg_resolve(0x1e88140, table='vtable' key='david.bowie@leary.org', value='possuekaarvo@leary.org'
sm_pg_resolve(0x1e88140, table='vtable' key='possuekaarvo@leary.org', value='bowie@heaven.example.com'
pg_map_open(mailertable, 0): using cached connection
sm_pg_resolve(0x1e88140, table='mtable' key='heaven.example.com', value='<NULL pointer>'
sm_pg_resolve(0x1e88140, table='mtable' key='.example.com', value='esmtp:[127.0.0.73]'
david.bowie@leary.org... deliverable: mailer esmtp, host [127.0.0.73], user bowie@heaven.example.com
pg_map_close(virtuser) entered
pg_map_close(virtuser) done
pg_map_close(virtuser): closed mailertable (shared pg connection)

Täytyy myöntää, että olen erittäin tyytyväinen siihen miten tämä PG-tietokantahomma nyt jo toimii! Koodia pitää tietysti vielä hioa, mutta yllättävän hienosti kaikki tuntuu sujuvan. Otin tietysti mallia olemassaolevasta LDAP-koodista ja myös muista osista kantakoodia.

Tämän projektin monimutkaisuutta lisää se, että Sendmailin kanta-abstraktiossa pyritään siihen, että kantayhteys jaetaan useampien tietokantamappien kesken. Näin ollen tarkoitus on, että jos vaikkapa virtusertable ja mailertable ovat kumpikin samassa tietokannassa, niin Sendmailin pitää avata vain yksi kantayhteys PostgreSQL:ään ja kummankin mapin kyselyt pitää tehdä saman TCP-putken kautta.

Jos tutkit ylläolevaa david.bowie@leary.org osoiteresoluutiota, niin näet, että virtuserin kohdalla Sendmail avaa yhteyden PostgreSQL-kantaan, mutta mailertablen kohdalla se käyttääkin cachessä olevaa yhteyttä, joka siis on juuri se, jonka haku virtuserista on jo aiemmin aukaissut.

Itseasiassa olen luultavasti kuluttanut noin 40 tuntia lukien Sendmailin koodia ennenkuin ryhdyin tekemään omaa koodiani. Rimakauhuakin oli, mutta tällä hetkellä Sendmail tuntuisi tekevän tietokantahaut järkevästi PostgreSQL-kannasta.

Huonojakin uutisia on: En nimittäin ole vielä implementoinut mitään timeoutteja tietokantayhteyksien muodostukseen tai hakujen kestoon. Kantayhteyden timeoutin saanee lisättyä URL-parametrien joukkoon, mutta kun haku on tehty, niin en täysin varmaksi tiedä miten timeouttaus pitäisi toteuttaa.