Windows-XPとiPhoneでのWifiルーター化
さてはて、先日(ちと遅い)夏休みとして、青春18きっぷで奈良へ行ってきました。もちろん鈍行乗り継ぎなので、片道9時間ぐらいかかります。半分ぐらい寝てましたが、後の半分はiPhoneに大量に入れていった、DVDなどから変換したビデオを見てました。おかげでぜんぜん飽きずに9時間(x2)が終わって楽勝でしたよ。
その時、一緒に持っていったラップトップ(Windows-XP SP3)で、iPhoneをWifiルーターとして上手く使う方法を確立したので、まとめて見ます。(今まで何度か試していたのだけれど、いまいち上手く行っていなかったのでした。結構めんどうくさいです。)
基本的な方針
WindowsからJailbreakしたiPhoneへWifi接続(adhoc)をした後に、PuttyのSOCKSフォーワーディング機能を使って、windowsからの接続をiPhone経由で3Gネット側へ確立できるようにする。
(しかし、そう簡単には行かないんだよなぁ、これが、、、)
必要なプログラム等
- Putty - http://www.chiark.greenend.org.uk/~sgtatham/putty/
- FreeCap - http://www.freecap.ru/eng/
- そしてもちろんJailbreakしたiPhone
設定
Wifi接続の設定
まずWindowsとiPhoneをWifiで接続しますが、一対一での接続でいいのでadhoc接続をします。
ここでは、iPhoneのIPアドレスを10.0.0.1、Windows側を10.0.0.2とします。
- まず「ネットワーク接続」開いて(「スタート」->「接続」->「すべての接続を表示」)、ワイヤレス・ネットワークの接続を探します。
- そのプロパティを表示し「ワイヤレス ネットワーク」のタブを選びます。
- 一番下の「詳細設定」を押して、「アクセスするネットワーク」の中から「コンピューター相互(ad hoc)のネットワークのみ」を選びます。(後で戻しておくと良いでしょう。)
- 「詳細設定」を閉じて「プロパティ」に戻り、「優先ネットワーク」の「追加...」を選びます。
- 「SSID」を適当なものに(ここでは「adhoc」)設定、「キーは自動的に提供される」のチェックをはずす。
- 「ネットワークキー」とその入力確認が入れられるようになるので、適当なパスワードを(5文字、もしくは13文字)を両方へ入力
- 「OK」を押して「プロパティ」に戻り、また「全般」のタブに戻る。
- 「インターネット プロトコル(TCP/IP)」を選択し、「プロパティ」を押す
- 「次のIPアドレスを使う」を選択し、「IPアドレス」に「10.0.0.2」、「サブネットマスク」に「255.0.0.0」、「デフォールト ゲートウエイ」に「10.0.0.1」を入力
- 「OK」で「プロパティ」へ戻り、また「OK」で「プロパティ」を閉じる
これで設定は終了。接続してiPhone側の設定をします。
- iPhone側で「設定」->「Wi-Fi」に「adhoc」が出ていたらそれに接続、無かったら「その他…」を開き、名前:「adhoc」、セキュリティ:「WEP」、パスワード:(さっきWindowsで設定したパスワード)を入力し「Join」します。
- Windows側で、「ネットワーク接続」にある、ワイヤレス・ネットワークの接続をダブルクリックして「ワイヤレス・ネットワークの選択」を表示します。
- 登録した「adhoc」が見えるので、それを選択して「接続」を押します。
- 接続できたらiPhone側上で、この「adhoc」接続の設定を開き、IPアドレスの設定を「DHCP」から「静的」にします。
- 「IPアドレス」を「10.0.0.1」に、「サブネットマスク」を「255.0.0.0」にします。
これで、設定できました。
メモ:通常Windows同士でad hoc接続をする時には、IPアドレスはWindowsが擬似DHCPサーバーになってくれるので、お互いに相手先からIPアドレスが取れます。しかし、iPhoneはこれをしてくれないので、今回の場合iPhone側はIPアドレスが取れますが、Windows側はIPアドレスが取れません。なので、静的にIPアドレスを設定してやる必要があります。
こうすると、他のWiFiアクセスポイントに接続する場合などに、非常に不便なので、「ヴァルヘルIPコンフィグ」 - http://www.valsoft.jp/delphi/vipconfighp.html 等の設定スイッチ・ユーティリティを使用することをお勧めします。
SOCKSトンネルの作成
次にPuttyでiPhoneにアクセスしてSOCKSトンネルを作ります。
あまり知られていないのかもしれませんが、Putty(もしくはOpenSSH)には、SSHアクセスの他に、特定のポートのフォーワーディングや、SOCKSによるダイナミックなフォーワーディング(トンネリング)を作ることができます。今回はこの機能を使って、Windows上にSOCKSポート(localhost:1080)を作って、iPhone上までトンネルを作ります。
- まずPuttyを立ち上げて、iPhone Wifiルーター用の設定を作ります。
- 「ホスト名(または IPアドレス)」に「10.0.0.1」を入れます。
- 「カテゴリ」から「接続」を選び、「keep aliveの間隔(秒、0でオフ)」を2秒程度に設定します。
- 「カテゴリ」から「接続」->「SSH」->「トンネル」を選びます。
- 「源ポート」に「1080」を、「送り先」に「localhost」、その下のラジオボタンを「ダイナミック」にします。
- そこで「追加」ボタンを押します。(すると上のリストに「AD1080」と表示される)
- また、最初の所へ戻るために、「カテゴリ」から「セッション」を選ぶ。
- 「保存されたセッション」の所へ適当な名前を付けて保存を押す。(「iPhoneAdhoc」など)
これでトンネル設定、終了です。
iPhoneにWifi接続後、Puttyでこの設定を読み込んで「開く」をすれば、SSH接続のウインドウが表示され、それと同時にSOCKSトンネルが確立されます。(閉じる時には、SSHのウインドウに「exit」と入れれば閉じます。)
メモ:この設定の途中で「keep aliveの間隔」を数秒に設定していますが、これをしないとWiFi接続がすぐに切れてしまいました。それの対策として設定しています。(ここがまた、はまった所だったのでした、、、)
SOCKS用のユーティリティFreeCap を設定する。
この状態でSOCKSトンネルが動いているので、一応、SOCKS対応のIEやFirefoxの設定でこれを指定してあげれば、接続できるのですが、DNSがSOCKSに対応していないため名前が引けないのと、毎回SOCKSの設定するのが面倒なので、ここは登録したアプリケーションのすべてのアクセスを強制的にSOCKS経由にするFreeCapと言うユーティリティを使うことにします。( http://www.freecap.ru/eng/ )
- FreeCapを立ち上げて「File」メニューから「Settings」を選びます。
- 「Default Proxy」の「Server」に「localhost」を、「Port」に「1080」を入れて「OK」を押します。
- 「File」メニューから「New Application」を選びます。
- アプリケーションの設定画面が出るので「Browse...」を押してSOCKS上で使うアプリケーションを選びます。
- すると、必要な情報が入るので問題なさそうなら「Ok」を押します。
- メイン画面に設定したアプリケーションが現れるので、それをダブルクリックするとSOCKS対応状態でアプリケーションが開きます。
(English) Grouped TableView Sample
(This entry is English translate of previous post.)
HelloPython, sample program of python on iPhone, is nice demonstration of how python code runs on iPhone. but I wanted more practical sample like this. (with some buttons, labels...)
so, I made one.
This uses TableView with GroupedStyle (UITableViewStyleGrouped).
To use, you can paste this code into HelloPython.py.
and in the middle of this code, there is "self.sections_" settings like this.
self.sections_ = [ {"header": "header 1", "items": [{"title": "title 1", "action": lambda p: self.callback(p)}]}, {"header": "header 2", "items": [ {"title": "title 2-1", "action": None}, {"title": "title 2-2", "action": None}] } ]
You can configure the application by changing this settings. (clicking on "title 1" shows alert dialog by defalt.)
Enjoy iPhone programing.
(Ado Nishimura - email: ado at sig dot or dot jp)
- Grouped TableView Sample code in Python.
import sys import objc from _uicaboodle import UIApplicationMain from objc import YES, NO, NULL objc.loadBundle("UIKit", globals(), "/System/Library/Frameworks/UIKit.framework") class PYApplication(UIApplication): @objc.signature("i@:@") def numberOfSectionsInTableView_(self, table): return len(self.sections_) @objc.signature("i@:@i") def tableView_numberOfRowsInSection_(self, table, section): return len(self.sections_[section]["items"]) @objc.signature("@@:@@") def tableView_cellForRowAtIndexPath_(self, table, path): cell = table.dequeueReusableCellWithIdentifier_("PYApplication") if cell == None: zrect = ((0, 0), (0, 0)) cell = UITableViewCell.alloc().initWithFrame_reuseIdentifier_(zrect, "PYApplication") cell.setText_(self.sections_[path.section()]["items"][path.row()]["title"]) return cell @objc.signature("@@:@i") def tableView_titleForHeaderInSection_(self, table, section): return self.sections_[section]["header"] @objc.signature("v@:@@") def tableView_didSelectRowAtIndexPath_(self, table, path): action = self.sections_[path.section()]["items"][path.row()]["action"] if action != None: action(path) def callback(self, path): UIAlertView.alloc().initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles_("", "You cliked on a TableView", None, "Ok", None).show() @objc.signature("v@:@") def applicationDidFinishLaunching_(self, unused): self.sections_ = [{"header": "header 1", "items": [{"title": "title 1", "action": lambda p: self.callback(p)}]}, {"header": "header 2", "items": [{"title": "title 2-1", "action": None}, {"title": "title 2-2", "action": None}] } ] # init window. outer = UIHardware.fullScreenApplicationContentRect() self.window = UIWindow.alloc().initWithFrame_(outer) self.window.orderFront_(self) self.window.makeKey_(self) self.window.setHidden_(NO) inner = self.window.bounds() navsize = UINavigationBar.defaultSize() navrect = ((0, 0), (inner[1][0], navsize[1])) # init UIView self.view = UIView.alloc().initWithFrame_(self.window.bounds()) self.window.setContentView_(self.view) # init Navi-bar self.navbar = UINavigationBar.alloc().initWithFrame_(navrect) self.view.addSubview_(self.navbar) self.navbar.setBarStyle_(1) navitem = UINavigationItem.alloc().initWithTitle_("Grouped TableView") self.navbar.pushNavigationItem_(navitem) # init Table View lower = ((0, navsize[1]), (inner[1][0], inner[1][1] - navsize[1])) table = UITableView.alloc().initWithFrame_style_(lower, 1) # grouped TableView self.table = table self.view.addSubview_(table) table.setSeparatorStyle_(1) table.setDelegate_(self) table.setDataSource_(self) table.reloadData() UIApplicationMain(sys.argv, PYApplication)
PythonでのiPhoneアプリ-GroupedTableViewサンプル
前に書いた、Pythonで作るiPhoneアプリのサンプルを見ていたのだけれど、アドレスブックの内容(名前のみ)の一覧をざーっとテーブルにするだけで、あまり面白くない。(sqliteとの連携が簡単って所はいいのだけど。)
もっとよくある感じの画面に沢山ボタン等が出てる感じのサンプルを作ってみた。
実はこれもテーブル(TableView)の一種で、TableViewを作るときにstyleを「UITableViewStyleGrouped」に指定すると、こんな感じのを作ることができる。
簡単にこのボタン等を変えられるように、プログラム中で、、、
self.sections_ = [ {"header": "header 1", "items": [{"title": "title 1", "action": lambda p: self.callback(p)}]}, {"header": "header 2", "items": [ {"title": "title 2-1", "action": None}, {"title": "title 2-2", "action": None}] } ]
の、ように設定すると、自動的にボタンが表示されるようにしてみた。(「title 1」のボタンを押すと、とりあえずアラートが表示される。)
これを作るのにやっぱり一番苦労したのは、Objective-C Type Encodingsで、@objc.signature("i@:@")見たいな奴。これがPythonのメソッドと合わないとエラーになったり、Python側でとんでもない値が渡って来たりする。
ちょっと簡単に解説すると、、、
@objc.signature("i@:@")
def numberOfSectionsInTableView_(self, table):
これ「i@:@」は、この関数(メソッド)の戻り値や引数を表している。
- 最初の文字は戻り値の型で、この場合の「i」はInteger/NSInteger
- 2文字目は「self」を表していて、この場合の「@」はオブジェクト一般
- 「:」はメソッドセレクタ
- その後は引数で、この場合はオブジェクト(「@」)一個
#文字種は「i」「@」ぐらいしか使わない。文字列も大抵NSStringと言うオブジェクトを使うので、出てくるのはほとんど「i」「@」で、たまに「v」(void)を見る程度。詳しくはType Encodingsに載っているのを参照。
ちょっと分かりづらいのは、Objective-Cではメソッド名=パラメーター識別名で、メソッド名なのか、パラメーター名なのか分かりづらい。「numberOfRowsInSection」メソッドを例に取ると、、、
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
これが、これになる、、、
@objc.signature("i@:@i")
def tableView_numberOfRowsInSection_(self, table, section):
要は、「numberOfRowsInSection」がメソッド名と思わずに、メソッド名は、「tableView」+「numberOfRowsInSection」と考えるわけ。理解するのに時間がかかりました。
参考リンク:
- Table View Programming Guide for iPhone OS
- UITableView Class Reference
- The Objective-C 2.0 Programming Language - Type Encodings
Grouped TableViewサンプルプログラム
import sys import objc from _uicaboodle import UIApplicationMain from objc import YES, NO, NULL objc.loadBundle("UIKit", globals(), "/System/Library/Frameworks/UIKit.framework") class PYApplication(UIApplication): @objc.signature("i@:@") def numberOfSectionsInTableView_(self, table): return len(self.sections_) @objc.signature("i@:@i") def tableView_numberOfRowsInSection_(self, table, section): return len(self.sections_[section]["items"]) @objc.signature("@@:@@") def tableView_cellForRowAtIndexPath_(self, table, path): cell = table.dequeueReusableCellWithIdentifier_("PYApplication") if cell == None: zrect = ((0, 0), (0, 0)) cell = UITableViewCell.alloc().initWithFrame_reuseIdentifier_(zrect, "PYApplication") cell.setText_(self.sections_[path.section()]["items"][path.row()]["title"]) return cell @objc.signature("@@:@i") def tableView_titleForHeaderInSection_(self, table, section): return self.sections_[section]["header"] @objc.signature("v@:@@") def tableView_didSelectRowAtIndexPath_(self, table, path): action = self.sections_[path.section()]["items"][path.row()]["action"] if action != None: action(path) def callback(self, path): UIAlertView.alloc().initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles_("", "You cliked on a TableView", None, "Ok", None).show() @objc.signature("v@:@") def applicationDidFinishLaunching_(self, unused): self.sections_ = [{"header": "header 1", "items": [{"title": "title 1", "action": lambda p: self.callback(p)}]}, {"header": "header 2", "items": [{"title": "title 2-1", "action": None}, {"title": "title 2-2", "action": None}] } ] # init window. outer = UIHardware.fullScreenApplicationContentRect() self.window = UIWindow.alloc().initWithFrame_(outer) self.window.orderFront_(self) self.window.makeKey_(self) self.window.setHidden_(NO) inner = self.window.bounds() navsize = UINavigationBar.defaultSize() navrect = ((0, 0), (inner[1][0], navsize[1])) # init UIView self.view = UIView.alloc().initWithFrame_(self.window.bounds()) self.window.setContentView_(self.view) # init Navi-bar self.navbar = UINavigationBar.alloc().initWithFrame_(navrect) self.view.addSubview_(self.navbar) self.navbar.setBarStyle_(1) navitem = UINavigationItem.alloc().initWithTitle_("Grouped TableView") self.navbar.pushNavigationItem_(navitem) # init Table View lower = ((0, navsize[1]), (inner[1][0], inner[1][1] - navsize[1])) table = UITableView.alloc().initWithFrame_style_(lower, 1) # grouped TableView self.table = table self.view.addSubview_(table) table.setSeparatorStyle_(1) table.setDelegate_(self) table.setDataSource_(self) table.reloadData() UIApplicationMain(sys.argv, PYApplication)
QuickPwn
さて、また、iPhoneOSのアップデートが来たので、jailbreakもまたやらなくてはならない。
ただ、今回は、Pwnage Toolを使わずに、新しく出たWindows用のQuickPwn( + QuickPwn-GUI-1.20)を使ってみた。
このQuickPwnとPwnage Toolの一番の違いは、Pwnage Toolが「.ipsw」ファイル(ディスクイメージみたいなもの?)を解凍+パッチ当て+再圧縮して、それをiTunesでiPhoneへ転送するのだけれど、QuickPwnは直接iPhoneとやり取りして、現状のシステムにjailbreakを追加する、と言う方法を使うので圧倒的にスピードが速い。(コマンドラインの表示を見ていると、正確には小さな「.ipsw」ファイルを作ってそれを転送している模様。)
このBlogに詳しい方法が書いてあった。
Tool's 4 Hack - iPhone 2.0.2対応 JailBreak、脱獄ツール
#ここのコメントを読むと、上手くiTunes関連プログラムが終了せず(QuickPwnが自動的に終了させる)、上手く行かなかった人もいるようだけど、僕のところでは何の問題も無くスムーズにjailbreakできた。
後は、jailbreak後に、前のjailbreak時にインストール・設定したパッケージを、また、新たにインストールしなくてはならないのだけど、これは、何か、上手くやってくれるツールなどないのだろうか、、、(毎回やるのはめんどうくさいし、、、)
Pythonで作るiPhoneアプリ
前にちらっと書いたけれど、うちにはIntelMacが無い。(PowerPCのMacMiniならあるけれど、、、)
と言うことは、AppleのiPhone SDKが使えないわけで、通常iPhoneアプリは作れない。SDKは無料で配っているけれど、IntelMac買うのは高いよ、、、
そう、Jailbreakした理由の一つは、iPhoneアプリを作って見たかったからでもある。
正攻法ではWindows等の母艦にクロスコンパイル用のtoolchainをインストールするらしいけれど、もっと安易にスクリプト言語なんかで作れないかなぁ、、、と思ってCydiaをいろいろ見ていたら、iPhone用Pythonサポートがインストールできるようだ。
これは、iPhoneにPython本体、Object-Cブリッジ(PyObjc)、サンプルアプリケーション(iphone-pythonパッケージ)をインストールするもので、インストールすると、スプリングボード(iPhoneのメイン画面)に「HelloPython」と言うアプリケーションが出てくる。
さすがに立ち上がりなんかは遅いけれど、まあ、ちゃんと動くようだ。(sqliteサポートを使って、アドレスブックから全員のリストを表示するだけのものだけれど。)
見た所、ソースコードは/Applications/HelloPython.app/HelloPython.pyに入っている。ここをちょっと書き換えるとちゃんと動作が変わる。(まあ、あたりまえか、、、)
ドキュメンテーションとかは無いのかなぁ?と思って、探してみると、このBlogエントリ、、、
に、基本的な事が書いてあった。(これの作者は、Cydiaの作者でもある、Jay Freeman (saurik)さんみたい。よく見ると他にもiPhone用Javaバインディングなんかも作ってるようだ。)
ざっと読んでみたけど、特に変な制約とかもなさそうだし、一応、ちゃんと使えそう。(まあ、あの奇妙なObjective-C type signatureとかは別にして、ね。@objc.signature("i@:@i")こういう奴。)
#参考リンク
- PyObjC
- iPhone Dev Center (要登録)
jailbreakの記録2
ああ、さすがにjailbreakってば、きびしい。
と言うのも、今度のiPhone ファームウエア2.0.1対応のjailbreakをするのに数日かかってしまった。
長い試行錯誤をはしょって、最終的に上手く行った方法は、、、
1)一度普通にiTunesからファームウエア2.0.1にアップデートする。(この時点で2.0に対して行ったjailbreakはきれいさっぱり無くなる。)
2)次にダウンロードしてきた、Pwnage Tool 2.0.2(ファームウエア2.0.1対応版)で、2.0.1用jailbreakイメージを作る。
3)(以前にjailbreakした事は無視して)Pwnage Tool に新たにjailbreakする、事を伝えて、DFUリカバリーモードに入れる。
4)iTunesで作ったイメージを書き込む
これで、やっとできました。
やれやれ、、、
#今度のヴァージョンには、以前のインストーラー「Installer」も入っているようです。なぜ、標準の「Cydia」と2個もインストーラーが?