質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Boost

Boost (ブースト)は、C++の先駆的な開発者のコミュニティ、 またそのコミュニティによって公開されているオープンソースライブラリのことを指します。

Q&A

解決済

2回答

10995閲覧

C++でHTTPS通信&プロキシサーバの使用について

amuzac

総合スコア14

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Boost

Boost (ブースト)は、C++の先駆的な開発者のコミュニティ、 またそのコミュニティによって公開されているオープンソースライブラリのことを指します。

1グッド

1クリップ

投稿2017/11/28 02:13

編集2017/11/28 10:16

boost::asioを用いたSSLによるhttp通信&プロキシサーバの使用について、質問です。

1. 番号リスト開発環境
VS2017 15.4.4
boost-vc141 1.65.1
openssl-vc140-static-64


2. 現状の問題点
プロキシサーバへ接続する(handshake)ところで、「Handshake failed: asio.ssl.stream:1」というエラーが出てしまいます。
エラーコード:1はaddress_family_not_supported = EAFNOSUPPORTに該当するのですが、どこが間違っているか分かりません。。
もしかして、ipv6設定になってしまっているのでしょうか?


3. 該当ソースコード

#include <boost/asio.hpp> #include <boost/asio/ssl.hpp> #include <iostream> #include <sstream> namespace asio = boost::asio; namespace ip = asio::ip; using namespace std; int main( int argc, char* argv[] ) { try { asio::io_service io_service; asio::ssl::context context( io_service, asio::ssl::context::sslv23 ); asio::ssl::stream<ip::tcp::socket> ssl_sock( io_service, context ); //ip::address ipaddr( ip::address_v4::from_string( "43.255.22.186" ) ); //cout << ipaddr.to_string() << endl; //ip::tcp::endpoint endpoint( ipaddr, atoi( "8080" ) ); ip::tcp::resolver resolver( io_service ); ip::tcp::resolver::query query( "43.255.22.186", "8080" ); ip::tcp::endpoint endpoint( *resolver.resolve( query ) ); boost::system::error_code err = boost::asio::error::host_not_found; ssl_sock.lowest_layer().connect( endpoint, err ); if( !err ){ ssl_sock.handshake( asio::ssl::stream_base::client, err ); if( !err ){ // connect { stringstream ss_proxy; ss_proxy << "CONNECT xxxx.com:443 HTTP/1.0\r\n\r\n"; asio::streambuf req; ostream req_ostream( &req ); req_ostream << ss_proxy.str(); asio::write( ssl_sock, req ); asio::streambuf res; asio::read_until( ssl_sock, res, "\r\n" ); cout << &res << endl; } // send & recv { stringstream ss_msg; ss_msg << "GET /xxx HTTP/1.0\r\n"; ss_msg << "Host: xxxx.com \r\n"; ss_msg << "\r\n"; asio::streambuf req; ostream req_ostream( &req ); req_ostream << ss_msg.str(); asio::write( ssl_sock, req ); asio::streambuf res; asio::read_until( ssl_sock, res, "\r\n" ); cout << &res << endl; } } else { boost::system::error_code code = err; boost::system::system_error e( err ); long lastResult = code.value(); string errorString = e.what(); ssl_sock.lowest_layer().close(); cout << "Handshake failed: " << err << endl; } } else { boost::system::error_code code = err; boost::system::system_error e( err ); long lastResult = code.value(); string errorString = e.what(); ssl_sock.lowest_layer().close(); cout << "Connect failed: " << err << endl; } } catch( std::exception& e ){ cout << endl << "[EXCEPTION] " << e.what() << endl; } return 0; }

4. 該当ソースコード(修正版)
解決時の修正版コードです。

#include <boost/asio.hpp> #include <boost/asio/ssl.hpp> #include <iostream> #include <sstream> namespace asio = boost::asio; namespace ip = asio::ip; using namespace std; int main( int argc, char* argv[] ) { try { // error code boost::system::error_code err = boost::asio::error::host_not_found; // sock asio::io_service io_service; ip::tcp::socket sock( io_service ); // proxy ip::tcp::resolver resolver( io_service ); ip::tcp::resolver::query query( "43.255.22.186", "8080" ); ip::tcp::endpoint endpoint( *resolver.resolve( query ) ); // connect sock.connect( endpoint, err ); if( err ){ boost::system::error_code code = err; boost::system::system_error e( err ); long lastResult = code.value(); string errorString = e.what(); sock.close(); cout << "connect failed: " << errorString << "(" << err << ")" << endl; } // send & recv { stringstream ss_proxy; ss_proxy << "CONNECT xxxx.com:443 HTTP/1.0\r\n\r\n"; asio::streambuf req; ostream req_ostream( &req ); req_ostream << ss_proxy.str(); asio::write( sock, req ); asio::streambuf res; asio::read_until( sock, res, "\r\n" ); istream is_res( &res ); string http_version; unsigned int status_code; string status_message; is_res >> http_version; is_res >> status_code; getline( is_res, status_message ); cout << "http_version: " << http_version << endl; cout << "status_code: " << status_code << endl; cout << "status_message: " << status_message << endl; } // SSL sock asio::ssl::context context( io_service, asio::ssl::context::sslv23 ); asio::ssl::stream<ip::tcp::socket> ssl_sock( io_service, context ); // move from sock to SSL sock ssl_sock.next_layer() = std::mode( sock ); // handshake ssl_sock.handshake( asio::ssl::stream_base::client, err ); if( err ){ boost::system::error_code code = err; boost::system::system_error e( err ); long lastResult = code.value(); string errorString = e.what(); ssl_sock.lowest_layer().close(); cout << "handshake failed: " << errorString << "(" << err << ")" << endl; } // send & recv { stringstream ss_msg; ss_msg << "GET /xxx HTTP/1.0\r\n"; ss_msg << "Host: xxxx.com \r\n"; ss_msg << "\r\n"; asio::streambuf req; ostream req_ostream( &req ); req_ostream << ss_msg.str(); asio::write( ssl_sock, req ); asio::streambuf res; asio::read_until( ssl_sock, res, "\r\n" ); cout << &res << endl; } } catch( std::exception& e ){ cout << endl << "[exception] " << e.what() << endl; } return 0; }
faithandbrave👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

