挨拶と前書き
みなさんこんにちは、絵とウーバーイーツが楽しいなおこです。
今回はPythonのプログラムをかいている際にrequests使って情報の取得を行おうとしましたがクライアント側はIPv6対応しているけどサーバー側のウェブサーバーはIPv4しか対応していない、けどAAAAレコードにはIPv6が振られているというときにIPv6で接続をしようとしてしまうみたいなガチのレアケースに遭遇しました。
requests自体にipv4やipv6を切り替える機能が無いためOS側から変更しようと思いましたが使っている環境がレンタルサーバーなどのOSのシステム領域をいじれない環境だったため、プログラム側から調整できないかとのgithubやstackoverflowを覗いてみると同じような問題に遭遇している方が数名居ました。
要はソケットいじればうまく行くよってことらしい。
本題
ソケットをいじるのも良いですが、接続ごとに指定してかんたんにできる物ないかな〜って調べていると先程のissuesの最後にipv4やipv6の選択ができるようにパッチを当てたラッパーがあるらしい↓
requests_wrapper.pyをダウンロードしてプログラムを同じディレクトリに設置して使ってやってみたけど、、、https環境のサイトには使えない!httpは行ける
import socket
import requests_wrapper as requests
response = requests.get("http://ipv4.google.com/", family=socket.AF_INET)
print (response.status_code)
print (response.text)
じゃあhttpsのipv4でできないの?と思うかもしれませんができます。ラッパーを使わず、数行を追記するだけでipv4の通信にしたりipv6にしたり両方にしたりすることができます。
実際にプログラムを書いていく
ipv4だけの接続
ipv4の場合はこのようになります。
import socket
import requests
# これ↓
import requests.packages.urllib3.util.connection as urllib3_cn
def allowed_gai_family4(): return socket.AF_INET
urllib3_cn.allowed_gai_family = allowed_gai_family4
# これ↑
response = requests.get("https://ipv4.google.com/")
print (response.status_code)
print (response.text)
ipv6だけの接続
今度は逆にipv6へ接続してみましょうsocket.AF_INETに6を追加するだけでipv6だけに制限することができます。
import socket
import requests
# これ↓
import requests.packages.urllib3.util.connection as urllib3_cn
def allowed_gai_family6(): return socket.AF_INET6 #ここ変更
urllib3_cn.allowed_gai_family = allowed_gai_family6 #ここ変更
# これ↑
response = requests.get("https://ipv6.google.com/") #ここ変更
print (response.status_code)
print (response.text)
その他
デフォルトに戻すには?(IPv6とipv4の両方)
import socket
import requests
# これ↓
import requests.packages.urllib3.util.connection as urllib3_cn
def allowed_gai_family(): return socket.AF_UNSPEC #ここ変更
urllib3_cn.allowed_gai_family = allowed_gai_family #ここ変更
# これ↑
response = requests.get("https://ipv6.google.com/")
print (response.status_code)
print (response.text)
事前に関数を用意しておけば動的に変更することも可能です。
import socket
import requests
# これ↓
import requests.packages.urllib3.util.connection as urllib3_cn
def allowed_gai_family(): return socket.AF_UNSPEC
def allowed_gai_family4(): return socket.AF_INET
def allowed_gai_family6(): return socket.AF_INET6
urllib3_cn.allowed_gai_family = allowed_gai_family
# これ↑
# ipv6 or ipv4
urllib3_cn.allowed_gai_family = allowed_gai_family
response = requests.get("https://google.com/")
print (response.status_code)
# ipv4
urllib3_cn.allowed_gai_family = allowed_gai_family4
response = requests.get("https://ipv4.google.com/")
print (response.status_code)
# ipv6
urllib3_cn.allowed_gai_family = allowed_gai_family6
response = requests.get("https://ipv6.google.com/")
print (response.status_code)
最後に
ちなみに、ブログで紹介するプログラムには例としてgoogleのサイトを使用しています。ipv4.google.comやipv6.google.comは適切にAAAAレコードやAレコードが設定されているためプログラム側でいじる必要もないし問題ないですが、個人サイトのAPIで実際にあった怖い話ですね。
The post Python OS側でIPv6制限せずにプログラムで制限を行いIPv4 requestsを行う方法 first appeared on FascodeNetwork Blog.