読者です 読者をやめる 読者になる 読者になる

mruby-maxminddb という mgem を作った話

このブログは mruby Advent Calendar 2016 の 21 日めの記事です。

最近,と言っても少し前ですが,mgem を作りました.

作った mgem の名前は mruby-maxminddb って言います.

GioIP の DB である maxminddb を読み書きする Gem です.

MaxMindDbDat = "/tmp/GeoLite2-City.mmdb"
IPAddr = '8.8.8.8'

maxminddb = MaxMindDB.new MaxMindDbDat

maxminddb.lookup_string IPAddr

maxminddb.country_code       #=> US
maxminddb.region             #=> CA
maxminddb.region_name        #=> California
maxminddb.city               #=> Mountain View
maxminddb.postal_code        #=> 94035
maxminddb.latitude           #=> 37.386
maxminddb.longitude.round(4) #=> -122.0838
maxminddb.metro_code         #=> 807
maxminddb.time_zone          #=> America/Los_Angeles

などのように利用します。

例えば,Webサーバーやメールサーバーのスパム対策に利用できるかと思います。

幸いにも maxminddb を利用するための C 言語のライブラリがあったので,こちらをラップする形で実装していきました。 libmaxmind と言います。

libmaxmind はドキュメントが充実しており,それらを参考に実装していきました。

また, maxminddb をダンプするための mmdblookup というツールも同梱されており,そちらのコードはとても参考になりました。 ほぼ,このツールの動作を模倣する形で実装を進めていきました。

static mrb_value mrb_maxminddb_lookup_string(mrb_state *mrb, mrb_value self) {
  mrb_maxminddb_data *data = DATA_PTR(self);
  char *ipaddr = NULL;

  mrb_get_args(mrb, "z", &ipaddr);
  data->host = ipaddr;

  data->lookup_result_s =
      MMDB_lookup_string(&(data->mmdb), data->host, &(data->gai_error),
                         &(data->mmdb_lookup_error));

  if (0 != data->gai_error) {
    mrb_raise(mrb, E_RUNTIME_ERROR, gai_strerror(data->gai_error));
  }

  if (MMDB_SUCCESS != data->mmdb_lookup_error) {
    mrb_raise(mrb, E_RUNTIME_ERROR, "Lookup Error");
  }

  return self;
}

例えば,上記のようなコードは, mmdblookup.c の似た関数を模倣して実装しています。

mgem として実装するので,エラーが発生したときに mrb_raise を返すところや, 構造体に MMDB_lookup_string の戻り値を格納しているところが違うぐらいです。

その構造体は mruby で C 言語の構造体をラップしたオブジェクトを作る正しい方法 を参考にして mruby のオブジェクトとしてラップしています。

mruby-maxminddb は初めて C 言語で mgem を作るには手頃なお題でした。 libmaxminddb というライブラリもあり,ドキュメントも充実しており,参考にできるコードも同梱されていました。

mgem を作るにあたって参考になるブログもありました。

来年も,幾つか mgem を開発していきたいと思います。

以上です。