02:54重い腰を上げて、GoogleDrive対応を実装しました。前にAPIを少しみた段階で”うえっっ”てなって放置していたのですが、ちょっと癖があるんですよね。基本はHTTPなので、通信内容に癖があるわけじゃないのですが、SDKが微妙に使いづらいのです。いっても作りが悪いわけでなく、GoogleDriveだけでなくGoogleの汎用ライブラリを使っているのが原因です。ファイルのダウンロードは比較的簡単ですが、ストリーミングが少し面倒でした。しても細切れにアクセスすることになるのでパフォーマンスがいまいち。通信自体Googleのソース使わずに全部独自に実装してやろうか・・と思いましたがあまりに大変すぎて断念。ComicGlassではリモートのファイル一覧を開いた状態のまま、アプリのスリープ中や終了後も状態やリストを保持する機能があります。モバイルデバイスですから、前の状態からいつでも再開できるというのは重要なことです)ここがまた問題で、GoogleDriveが返すファイルにアクセスするためのユニークURIが時間が経つと(正確にはセッションが新しくなると)勝手に変わります。といわけで、前回開いていたファイルが現在どのファイルなのかをマッチングする必要があります。この辺もURIが変わったら自動的に情報を再取得することで問題を回避しました。失敗しますので、どうしてもエラーになる場合は接続からやりなおしてください。02:03かつて、LinuxからSambaを使ってWindowsのファイル共有にアクセスすると、文字コードの変換の問題で大変苦労しました。時代は流れ、LinuxでもWindowsでもUnicodeが標準になりました。いうわけで、一般的にSMBを使う分には文字コードで苦労することはほぼなくなりました。以下の画像をご覧ください。これはWindows8のエクスプローラーの画面です。何かおかしいところにお気づきでしょうか。いいおっさんがプリキュア・・・じゃなくて、同一のファイル名が同じフォルダに存在しています。実はこれ、「ピ」と「プ」の文字がUNICODEの結合文字列になっています。通常、Windowsで「プリキュア」と入力した場合、「プ」の文字はU+30D7になります。U+309Aになります。U+30D5は「フ」です。U+309Aは半濁点記号です。゚」の合成で表現されます。゚」はU+309Cで、合成用ではなくそういう1文字ですので念のため)Unicodeではこのような複数の表現方法があります。混ざってる何かと困るので、普通は同一コードになるように正規化して扱います。Decomposition)と言います。結果として4パターンの同名ファイル名が出来てしまったわけです。Windowsは上記のような状態を許してくれます。許しているというか、初期の頃になんの正規化もせず扱ってしまったため、以後もそのままになっているんだと思います。(憶測)さて、一方MacOSですが、上記のようなNFD,NFCの混在を許してくれません。NFDライクな正規化をされます。(正確にNFDでないのがまた困るんですが・・・)iOSも一緒です。よってファイル共有をすると大変困ります。SMBには明確な仕様がない(もしかしたらあるかもしれなけど、ドキュメント読む限り発見できず)。endianなんでしょうが、NFDでもプロトコルとしては問題なく(というかノータッチで)動きます。実装するのが正解なのでしょうか。23:57VisualStudio2013では、デフォルトの設定でWindowsXPが切り捨てられています。フレームワークのバージョンとかでなく、素のWindowsAPIのみを利用していても起動できません。WindowsXPはサポートしなくても良いのかもしれませんが、Windows2003ServerはWindowsXPと同じ扱いですので、WindowsXPで起動できないとWindows2003Serverでも起動できません。2003Serverは15年7月くらいまでサポート期間があります・・・)以下の設定を変更すると起動できるようになります。動作確認環境もなく、なかなか気づきにくいので気をつけないといけませんね。・・・しかしVisualStudio高いよねーー。自分の作るWindowsアプリケーションは本当に素のC++で書いているので別にどんなコンパイラでもコンパイルできるんですよね。高く感じますが・・・しかし、VisualStudio2013のエディタはかなり良くなりました。エディタとして買ってます。00:02Windowsでマイネットワークを開くと近くのコンピュータが自動的に表示されます。これは単純にブロードキャストしてそれぞれが応答をするのではなく、バックアップブラウザを持っているホストに問い合わせをすることで実現しています。バックアップブラウザはLAN上のコンピュータが自動的に選定されて、どれかがなります。OSのバージョンによって優先度が決まっていたりしますが、問い合わせるだけなのでその部分はあまり気にしないことにします。これを実装しようとすると結構面倒で、1.バックアップブラウザの検索2.バックアップブラウザからブラウザリスト(ホスト一覧)の取得と2段階を踏む必要があります。Response)を返してくれます。ここで一つ重大な問題があります。問い合わせは当然ながらIPで行っているわけですが、元はNetBIOSのため、バックアップブラウザが自分に対して応答を返すときに、名前解決を要求してきます。よってNBNSの問い合わせに応答できるように実装しておかなければなりません。(IPで送ってるんだからそのアドレスに返してよ・・と思いますがそうはなっていません)これは本来であればOSの機能なので、アプリで実装してしまうと、他のアプリと同時にポートをBINDしたときに問題が起こりそうですね。次に2ですが、これはSMBと同じです。ダイレクトホスティング(ポート445番)では挙動が不安定でした(Windows7で確認)。NetBIOSセッションサービスを利用すると安定して取得できます。この方法ですと、ワークグループ名の指定が必要です。何かしらの理由でバックアップブラウザが存在しないとホスト一覧を取得できません。以下は禁じ手だと思うのですが、GoodReaderの実装がそうなっていたので紹介しておきます。確実な方法ですが、ポートスキャンがちょっと攻撃っぽいので今のところComicGlassでは非採用にしました。1.ネットワークのサブネットマスクからネットワークアドレスとホストアドレスを分離します。2.全てのホストアドレスのTCP/445番ポートに対して接続を試みます。(つまりポートスキャン)3.接続できたら、セッションセットアップの途中までやります。途中というのは相手のホスト名が判明するところまでです。(認証の途中)4.ホスト名(NetBIOS名)が取得できたら切断しちゃいます。上記の方法であればブラウジング機能に頼らずホストを検出できます。同じサブネットの中のみ。22:36今回はただの愚痴ですが・・・。SMB/CIFSの仕様をみると、データの途中にいきなり可変長データが入っていたりします。paddingがいたるところに出てきます。変数は存在するものの、実際のところどういう値にしていいのか、定数としてどんなものがあるのかわからないことが多々あります。そんなときはプロトコルアナライザです。WindowsServer系ならばリソースキットで利用できますが、WireSharkなんかも便利です。絶対あったほうが良いもの・・。それはポートミラーリング、またはポートモニタリング機能がついたスイッチングハブです。完全なリピータハブがあればそれでもいいですが、おそらく10BASE-Tの時代のものしか無いと思うので・・・。数年前までは何万円もしましたが、今では1万円を切る商品もあるみたいです。00:56Windowsで使われる、NetBIOSまわりを頑張ってみます。やりたいことは、Windowsコンピュータ名(NETBIOS名)からIPアドレスを取得することです。NetBIOSはとにかく不可解です。本当に不可解なんですが、歴史的背景があるので仕方がありません。どのくらい古いかというと1980年代くらいなので仕様書もイマイチ読みづらく、後の改変も多数あるので本当にわかりづりです。NetBIOSはTCP/IPとは別物です。現在ではプロトコル部分をNetBEUI、インターフェイス部分をNetBIOSと呼ぶようですが、元はインターフェイス(API)からプロトコルまですべてひっくるめた規格でした。NetBEUIは現在では使われることは無くなりましたが、NetBIOSのインターフェイスだけは利用され続けているというわけです。(TCP/IPとか言いながらUDP/IPも多用します)これ時代もWindows3.x時代のものなので何かと理解しづらいです。Extensions[MS-NBTE]をまず見てみると名前解決についてはRFC1002を見るように書いてあります。RFC1002の内容は「NetBIOS-over-TCP」となっています。読んでみると・・・なんか意味不明・・ですががんばって読む。+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+Bはブロードキャストフラグなので、1で固定みたいです。フラグ部分は0x0110。これがまだ何故??というやり方で頭に???浮かびっぱなしです。NETBIOS名は15文字です。本来は16文字でしたが、最後の1バイトを別の用途に割り当てたため15文字。15文字に満たない場合はスペース(0x20)で埋めます。Managerの仕様ではなくMicrosoftの拡張仕様です。0x00はクライアント、0x20がサーバサービスを示すようなのでここでは0x20が正解だと思われます。(実際にはどちらも名前登録されていて、反応もしてくれるので良いのですが)で、肝心のNETBIOS名の格納方法です。最初の1バイトは0x20を指定します。0x20=長さが32byteの意味で、NETBIOS名の場合は32byte固定と書いてあるので必ず0x20を指定。NEBIOS名+サフィックスの16byteを以下の手順で符号化して32Byteにします。最後にNULL(0x00)くっつけるので、長さを表す先頭の0x20と合わせて34byteになります。これがQUESTION_NAMEになります。NETBIOS名の符号化は、まず文字コードを4バイト単位にわけます。空白文字(0x20)であれば0x2と0x0です。0x0をA、0x1をB、0x2をC・・・という感じで文字を割り当てます。0x20であれば「CA」になります。NETBIOS名が「FRED」であれば「EGFCEFEECACACACACACACACACACACACA」になります。これをUDPでブロードキャストすれば該当ホストがレスポンスを返してくれます。iOSで1024番以下のポートをbindできるのか?とちょっと不安でしたが、やってみたら大丈夫でした。+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+TTLより前はリクエストと同じ。ARRAYにIPアドレスが入ります。(複数ある場合は複数入ります。パケットの送信アドレスを見るか、自分のネットワークマスクと較べてどれを利用するか決めます)実際には名前の解放パケットとかも飛んでくるようですが(それを見てキャッシュから削除する)、iOSでそれを監視し続けるのは非現実的なので無視します。Resolution)を使う必要があります。今回はIPv4だけでいいのでそっちはやりません。(そのくらいの知識がある人はIPアドレスでわかるでしょうし・・・)さて、これでコンピュータ名(NETBIOS名)を解決できるようになりました。なったところでどう使うかは悩ましいところなんですけどね。02:23Windows関連情報は運用を目的にしたものが多く、コーディング例はあんまり聞かないのでメモ的に書いておきます。SMBの認証にはLM認証、NTLMv1認証、NTLMv2認証、Kerberos認証のいずれかを使います。Kerberos認証は鍵交換でActiveDirectly環境想定のものなので除外。LM認証はNTLMv1認証はだいぶ古いにもかかわらずつい最近まで使われていましたがWindows7からはデフォルト無効になっているようなのでこちらも使わない方針で行きます。ようするに面倒なのでNTLMv2だけ対応します。あと、まず非対応ということはあり得ないので、Unicodeだけ対応します。Protocol」のことらしいです。Microsoftの仕様書名は[MS-NLMP]です。基本的には認証情報をバイト列に並べているだけなので仕様書を読みながら実装すればなんとか出来ます。SMB/CIFSがアライメントやら謎のpaddingやらとても実装しにくいのに比べると可変長データをpayloadにまとめているので少しだけ気楽です。クライアント側で生成する必要があるのは、NEGOTIATE_MESSAGEとAUTHENTICATE_MESSAGEです。前者は本当に必要な情報を連結してバイト列にしているだけなので楽勝。それでも実際実装してみると一発ではなかなか思い通りにならないのですが・・・。後者のAUTHENTICATE_MESSAGEもほとんど同じですが、NEGOTIATE_MESSAGEに対してサーバから返されたCHALLENGE_MESSAGEに含まれる情報を使ってチャレンジレスポンスを計算する必要があります。それがNTLMv2なわけですね。Responseというフィールドです。いっても、使うのはmd4とmd5だけなんですね。試行錯誤の結果正しい結果を出せるようになったのでメモ。challengeと4を連結する6.5に対して、3を鍵として、hmac_md5を計算する7.6と4を連結する7で出来たものがNTLMv2レスポンスになります。2104に規定されています。それぞれC言語コードが附属しています。計算量も無いので最適化も不要でしょうから、そのまま使ってもOKかと思います。ライセンス表記が必要です。探せばそのあたりをクリアしたコードも見つかります。(僕はmd4はそのまま使いました。md5は以前準備したコードがあったのでそっちを利用)基本的に、SMBと同じく全てリトルエンディアンが使われます。普通ネットワークバイトオーダーと言えばビッグエンディアンなんですけどね。最初はエンディアン気にしてコード書いてましたが、どうせiPhoneもリトルエンディアンだし途中から面倒になってリトルエンディアン環境想定で作りました。これでSMBサーバと認証ができるようになりました。TCP/IPを使って名前解決(IPアドレスを取得する)をやってみたいと思います。IPアドレスでアクセスすれば良いので不要な気もしますが。欲を言えばBROWSERプロトコルを使ってネットワーク上のNETBIOSホスト名を自動収集したいところですが、、まぁNASとかはBonjourに対応してるから不要な気もします。challengeのスペルが違うというお恥ずかしいミスがありましたのでこっそり修正。00:29何故か全くライブラリが見つからないSMB/CIFS。ググってみても、「SMBのiOSライブラリないの?(そもそもObj-cに限定しなくても無い)」「sambaがGPL、ぐぬぬぬ・・・」という記事しか見当たらない。組込み向けの製品はいくつか見つかりましたが、たぶん個人で買えるようなものじゃない。実装は厄介なんだろうなと見る気もしてなかったですが、風邪でダウンしていたので、その時に一度気合を入れてテストしてみました。tangoというOSSがあるようですが、うまく動かない。認証関連が古いせいで最近のWindowsでは動かないぽい。flagが立っていると即座にエラー返すようなのできっとダメだろう、ってことでtangoは諦める。SMBはSMB1とSMB2があって、SMB2は大きなバッファが扱えて高速LANで効率がいいようですが、NASとかで対応していないものもあるかもしれないし、iPhoneのWifiからではほとんど速度は変わらない(と思う)のでまずSMB1にトライ。みたいな実装があって苦しむと噂には聞いております。仕様書読むと案外単純な通信です。(ワイルドカード*でFindFileする。ルートディレクトリだと失敗するのでちょっと変える必要ある)・データの転送(実は簡単な命令っぽい)という感じで、バイナリデータなのでC言語では扱いやすい(はず・・)しかしセッションリクエストで早速ハマる。Manager1.0のときの(方言で?)定義された。このコマンドが最初に定義されてから、リクエストと応答メッセージのフォーマットが変わったよ。