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

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

ただいまの
回答率

91.35%

  • C++

    2412questions

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

  • Boost

    25questions

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

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

解決済

回答 2

投稿 2017/11/28 11:13 ・編集 2017/11/28 19:16

  • 評価
  • クリップ 1
  • VIEW 475

amuzac

score 7

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;
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+3

手順がおかしいんじゃないでしょうか?
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 13:18

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/28 15: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 );

    キャンセル

  • 2017/11/28 16:27

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

    キャンセル

  • 2017/11/28 17: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);
    こんな感じだと思います。

    キャンセル

  • 2017/11/28 18:34

    プロキシ経由でのSSL接続が目的です。
    すみません、早とちりしたようです。

    アドバイス頂いたようにsockからssl_sockへ切り替えることで、プロキシ経由でのSSL接続ができました。
    修正版のソースもupdateしました。

    sockの切り替えはこのようにできるんですね。
    いろいろと勉強になりました。
    ご丁寧にどうもありがとうございました。

    キャンセル

+1

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

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

投稿 2017/11/28 12:12

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/28 13:17

    レスありがとうございます。
    (Faith and Brave - C++で遊ぼうは、いつも参考にさせていただいています!)

    ホスト側の設定ですかね。。
    いくつか無料プロキシサーバのIPアドレスで試してみたのですが、状況変わらずです。
    (connectまではOK、handshakeでNG)

    pythonでは比較的簡単にできたんですが、C++ではもう一息なのかなぁ。

    キャンセル

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

ただいまの回答率

91.35%

関連した質問

同じタグがついた質問を見る

  • C++

    2412questions

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

  • Boost

    25questions

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