複数個のグローバルIPが割り当てられたroot権限付きのLinix(CentOS 5.3 64bit)サーバがあります。

内部のPHPスクリプトから外部のAPIサーバへ、メインIPではない公開しても良いサブIPアドレス(複数)をアクセス元としてHTTPリクエストを送る方法があれば教えて下さい。

可能であれば動的に割り当てられたグローバルIPを自由に選択して外部にアクセスが出来ると理想です。
言い換えると、発信元のIPアドレスをLinux上の何らかのコマンドでサーバを稼働させたまま動的に変更する事は出来ますでしょうか。

※ ListenさせるIP毎にproxyサーバを建てる方法は避けたいです

回答の条件
  • 1人3回まで
  • 登録:
  • 終了:2009/07/07 20:28:14
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:y-kawaz No.2

回答回数1422ベストアンサー獲得回数226

ポイント30pt

こんな感じでしょうか?

<?php
function my_http_get($host, $path='/', $source_ip='0.0.0.0') {
  // 新しいソケットを作成する
  $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  // ソースアドレスをバインドする
  socket_bind($sock, $source_ip);
  // 接続先ホストと接続する
  socket_connect($sock, gethostbyname($host), 80);
  // リクエストを書き込む
  $request = "GET {$path} HTTP/1.0\r\n"
    ."Host: {$host}\r\n"
    ."\r\n";
  socket_write($sock, $request);
  // レスポンスを取得
  $response = '';
  while(1) {
    $buf = socket_read($sock, '1024');
    if($buf === '') {
      break;
    }
    $response .= $buf; 
  }
  // ソケットを閉じる
  socket_close($sock);
  // HTTPレスポンスからヘッダ部分を削除
  $content = preg_replace('/^.*?\r\n\r\n/sm', '', $response);
  return $content;
}

//接続元IPを指定してGET (a.b.c.dは自分のIP)
echo my_http_get('www.yahoo.co.jp', '/', 'a.b.c.d');
?>

ソケットを直接扱えば接続元IPの指定が出来ます。

ただしその場合はHTTPリクエストを作ったりレスポンスからコンテンツを分離する作業なども自分でやる必要もあります。

上記は a.b.c.d を帰れば一応動くと思いますが、エラー処理などは全くしてないので実際に使うにはもう少しキチンと書く必要があると思います。

id:ko-takada

求める動作が行われる事を確認しました。ありがとうございます。素晴らしいです!!

実際に使う場合、例えばどのようなエラー処理をする必要があるか、箇条書きでもかまわないので頂けると幸いです。

404, 500などのエラー処理やLocationヘッダが返ってくる事の他に、何か対応が必要そうな物はありますでしょうか?

2009/07/05 12:12:23

その他の回答2件)

id:b-wind No.1

回答回数3344ベストアンサー獲得回数440

ポイント16pt

複数個のグローバルIP

これらはネットワーク的には同一サブネットに属しますか?

同一サブネットでなければ route コマンドでルーティング情報を制御することにより、

特定の相手先への経路を変更することが出来ます。

Manpage of ROUTE


可能であれば動的に割り当てられたグローバルIPを自由に選択して外部にアクセスが出来ると理想です。

動的に、というところがちょっとやっかいですが、iproute2 を使えばおおよそ希望した動作が得られるかと。

NETMARKとiproute2でトラフィックを完全掌握 − @IT


こういった動作は業務用ルーターではさして珍しくない機能として持っていることが多いですが、

Windows/Linux などの汎用のOSではほとんど有りません。

id:ko-takada

紹介頂きありがとうございます。

LIDSを組み合わせると出来そうなのですが、デーモンではなくphpという小さなスクリプトなため、コントロール対象とするのは難しそうです。

2009/07/05 11:55:46
id:y-kawaz No.2

回答回数1422ベストアンサー獲得回数226ここでベストアンサー

ポイント30pt

こんな感じでしょうか?

<?php
function my_http_get($host, $path='/', $source_ip='0.0.0.0') {
  // 新しいソケットを作成する
  $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  // ソースアドレスをバインドする
  socket_bind($sock, $source_ip);
  // 接続先ホストと接続する
  socket_connect($sock, gethostbyname($host), 80);
  // リクエストを書き込む
  $request = "GET {$path} HTTP/1.0\r\n"
    ."Host: {$host}\r\n"
    ."\r\n";
  socket_write($sock, $request);
  // レスポンスを取得
  $response = '';
  while(1) {
    $buf = socket_read($sock, '1024');
    if($buf === '') {
      break;
    }
    $response .= $buf; 
  }
  // ソケットを閉じる
  socket_close($sock);
  // HTTPレスポンスからヘッダ部分を削除
  $content = preg_replace('/^.*?\r\n\r\n/sm', '', $response);
  return $content;
}

//接続元IPを指定してGET (a.b.c.dは自分のIP)
echo my_http_get('www.yahoo.co.jp', '/', 'a.b.c.d');
?>

ソケットを直接扱えば接続元IPの指定が出来ます。

ただしその場合はHTTPリクエストを作ったりレスポンスからコンテンツを分離する作業なども自分でやる必要もあります。

上記は a.b.c.d を帰れば一応動くと思いますが、エラー処理などは全くしてないので実際に使うにはもう少しキチンと書く必要があると思います。

id:ko-takada

求める動作が行われる事を確認しました。ありがとうございます。素晴らしいです!!

実際に使う場合、例えばどのようなエラー処理をする必要があるか、箇条書きでもかまわないので頂けると幸いです。

404, 500などのエラー処理やLocationヘッダが返ってくる事の他に、何か対応が必要そうな物はありますでしょうか?

2009/07/05 12:12:23
id:y-kawaz No.3

回答回数1422ベストアンサー獲得回数226

ポイント34pt

404や500などのHTTPステータス毎の処理もそうですが、それとは別にsocket通信そのもののエラー処理もちゃんとした方がよいです。

  • socket_bindが失敗する可能性(自分のIP以外を指定しまうなど)
  • gethostbynameが失敗する可能性(ネットワーク障害やDNSサーバの問題など)
  • socket_connectが失敗する可能性(ネットワーク障害や相手サーバの問題など)
  • socket_writeが失敗する可能性
  • socket_readの応答が返ってこない可能性(相手サーバの問題などでwriteしても応答が返ってこなかったり、Keep-Aliveされてしまったりなど)

エラー処理の典型的なやりかた等はPHPのマニュアルのサンプルやネット上の情報が色々あるのでsocket関係の関数名をキーワードに探せば色々見つかると思います。

id:ko-takada

ありがとうございます!

2009/07/06 09:34:20

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません