まんぷく

Eximのlocal_scan動的読込み機能 その3

前回は本当にさわりだけだったので、今回は実際に役に立つ機能を加えたいと思います。実際にどんな機能を実装しようかと考えたのですが、以前qmailでMTAを運用しているときに独自に追加したGeoIP関連の処理を追加することにしました。

GeoIPとは

GeoIPはMaxMind社が提供しているサービス及びデータベースの事で、IPアドレスから国や地域を調べることが出来ます。基本有料のサービスなのですが、精度が少し低くて更新が自動で出来ないバージョンは無料で利用出来ますので、今回はこれを利用します。

GeoIPデータベースを使うと国や地域だけでなく都市名なども分かるのですが、今回は国名と座標をメールヘッダーに追加します。

GeoIPライブラリのインストール

GeoIPを利用するためにまずライブラリのインストールを行います。MaxMindの開発者向けページを見るとGeoIP2というのも出てきていますが、これから実装する機能にはLegacy版で十分ですので、Debianにあるパッケージを使ってちゃっちゃとインストールします。他のディストリビューションやOSをお使いの方は先ほどのページを参考に頑張って下さい。

sudo apt-get install libgeoip1
sudo apt-get install libgeoip-dev
sudo apt-get install geoip-database
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
gunzip GeoLiteCity.dat.gz
sudo mv GeoLiteCity.dat /usr/share/GeoIP

これで開発の準備は完了です。

local_scanの作成(geoip-exim.c)

さっそくソースコード見て下さい。前回の説明した最小限の構成と比べるとlocal_scan関数に少しばかりコードが増えていますが、やっていることは簡単で以下のような事を行っています。

  • データベースをオープン
  • メール送信者のIPアドレスを元にデータベースを検索
  • 検索結果を元にX-Geoip-CountryとX-Geoip-Coordinatesヘッダーを追加
  • 検索結果の後始末
  • データベースをクローズ
#include <local_scan.h>
#include <GeoIP.h>
#include <GeoIPCity.h>

#define MAX_LENGTH 998

static const char *_mk_NA(const char *p)
{
  return p ? p : "N/A";
}

int local_scan_version_major(void)
{
  eturn LOCAL_SCAN_ABI_VERSION_MAJOR;
}

int local_scan_version_minor(void)
{
  return LOCAL_SCAN_ABI_VERSION_MINOR;
}

int local_scan(int fd, uschar **return_text)
{
  fd = fd;
  return_text = return_text;

  /* open geoip database */
  GeoIP *gi;
  GeoIPRecord *gir;
  char header[MAX_LENGTH];
  gi = GeoIP_open("/usr/share/GeoIP/GeoLiteCity.dat", GEOIP_MEMORY_CACHE);
  if (gi == NULL) return LOCAL_SCAN_ACCEPT;

  gir = GeoIP_record_by_addr(gi, (const char*)sender_host_address);
  if (gir == NULL) {
    GeoIP_delete(gi);
    return LOCAL_SCAN_ACCEPT;
  }

  snprintf(header, MAX_LENGTH, "X-Geoip-Country: %s\n", _mk_NA(gir->country_code));
  header_add((int)' ', header);
  snprintf(header, MAX_LENGTH, "X-Geoip-Coordinates: %f/%f\n", gir->latitude, gir->longitude);
  header_add((int)' ', header);
  GeoIPRecord_delete(gir);
  GeoIP_delete(gi);
  return LOCAL_SCAN_ACCEPT;
}

コンパイルして配置します。

gcc -Wall -I/usr/include -I/usr/include/exim4 -Wl,-R/usr/share/GeoIP -fPIC -c geoip-exim.c
gcc -shared -export-dynamic -ldl -Wl,-soname,libGeoIPExim.so.1 -olibGeoIPExim.so.1.0 geoip-exim.o -L/usr/lib -lGeoIP
sudo cp libGeoIPExim.so.1.0 /usr/lib/exim4/local_scan/
cd /usr/lib/exim4/local_scan/
sudo ln -s libGeoIPExim.so.1.0 libGeoIPExim.so

Eximに設定を追加して再起動します。

local_scan_path = /usr/lib/exim4/local_sacn/libGeoIPExim.so

届いたメールにX-Geoip-*から始まるヘッダーが二つ追加されていれば問題無く動作しています。local_scanがクラッシュしてもExim本体側で絵エラーハンドリングされるはずですが、テストもしないでいきなり本番サーバーで実行したりなど無謀な事はしないで下さい。何が起こっても当局は一切関知しません。

Categories
tech

Tags
Exim4 / Debian / GeoIP