libcurlをiPhoneアプリからつかう方法 (ついでにopensslも)

iPhoneアプリから自己証明書の https サーバーに接続しようと思った場合、どうするのがいいでしょう。

普通に Objective-CNSURLConnection を使用すると証明書の検証エラーになってしまいます。少し検索すると非公開APIを使用して回避する方法もあるようです。(NSURLConnection +setAllowsAnyHTTPSCertificate:forHost)

Cocoa アプリだとこの方法で良いかもしれません。しかし iPhone では審査ではじかれること請け合いです。と言うかはじかれました。

そこで libcurl をつかって C の世界で HTTP 接続をしてしまえばリジェクトしようがないだろうということでやってみたときの記録です。

前提

以後の作業はすべて iPhone SDK をインストールした OSX 上で行っています。

openssl のビルド

まず https 接続前提なので openssl が必要になります。openssl についてはすでにやっている人がいてhowto記事を書いてくれています。

The Rare AiR ? Tutorial: How To Compile OpenSSL for the iPhone

ここに書かれている手順そのままで、現在の最新版(0.9.8l)もビルドすることが出来ました。

また、ここでは上記の記事 config を

./config --prefix=$HOME/tmp/iphonelib/openssl

とし、SDKパスは iPhoneOS3.0.sdk として作業をすすめたとします。

ただしくビルドが完了すると ~/tmp/iphonelib/openssl/lib 以下に

  • libcrypto.a
  • libssl.a

と言う二つの静的リンクライブラリができているはずです。

libcurl のビルド

つぎに目的の libcurl をビルドします。ダウンロードページ から最新版(ここでは7.19.7)をダウンロードします。

展開し、以下のように configure を叩きます。

./configure --prefix=$HOME/tmp/iphonelib/curl \
    --host=arm-apple-darwin --disable-shared --with-random=/dev/urandom \
    CC=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc \
    CFLAGS="-arch armv6 -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk -I$HOME/tmp/iphonelib/openssl/include -L$HOME/tmp/iphonelib/openssl/lib" \
    CPP=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/cpp \
    AR=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ar

出力の最後に

SSL support:     enabled (OpenSSL)

と言う行が出ているのを確認してください。出ていれば https サポートが有効な libcurl をビルドできる準備が整いました。

あとは

make
make install

でビルド完了です。

~/tmp/iphonelib/curl/liblibcurl.a というライブラリが出来ていることを確認し、できていればOKです。

作ったライブラリをiPhoneアプリから使用する準備

何か適当にプロジェクトを作成しましょう。(ここではcurlと言うプロジェクトを作成しました)

そして、

libcurl

このように先ほど作成した3つのライブラリファイル:

  • libcrypto.a
  • libssl.a
  • libcurl.a

をプロジェクトに追加します。

また、openssl が zlib に依存しているのでそちらもプロジェクトに追加する必要があります。このライブラリはすでにiPhoneに入っているので、自分でビルドする必要はありません。

プロジェクト設定を開き、

-lz

このようにリンク項目の中の「他のリンクフラグ」という項目に

-lz

を加えればOKです。これで必要なライブラリの読み込みは完了です。

実際に使用するためにはヘッダファイルをただしく読み込めるように設定する必要があります。再びプロジェクト設定を開き、

header search path

検索パス項目の中の「ヘッダ検索パス」に libcurl のヘッダパスを指定します。上で説明したビルド手順どおりであれば、~/tmp/iphonelib/curl/include を指定すればOKです。

またここで $(SRCROOT) などの変数を指定することができ、$(SRCROOT) は xcode プロジェクトのルートディレクトリをさします。スクリーンショットでは /Users/typester/dev/iphone/scratch/curl/libcurl と言うディレクトリを指定しているように見えますが、これは実際には $(SRCROOT)/libcurl という設定を行っています。

このように、インストールしたディレクトリをそのままつかわず、ヘッダをプロジェクト内へコピーして使用するとプロジェクトごと他のマシンへ移動することが簡単にできるのでおすすめです。

作ったライブラリをiPhoneアプリから使用

やっと準備が整いました。あとは通常どおり libcurl を使用することが出来ます。ここでは少し横着して、AppDelegate 内にコードを追加して試してみます。

#import "AppDelegate.h"

#include <curl/curl.h>

@implementation AppDelegate

-(void)applicationDidFinishLaunching:(UIApplication *)application {
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // Override point for customization after application launch
    [window makeKeyAndVisible];

    CURL *curl;
    CURLcode res;

    curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "https://twitter.com/");
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);

        res = curl_easy_perform(curl);
        if (0 != res) {
            fprintf(stderr, "curl error: %d\n", res);
        }

        curl_easy_cleanup(curl);
    }
}

-(void)dealloc {
    [window release];
    [super dealloc];
}

@end

こんなようなコードになりました。

これをビルドして実行で実機で動かしてみましょう。うまくいけば、デバッグコンソールに取得したhtmlが表示されるはずです。

https:__twitter.com

うごきましたね!

シミュレータでもlibcurlをつかう

いまのままではこのコードは実機でしか動かすことが出来ません。なぜならシミュレータとiPhoneではCPUアーキテクチャが違うため先ほどビルドした openssl や curl のライブラリが動作しないためです。

シミュレータで開発できないとかなり開発効率がおちますね。

これを解決するために先ほどと同様にしてシミュレータ用にもライブラリをビルドしておきましょう。

手順はまったく同じで、gccやsysrootへのパスの iPhoneOS.platform 部分を iPhoneSimulator.platform に、また -arch armv6-arch i386 に変更してあとはビルドするだけでOKです。

しかし同じ名前のライブラリファイルをプロジェクトに追加しても有効にならないため、出来たライブラリを

  • libcrypto_simulator.a
  • libssl_simulator.a
  • libcurl_simulator.a

などに変更してからプロジェクトに突っ込みましょう。これで、実機ビルドの時は実機側のライブラリが、シミュレータの場合はシミュレータ用のライブラリが使用されるようになります。

もしくはビルドターゲットを追加し、それでもってライブラリを切り替えるという方法もあります。 こちらの場合は同じファイル名で追加してもOKです。またアーキテクチャが違うと言う警告もでないため本来であればこちらを使うべきなのでしょう。

まとめ

以上のような方法で、curl および openssl がiPhoneアプリから利用できるようになります。

このようにビルドさえ出来てしまえば既存のライブラリをそのまま使用できるのは iPhone のメリットと言えるでしょう。 特にこのような枯れたライブラリの再発明を Objective-C でやる必要はありません。どんどん車輪を使って効率の良い開発をしていきましょう。