主夫ときどきプログラマ

データベース、Webエンジニアリング、コミュニティ、etc

GoogleAppEngine Python でIPアドレスを操作する方法

GoogleAppEngineでアクセス元のIPアドレスによって処理を分けたい場合があります。(主にガラケー対応)
その場合IPアドレスを表現するオブジェクトを使いたいところですが、その用途にあったPythonモジュールなかなか見つかりません。
そこで今回は

  • IP Address をオブジェクトとして扱う
  • IP Address をCIDR表記で範囲として扱う

という2点をPythonでクリアするための方法を紹介します。

IPy モジュールを使う

Freeで公開されているIPyというモジュールを使います。

IPy - A python Module for handling IP-Addresses and Networks
http://c0re.23.nu/c0de/IPy/

こちらで紹介されているモジュールはPerlのNet::IPに似たモジュールで、IPv6にも対応しています。
最新版をダウンロードしましょう
IPy-0.42.tar.gz

まずはPythonインタプリタで動作確認

解凍すると IPy-0.42/build/lib/IPy.py が得られるので lib ディレクトリの階層でPythonインタプリタを起動します。

$ tar xzvf IPy-0.42.tar.gz
$ IPy-0.42/build/lib
$ python

IPy.pyはそのままではunicode errorが起こるので、762, 763の特殊文字を消します。
(コピーライトの文字が入っていました。)

では試しにOverviewにあるようにIPyを使ってみましょう。

>>> import IPy
>>> ip = IPy.IP('120.3.4.5/1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "IPy.py", line 294, in __init__
    raise ValueError, "%s goes not well with prefixlen %d" % (hex(self.ip), self._prefixlen)
ValueError: 0x78030405L goes not well with prefixlen 1

するとこのようにエラーが起こってしまいます。
すこしソースコードを編集してみましょう。1220行目をコメントアウトし、1221行目のようにします。

1218 def _checkNetaddrWorksWithPrefixlen(net, prefixlen, version):
1219     """Check if a base addess of e network is compatible with a prefixlen"""
1220     #if net & _prefixlenToNetmask(prefixlen, version) == net:
1221     if net:
1222         return 1
1223     else:
1224         return 0
1225 

これで準備完了!
Pythonインタプリタで動作確認をしましょう。

GoogleAppEngine での利用

IPy モジュールを GoogleAppEngine で使うためにソースファイルのIPy.py をGoogleAppEngieのルートディレクトリに配置します。
(GoogleAppEngineLaunchdr の Path に表示されているところにコピーします。)

$ mv IPy.py /path/to/gae/root

では、SDK Console の Interactive Console で動作確認をしよう。
Textareaに

import IPy
ip = IPy.IP('192.168.1.100')

と打って [Run Program] ボタンを押し、右側にエラーが表示されなければ成功。
この状態になれば、下層の*.pyでもimportすることができます。


では実際のコードで使ってみましょう。
サンプルとして、携帯キャリアのIP帯域からのアクセスかを判定するメソッドを作ります。

import IPy
from google.appengine.ext import webapp

class Main(webapp.RequestHandler):
'''
  中略
'''

    def include(self):
        # 簡素化するためにRequestParameterでキャリアとIPが送られてくる前提です^_^;
        carrier = self.request.get('carrier')
        remote_ip = IPy.IP(self.request.get('ip'))
        elif carrier == 'softbank':
            soft_ip = [
                IPy.IP('123.108.237.0/27'),   IPy.IP('202.253.96.224/27'),  IPy.IP('210.146.7.192/26'),  IPy.IP('123.108.237.224/27'),
                IPy.IP('202.253.96.0/27')]
            # CIDRで範囲指定されたIP群の中に含まれているかを判断します
            for ip in soft_ip:
                if remote_ip in ip:
                    return True
            return false
        else:
            return False

こんな感じでIPyを使う事でIPアドレスにより制御をGoogleAppEngineで実現できます。