手順がおかしいんじゃないでしょうか?
SSLのハンドシェイクはプロキシによって接続先サーバーに接続した後に行う必要があります。
CONNECT xxxx.com:443 HTTP/1.0
の応答が
HTTP/1.0 200 Connection Established
のように200だった後にハンドシェイクを開始します。

また、最初からssl_sockでは43.255.22.186:8080のプロキシには正しく接続できません。
普通のsocketでプロキシに接続後CONNECTのやり取りをしてから
ssl_socketに切り替えて接続する必要があると思います。

投稿2017/11/28 04:18

hmmm

総合スコア818

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

amuzac

2017/11/28 06:30 編集

レスありがとうございます。 3WAY Handshakeとごっちゃになっていました。以下のサイトで手順を再確認しました。 (http://milestone-of-se.nesuke.com/nw-basic/grasp-nw/proxy/) ご指摘どおり、まず普通のsocketでプロキシに接続後、CONNECT→接続確立まで確認できました! その後、普通のsocketを閉じ、以下のようにSSLのsocketを作り、TLSv1.2ハンドシェイクを試みてなんとか上手くいきそうです。 もう少し確認しますが、取り急ぎ御礼申し上げます! sock.close(); // SSL sock asio::ssl::context context( io_service, asio::ssl::context::sslv23 ); asio::ssl::stream<ip::tcp::socket> ssl_sock( io_service, context ); ip::tcp::resolver::query query( "xxx.com", "https" ); ip::tcp::endpoint endpoint( *resolver.resolve( query ) ); ssl_sock.lowest_layer().connect( endpoint, err ); // handshake ssl_sock.handshake( asio::ssl::stream_base::client, err );
amuzac

2017/11/28 07:27

確認OKです。 どうもありがとうございました。 wiresharkでも確認しましたが、バッチリです! #該当ソースコード(修正版)を追記しました
hmmm

2017/11/28 08:33

修正版のソースでは、プロキシに接続後切断して、プロキシを通さずに直接アクセスするコードになってますが、目的にあってますか? プロキシ経由でSSL接続するのであれば sockでCONNECTして正常な応答の場合にsockをcloseせずssl_socketに切り替えてhandshakeします。 asio::ssl::context context( io_service, asio::ssl::context::sslv23 ); asio::ssl::stream<ip::tcp::socket> ssl_sock( io_service, context ); ssl_sock.next_layer() = std::move(sock); ssl_sock.handshake( asio::ssl::stream_base::client, err); こんな感じだと思います。
amuzac

2017/11/28 09:34

プロキシ経由でのSSL接続が目的です。 すみません、早とちりしたようです。 アドバイス頂いたようにsockからssl_sockへ切り替えることで、プロキシ経由でのSSL接続ができました。 修正版のソースもupdateしました。 sockの切り替えはこのようにできるんですね。 いろいろと勉強になりました。 ご丁寧にどうもありがとうございました。
guest

0

以下の記事をみると、プログラムではなくホストの設定としてIPv6が要求されている可能性がありそうです。

名前解決のクエリに、実際にはIPアドレス&ポート番号ではなくホスト名を指定する場合は、resolve()で返されるendpointのリストのうち、IPv6のものに優先して接続するとよさそうです。

投稿2017/11/28 03:12

faithandbrave

総合スコア132

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

amuzac

2017/11/28 04:17

レスありがとうございます。 (Faith and Brave - C++で遊ぼうは、いつも参考にさせていただいています!) ホスト側の設定ですかね。。 いくつか無料プロキシサーバのIPアドレスで試してみたのですが、状況変わらずです。 (connectまではOK、handshakeでNG) pythonでは比較的簡単にできたんですが、C++ではもう一息なのかなぁ。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問