iPad Wifi版でiPhone4Sの3G回線が使えた件
この件の最新情報はこちら「iPhone4Sの疑似テザリングツールをGitHubに上げた」の記事へ
できました!
こんばんは、息子の保育園が始まり、息子は保育園を楽しんでいるようで、癒し系キャラと先生方に評価されているようです。まずは一安心といったところでしょうか、@kjunichiです。
ここしばらく、思わせぶりな記事の投稿が続きましたが、出来ました!
まずはソースです
iPhoneで動かすJavaScript
LAN側?のnode.jsを動かせる何かに以下のHTML(JavaScript)を入れておき、iPhoneでこのページをアクセスします。まぁ、そんな訳で、iPhoneの自動ロック設定は解除しておかないと実用的では無いです。
iPad用HTTPプロキシー
iPadのHttpProxyの指定をこのプログラムを動かすnode.jsサーバのIPアドレスにします
// HttpProxyとして要求を受けて、WebSocketにして要求を転送する。 // WebSocketから応答を受けて、HttpProxyに転送する。 // 例外が発生してもサービスを停止しないようにする process.on('uncaughtException', function(err){ console.log(err.stack); }); var app = require('http').createServer(handler) , io = require('socket.io').listen(app) , fs = require('fs') app.listen(80); function handler (req, res) { fs.readFile(__dirname + '/index.html', function (err, data) { if (err) { res.writeHead(500); return res.end('Error loading index.html'); } res.writeHead(200); res.end(data); }); } var myWsClient = {}; var count = 0; var webClients = {}; io.sockets.on('connection', function (wsclient) { myWsClient = wsclient; console.log("HTTP Proxy start"); var server = doProxy(wsclient); wsclient.on('end',function() { console.log("HTTP Proxy end"); server.close(); }); }); function doProxy(wsclient) { // HttpProxyを開始する。 var port = 8081; var sys = require('util'); var net = require('net'); var server = net.createServer(function(webBrowser) { count++; // 接続してきたブラウザのソケットを保持する。 webClients[count] = webBrowser; webBrowser.wid=count; console.log('web client connected [' + count + "]"); // クライアントからデータを受けた場合 webBrowser.on('data', function(data) { webBrowser.pause(); var parseheader = data.toString().split(/\n/); if(parseheader[0].match(/^GET/) || parseheader[0].match(/^CONNECT/) || parseheader[0].match(/^POST/)) { console.log("client send : " + parseheader[0].toString()); } else { console.log("client send "); } // Base64化してWS->HttpProxyへ送る //console.log("to ws : " + data.toString('base64')); wsclient.emit('httptows' ,{httpdata: data.toString('base64'),wid:webBrowser.wid}); webBrowser.resume(); }); webBrowser.on('end', function() { // ブラウザの接続が切断された場合の処理 console.log('client disconnected['+ webBrowser.wid + ']'); wsclient.emit('httpend',{wid: webBrowser.wid}); webClients[webBrowser.wid] = ""; }); webBrowser.on('close', function() { // ブラウザの接続が切断された場合の処理 console.log('client connection is closed.['+ webBrowser.wid + ']'); webClients[webBrowser.wid] = ""; wsclient.emit('httpend', {wid: webBrowser.wid }); }); webBrowser.on('error', function(err) { // ブラウザの接続が切断された場合の処理 console.log('client err['+ webBrowser.wid + '].' + err); webClients[webBrowser.wid] = ""; wsclient.emit('httpend', {wid: webBrowser.wid }); }); }); // server server.listen(port, function() { //'listening' listener console.log('server bound'); }); wsclient.on('wstohttp', function(data) { // WebSocketからの応答をブラウザに返す。 if(webClients[data['wid']]=="") { console.log('web client connection has been closed.'); wsclient.emit('httpend', { wid: data['wid']}); return; } // WebSocket経由で結果受取、Base64デコードしてブラウザに返す。 console.log('server send data : [' +data['wid'] + "]"); var a = new Buffer(data['httpdata'].toString(), 'base64'); var clientSocket = webClients[data['wid']]; clientSocket.pause(); clientSocket.write(a); //dumpResponse(a); clientSocket.resume(); }); wsclient.on('httpend', function(data) { console.log("server is closed.["+data['wid']+"]"); // HttpProxy先でコネクションが切れたら、こちらもクライアントを切断 var clientSocket = webClients[data['wid']]; if(clientSocket=="") { return; } clientSocket.end(); webClients[data['wid']] = ""; }); wsclient.on('end', function() { console.log('server disconnected'); //webBrowser.end(); //var clientSocket = webClients[data['wid']]; //clientSocket.end(); }); sys.puts('Server listening on port ' + port); } function dumpResponse(buf) { var tmp = ""; // 表示できる文字は表示する for (var i = 0; i < buf.length ; i++) { var c = buf.readUInt8(i); if( (c > 31 && c < 127) || c==13 || c==10) { tmp = tmp + String.fromCharCode(c); } else { tmp = tmp + c + ":"; } } console.log(tmp); }
WebSocketを受信してHTTPプロキシーに転送するサーバ
// WebSocketで受けた要求をHttpProxyに転送する。 // HttpProxyで受けた応答をWebSocketに転送する。 // 例外が発生してもサービスを停止しないようにする process.on('uncaughtException', function(err){ console.log(err.stack); }); var app = require('http').createServer(handler) , io = require('socket.io').listen(app) , fs = require('fs'); var net = require('net'); app.listen(8123); function handler (req, res) { fs.readFile(__dirname + '/index.html', function (err, data) { if (err) { res.writeHead(500); return res.end('Error loading index.html'); } res.writeHead(200); res.end(data); }); } io.sockets.on('connection', function (socket) { var isProxyClose = true; // HTTP Proxyサーバへ接続する。 doProxyProc(socket, isProxyClose); }); var proxys = {}; function doProxyProc(socket,isProxyClose) { var isProxyClose = true; console.log('server connected'); var wid = -1; socket.on('wstohttp', function(data) { console.log("proxys at "+ data['wid'] + " is " + proxys[data['wid']]); if(proxys[data['wid']]==undefined){ var proxy = net.createConnection(8080,'127.0.0.1',function() { proxys[data['wid']] = proxy; proxy.wid = data['wid']; // WebSocketでデータを受けたらHttpProxyに接続する proxy.pause(); console.log("from client : " + data); // base64デコードしてproxyサーバに送る var a = new Buffer(data['httpdata'].toString(), 'base64'); wid = data['wid']; proxy.write(a); dumpResponse(a); proxy.resume(); }); proxy.on('data',function(pdata) { // HttpProxyからの応答をWebSocketに転送する。 proxy.pause(); // WebSocketでHttpProxyからの応答をBase64エンコードして返す。 socket.emit('httptows', {httpdata: pdata.toString('base64'), wid: proxy.wid}); proxy.resume(); }); proxy.on('end', function() { console.log('server disconnected'); socket.emit('httpend', {wid: proxy.wid}); proxys[proxy.wid] = ""; }); proxy.on('close',function(){ // HttpProxyから切断された場合の処理 console.log('server close!'); proxys[proxy.wid] = ""; }); proxy.on('error', function(err) { console.log("proxy["+proxy.wid+"] error! " + err); socket.emit('httpend',{wid: proxy.wid}); }); } else { // 既に接続済みのHttpProxyへのSocketを利用する。 var workProxy = proxys[data['wid']]; if(workProxy=="") { console.log("proxy has been closed."); socket.emit('httpend', {wid: data['wid'] }); return; } workProxy.pause(); console.log("from client : " + data); var a = new Buffer(data['httpdata'].toString(), 'base64'); workProxy.write(a); dumpResponse(a); workProxy.resume(); } }); // end socket.on('wstohttp' socket.on('httpend', function(data) { console.log('client disconnected['+data['wid']+"]"); var p = proxys[data['wid']]; console.log("P = " + p); if(p!="" && p != undefined) { p.end(); proxys[data['wid']]=""; } }); } function dumpString(str) { var tmp = ""; // 表示できる文字は表示する for (var i = 0; i < str.length ; i++) { var c = str.charAt(i); if( (c > 31 && c < 127) || c==13 || c==10) { tmp = tmp + String.fromCharCode(c); } else { tmp = tmp + c + ":"; } } console.log(tmp); } function dumpResponse(buf) { var tmp = ""; // 表示できる文字は表示する for (var i = 0; i < buf.length ; i++) { var c = buf.readUInt8(i); if( (c > 31 && c < 127) || c==13 || c==10) { tmp = tmp + String.fromCharCode(c); } else { tmp = tmp + c + ":"; } } console.log(tmp); }
わかりにくいけど構成図
iPhoneの設定
iPhoneとアドホック通信で、iPhone側にプロキシアプリを開発環境を経由して インストールするとテザリングできる記事はこれまでありました。 アドホック接続だと、iPhoneとPC間(ローカルIP向けの通信)はWifiで通信して、iPhoneからは 3G経由でインターネット(グローバルIP)へアクセスできるのです。 つまり、iPhoneはWifiと3G回線を同時にアクセスできる!
実はアドホック接続でなくてもWifiと3Gが同時に使える
Wifiの設定で、ルータのアドレスを指定しなければ、ローカルIPへの 通信はWifiを使い、グローバルIPへの通信は3G回線を使う動きを します。
node.jsが動き、無線LANアクセスポイントとして動かす何か
iPhoneとiPadに加えてnode.jsが動き、無線LANアクセスポイントとして動かす何か があれば、iPadからiPhoneの3G回線を利用できるのです。
関連リンク
- ついに脱獄不要かつAppleが介入不能なテザリング手法が登場 この記事読んだのがきっかけでした。