<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4982215793068879977</id><updated>2025-08-31T15:12:23.762+08:00</updated><category term="linux"/><category term="python"/><category term="web"/><category term="C++"/><category term="mysql"/><category term="java"/><category term="C"/><category term="linux programming"/><category term="gdb"/><category term="django"/><category term="iOS"/><category term="network"/><category term="javascript"/><category term="vim"/><category term="trace code"/><category term="http"/><category term="apache"/><category term="css"/><category term="linker"/><category term="virtualbox"/><category term="android"/><category term="git"/><category term="objective-c"/><category term="software development"/><category term="windows"/><category term="strace"/><category term="async"/><category term="bash"/><category term="blog"/><category term="build"/><category term="profiling"/><category term="testing"/><category term="browser"/><category term="chrome"/><category term="effective c++"/><category term="gui"/><category term="mac"/><category term="mercurial"/><category term="XCode"/><category term="apple"/><category term="cpython"/><category term="https"/><category term="memory"/><category term="node.js"/><category term="signal"/><category term="tracing code"/><category term="bit operation"/><category term="common lisp"/><category term="cpp"/><category term="debug"/><category term="gcc"/><category term="hg"/><category term="ltrace"/><category term="multiprocess"/><category term="perl"/><category term="proxy"/><category term="security"/><category term="svn"/><category term="tcp"/><category term="thinking"/><category term="tip"/><category term="ubuntu"/><category term="ubuntu-build"/><category term="ui"/><category term="virtual machine"/><category term="virtualenv"/><category term="wifi"/><category term="arm"/><category term="benchmark"/><category term="cast"/><category term="compiler"/><category term="eclipse"/><category term="english"/><category term="firefox"/><category term="functional programming"/><category term="google"/><category term="gtk+"/><category term="html"/><category term="json"/><category term="machine learning"/><category term="macports"/><category term="macro"/><category term="mail"/><category term="make"/><category term="mobile web"/><category term="open source"/><category term="php"/><category term="programming"/><category term="programming language"/><category term="redmine"/><category term="screen"/><category term="stl"/><category term="template"/><category term="thread-safety"/><category term="unicode"/><category term="usability"/><category term="webkit"/><category term="wiki"/><category term="wireless"/><category term="2d graphics"/><category term="C99"/><category term="Core Data"/><category term="Go"/><category term="Google Apps"/><category term="algorithm"/><category term="apache2"/><category term="audio"/><category term="automation"/><category term="cairo"/><category term="ccache"/><category term="clang"/><category term="code"/><category term="coding"/><category term="crash"/><category term="credit card"/><category term="csv"/><category term="daemon"/><category term="dirty work"/><category term="document"/><category term="doxygen"/><category term="fedora"/><category term="ffmpeg"/><category term="flash"/><category term="font"/><category term="g++"/><category term="gc"/><category term="gif"/><category term="gj"/><category term="google play"/><category term="graphviz"/><category term="gzip"/><category term="heap profiling"/><category term="hotspot"/><category term="image"/><category term="ipv6"/><category term="ipython"/><category term="keychain"/><category term="license"/><category term="lldb"/><category term="llvm"/><category term="lxml"/><category term="man-in-the-middle"/><category term="memory leak"/><category term="micro blog"/><category term="mobile"/><category term="money"/><category term="multithread"/><category term="nacl"/><category term="natural language processing"/><category term="nginx"/><category term="oop"/><category term="os"/><category term="parser"/><category term="pdb"/><category term="protocol"/><category term="python3"/><category term="raid"/><category term="rails"/><category term="reading"/><category term="record"/><category term="regexp"/><category term="review board"/><category term="route"/><category term="ruby"/><category term="rwd"/><category term="server"/><category term="shell"/><category term="solr"/><category term="south"/><category term="sqlite"/><category term="ssh"/><category term="system management"/><category term="terminal"/><category term="tlpi"/><category term="tls"/><category term="tool"/><category term="user experience"/><category term="vcs"/><category term="vpn"/><category term="wsgi"/><category term="www"/><category term="x86"/><title type='text'>fcamel 技術隨手記</title><subtitle type='html'>自強不息的駱駝有草吃</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>519</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-4094279980270810525</id><published>2018-09-16T11:18:00.001+08:00</published><updated>2018-11-18T17:54:18.889+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="fedora"/><category scheme="http://www.blogger.com/atom/ns#" term="linux"/><title type='text'>在 Fedora 下裝 id-utils</title><content type='html'>&lt;p&gt;&lt;a href=&quot;https://lists.fedoraproject.org/pipermail/devel/2012-May/166914.html&quot;&gt;Fedora 似乎因為執行檔撞名，而沒有提供 &lt;a href=&quot;https://www.gnu.org/software/idutils/&quot;&gt;id-utils&lt;/a&gt; 的套件&lt;/a&gt;，但這是使用 &lt;a href=&quot;https://github.com/fcamel/gj&quot;&gt;gj&lt;/a&gt; 的必要套件，只好自己編。從官網抓好 &lt;a href=&quot;https://ftp.gnu.org/gnu/idutils/idutils-4.6.tar.xz&quot;&gt;tarball&lt;/a&gt;，解開來編譯 (./configure &amp;&amp; make)就是了。&lt;/p&gt;
&lt;p&gt;但編譯後會遇到錯誤:&lt;/p&gt;
&lt;pre&gt;./stdio.h:1010:1: error: ‘gets’ undeclared here (not in a function)&lt;/pre&gt;
&lt;p&gt;這個 stdio.h 是 id-utils 產生的。id-utils 沒用到 gets，所以&lt;a href=&quot;http://www.rootop.org/pages/3532.html&quot;&gt;註解掉那行不必要的檢查&lt;/a&gt;即可編成功。&lt;/p&gt;
&lt;p&gt;然後將需要用到的執行檔 gid、mkid 複製到自己的 PATH (例如 $HOME/bin)，就可以用了。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/4094279980270810525/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2018/09/fedora-id-utils.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/4094279980270810525'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/4094279980270810525'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2018/09/fedora-id-utils.html' title='在 Fedora 下裝 id-utils'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-8834954370996119791</id><published>2018-09-15T18:08:00.002+08:00</published><updated>2018-09-15T18:09:40.405+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="make"/><title type='text'>makefile 雜記</title><content type='html'>&lt;p&gt;備忘用到 makefile 的小技巧, 持續更新&lt;/p&gt;

&lt;h3&gt;Tip 1&lt;/h3&gt;
&lt;p&gt;
$ make -n TARGET: dry run, 產生會執行的指令&lt;/p&gt;
&lt;h3&gt;Tip 2&lt;/h3&gt;
&lt;p&gt;
問題: 用 make -n TARGET 產生指令 &quot;CMD&quot;，執行 CMD 卻失敗。&lt;/p&gt;
&lt;p&gt;
解法: 可能是少了 makefile 定的環境變數，可在makefile 加上&lt;/p&gt;
&lt;pre&gt;
shell:
        bash
&lt;/pre&gt;
&lt;p&gt;
然後用 &quot;make shell&quot; 透過 make 進入 bash，這樣就有一樣配置的環境變數，這時再執行 CMD 看看，若可以的話，表示問題出在少了環境變數。&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/8834954370996119791/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2018/09/makefile.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/8834954370996119791'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/8834954370996119791'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2018/09/makefile.html' title='makefile 雜記'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-8112500229782406790</id><published>2018-01-28T16:50:00.000+08:00</published><updated>2018-01-28T16:50:34.062+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C++"/><title type='text'>C++ 能否用 memcpy 複製 class / struct 的資料?</title><content type='html'>&lt;p&gt;答案是: POD (plain old data) type 可以。POD type 可和 C 互通, &lt;a href=&quot;http://en.cppreference.com/w/cpp/concept/PODType&quot;&gt;CPP Reference POD Type&lt;/a&gt; 的介紹:&lt;/p&gt;

&lt;blockquote&gt;Specifies that the type is POD (Plain Old Data) type. This means the type is compatible with the types used in the C programming language, can be manipulated using C library functions: it can be created with std::malloc, it can be copied with std::memmove, etc, and can be exchanged with C libraries directly, in its binary form.
&lt;/blockquote&gt;
&lt;p&gt;C++11 開始可用 std::is_pod 判斷&lt;/p&gt;

&lt;p&gt;在看 POD 定義的過程發現 &lt;a href=&quot;http://en.cppreference.com/w/cpp/concept/StandardLayoutType&quot;&gt;Standard layout type&lt;/a&gt; 的定義也重要的:

&lt;blockquote&gt;
Specifies that a type is standard layout type. Standard layout types are useful for communicating with code written in other programming languages.
&lt;/blockquote&gt;

&lt;p&gt;符合 standard layout type 才能&lt;a href=&quot;http://en.cppreference.com/w/cpp/types/is_standard_layout&quot;&gt;用 reinterpret_cast 轉型成第一個 member 的 type&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
不過最穩的最法是直接用 C++11 提供的 &lt;a href=&quot;http://en.cppreference.com/w/cpp/types/is_trivially_copyable&quot;&gt;std::is_trivially_copyable&lt;/a&gt; 判斷, 成立的話就可以安心地用 memcpy。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/8112500229782406790/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2018/01/c-memcpy-class-struct.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/8112500229782406790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/8112500229782406790'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2018/01/c-memcpy-class-struct.html' title='C++ 能否用 memcpy 複製 class / struct 的資料?'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-8343682677448658907</id><published>2018-01-28T16:24:00.000+08:00</published><updated>2018-01-28T16:24:03.613+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C++"/><category scheme="http://www.blogger.com/atom/ns#" term="template"/><title type='text'>如何讓 C++ STL, smart pointer 和 forward declaration</title><content type='html'>&lt;p&gt;有時在 class 內訂義 member 用 std::vector&lt;MyObject&gt; 或 std::unique_ptr&lt;MyObject&gt;, 可以用 forward declaration 宣告 MyObject, 有時不行。原因在於是否有觸發 compiler inline vector / unique_ptr 的 ctor / dtor。&lt;/p&gt;
&lt;p&gt;所以避免造成 inline ctor / dtor, 就可以用 forward declaration 宣告 MyObject, 降低程式之間不必要的 dependency, 減少重新編譯時間。
&lt;/p&gt;
&lt;p&gt;參考資料:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/13414884/278456&quot;&gt;Forward declaration with unique_ptr? &lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sites.google.com/a/chromium.org/dev/developers/coding-style/cpp-dos-and-donts#TOC-Stop-inlining-constructors-and-destructors&quot;&gt;The Chromium Projects: C++ Dos and Don&#39;ts
 -&gt; Stop inlining constructors and destructors
&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/8343682677448658907/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2018/01/c-stl-smart-pointer-forward-declaration.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/8343682677448658907'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/8343682677448658907'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2018/01/c-stl-smart-pointer-forward-declaration.html' title='如何讓 C++ STL, smart pointer 和 forward declaration'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-5117227627881086939</id><published>2017-09-03T12:39:00.000+08:00</published><updated>2017-09-03T12:46:50.952+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="debug"/><category scheme="http://www.blogger.com/atom/ns#" term="gdb"/><category scheme="http://www.blogger.com/atom/ns#" term="linux"/><title type='text'>讀出 gcc/g++ 編譯的參數</title><content type='html'>&lt;p&gt;從&lt;a href=&quot;http://itrs.tw/wiki/System_Programming#GCC:_-.7Bg.2Cf.7Drecord-gcc-switches&quot;&gt;這裡看來&lt;/a&gt;的，官方文件說 &lt;a href=&quot;https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html&quot;&gt;-grecord-gcc-switches 預設有開&lt;/a&gt;，所以滿可靠的。&lt;/p&gt;
&lt;p&gt;使用例子:
&lt;/p&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
$ g++ f.cpp -std=c++11 -g -o f
$ readelf --debug-dump f | grep DW_AT_producer
    &lt;c&gt;   DW_AT_producer    : (indirect string, offset: 0xded): \
  GNU C++11 5.4.0 20160609 -mtune=generic -march=x86-64 -g -std=c++11 -fstack-protector-strong
    DW_AT_producer     DW_FORM_strp
&lt;/pre&gt;
&lt;p&gt;但用 clang 編的資訊就沒存完整:&lt;/p&gt;
&lt;pre&gt;
$ clang++ f.cpp -std=c++11 -g -o f
$ readelf --debug-dump f | grep DW_AT_producer
    &lt;c&gt;   DW_AT_producer    : \
  (indirect string, offset: 0x0): clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)
    DW_AT_producer     DW_FORM_strp
&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/5117227627881086939/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2017/09/gccg.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/5117227627881086939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/5117227627881086939'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2017/09/gccg.html' title='讀出 gcc/g++ 編譯的參數'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-5805696264087627537</id><published>2017-04-29T23:52:00.001+08:00</published><updated>2017-04-30T00:16:46.151+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="build"/><category scheme="http://www.blogger.com/atom/ns#" term="linux"/><title type='text'>(C/C++ ) 如何在 Linux 上使用自行編譯的第三方函式庫</title><content type='html'>&lt;p&gt;以使用 &lt;a href=&quot;https://github.com/google/leveldb&quot;&gt;LevelDB&lt;/a&gt; 為例。
&lt;/p&gt;&lt;p&gt;
抓好並編好相關檔案，編譯方式見第三方函式庫附的說明：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ ls include/  # header files
leveldb/
$ ls out-shared/libleveldb.so*  # shared library
out-shared/libleveldb.so@  out-shared/libleveldb.so.1@  out-shared/libleveldb.so.1.20*
&lt;/pre&gt;&lt;p&gt;
下面的例子用 clang++ 編譯，這裡用到的參數和 g++ 一樣。
&lt;/p&gt;&lt;h4&gt;問題一：找不到 header&lt;/h4&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ clang++ sample.cpp
sample.cpp:5:10: fatal error: &#39;leveldb/db.h&#39; file not found
#include &quot;leveldb/db.h&quot;
         ^
1 error generated.
&lt;/pre&gt;&lt;p&gt;
解法：用 &lt;tt&gt;-I&lt;/tt&gt; 指定 header 位置
&lt;/p&gt;&lt;h4&gt;問題二：找不到 shared library&lt;/h4&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ clang++ sample.cpp -I include/
/tmp/sample-2e7dd8.o: In function `main&#39;:
sample.cpp:(.text+0x1e): undefined reference to `leveldb::Options::Options()&#39;
sample.cpp:(.text+0x6f): undefined reference to `leveldb::DB::Open(leveldb::Options const&amp;amp;, std::string const&amp;amp;, leveldb::DB**)&#39;
sample.cpp:(.text+0x10c): undefined reference to `leveldb::Status::ToString() const&#39;
sample.cpp:(.text+0x7d0): undefined reference to `leveldb::Status::ToString() const&#39;
clang: error: linker command failed with exit code 1 (use -v to see invocation)
&lt;/pre&gt;&lt;p&gt;
要求 linker 連結 libleveldb.so (linker 的參數由 clang++ / g++ 轉傳)：
&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ clang++ sample.cpp -I include/ -l leveldb
/usr/bin/ld: cannot find -lleveldb
clang: error: linker command failed with exit code 1 (use -v to see invocation)
&lt;/pre&gt;&lt;p&gt;
但 compiler 說找不到要連結的 library
&lt;/p&gt;&lt;p&gt;
補上 libleveldb 的位置：
&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ clang++ sample.cpp -I include/ -l leveldb -L out-shared/
&lt;/pre&gt;&lt;h4&gt;問題三：執行時找不到 shared library&lt;/h4&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ ./a.out
./a.out: error while loading shared libraries: libleveldb.so.1: cannot open shared object file: No such file or directory
&lt;/pre&gt;&lt;p&gt;
編出 executable file 或 shared library 表示 static linker 成功，但執行時會用到 dynamic linker 載入函式庫。這錯誤訊息是dynamic linker 回報的。
&lt;/p&gt;&lt;p&gt;
用 ldd 可以檢查 shared library 的路徑是否正確：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ ldd a.out | grep leveldb
        libleveldb.so.1 =&amp;gt; not found
&lt;/pre&gt;&lt;p&gt;
幾種解法：
&lt;/p&gt;&lt;p&gt;
1. 用 LD_LIBRARY_PATH 指定位置 (man ld.so 查看細節)
&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ LD_LIBRARY_PATH=`pwd`/out-shared ./a.out
&lt;/pre&gt;&lt;p&gt;
若 out-shared 的位置有固定的話，可以在 &lt;span class=&quot;escaped&quot;&gt;/&lt;/span&gt;.bashrc 加上&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;export LD_LIBRARY_PATH=/path/to/out-shared 
&lt;/pre&gt;&lt;p&gt;
2. 將 library path 寫到 executable 裡 (man ld.sh 查看細節)：
&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ clang++ sample.cpp -I include/ -l leveldb -L out-shared/ -Wl,-rpath,`pwd`/out-shared 
$ objdump -p a.out | grep PATH  # 確認有記錄
  RPATH                /home/fcamel/dev/study/leveldb/out-shared
$ ldd a.out  | grep leveldb  # 也可用 ldd 確認
        libleveldb.so.1 =&amp;gt; /home/fcamel/dev/study/leveldb/out-shared/libleveldb.so.1 (0x00007fc1f091e000)
&lt;/pre&gt;&lt;p&gt;
這裡我用絕對路徑減少潛在的問題。
&lt;/p&gt;&lt;p&gt;
3.  搬到系統函式庫
&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ ldd a.out  | grep leveldb
        libleveldb.so.1 =&amp;gt; not found
$ sudo su
$ cp out-shared/libleveldb.so* /usr/lib
$ ldd a.out  | grep leveldb
        libleveldb.so.1 =&amp;gt; /usr/lib/libleveldb.so.1 (0x00007f1717026000)
&lt;/pre&gt;&lt;p&gt;
但這樣和系統內建的混在一起，不好維護。改放到 /usr/local/lib/leveldb/ 下：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ mkdir /usr/local/lib/leveldb
$ cp --preserve=links out-shared/libleveldb.so* /usr/local/lib/leveldb/
$ echo &quot;/usr/local/lib/leveldb&quot; &amp;gt; /etc/ld.so.conf.d/leveldb.conf
$ ldconfig # Update ldconfig&#39;s cache
$ ldd a.out | grep leveldb
        libleveldb.so.1 =&amp;gt; /usr/local/lib/leveldb/libleveldb.so.1 (0x00007f0314b32000)
&lt;/pre&gt;&lt;p&gt;
由 man ldconfig 得知 ldconfig 會讀 /etc/ld.so.conf。我在 Ubuntu 14.04 看到的設定如下：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf
&lt;/pre&gt;&lt;p&gt;
所以在 /etc/ld.so.conf.d/ 建新檔案寫入  /usr/local/lib/leveldb，然後更新 ldconfig cache 即可。
&lt;/p&gt;
&lt;h4&gt;參考資料&lt;/h4&gt;&lt;ol&gt;&lt;li&gt; The Linux Programming Interface ch41: Fundamentals of Shared Libraries&lt;/li&gt;
&lt;li&gt; man ld.so&lt;/li&gt;
&lt;li&gt; man ldconfig&lt;/li&gt;&lt;/ol&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/5805696264087627537/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2017/04/blog-post.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/5805696264087627537'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/5805696264087627537'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2017/04/blog-post.html' title='(C/C++ ) 如何在 Linux 上使用自行編譯的第三方函式庫'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-7656013417132606022</id><published>2017-03-07T21:18:00.000+08:00</published><updated>2017-03-08T10:01:10.731+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="debug"/><category scheme="http://www.blogger.com/atom/ns#" term="linux"/><category scheme="http://www.blogger.com/atom/ns#" term="ubuntu"/><title type='text'>除錯技巧：在 Ubuntu 上找出第三方函式庫的程式碼</title><content type='html'>&lt;p&gt;這篇藉由一個小例子說明如何使用 gdb, locate, apt-file, apt-get 找出問題原因。藉由取得第三方函式庫的程式碼，可以減少瞎猜的時間。
&lt;/p&gt;&lt;h4&gt;問題&lt;/h4&gt;&lt;p&gt;我發現某個程式會不預期的結束。但它不是 crash，沒有 core dump 可看。
&lt;/p&gt;&lt;h4&gt;用 gdb 找出怎麼結束的&lt;/h4&gt;&lt;p&gt;先用 gdb attach 程式，繼續操作。程式結束時 gdb 顯示是呼叫 exit() 結束的。
&lt;/p&gt;&lt;p&gt;
於是再執行一次，這次用 gdb 在 exit 設中斷點再繼續執行。&lt;/p&gt;&lt;p&gt;取得的 backtrace 如下：
&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;#0  __GI_exit (...) at exit.c:104
#1  0x00007fdd27f60408 in _XDefaultError (...) at ../../src/XlibInt.c:1414
#2  0x00007fdd27f6054b in _XError (...) at ../../src/XlibInt.c:1463
#3  0x00007fdd27f5d5e7 in handle_error ...) at ../../src/xcb_io.c:213
#4  0x00007fdd27f5e687 in _XReply (...) at ../../src/xcb_io.c:699
#5  0x00007fdd27f45346 in XGetWindowProperty (...) at ../../src/GetProp.c:69
#6  0x00007fdd2825db30 in XmuClientWindow () from /usr/lib/x86_64-linux-gnu/libXmu.so.6
...
&lt;/pre&gt;&lt;p&gt;
看 backtrace 沒什麼頭緒，都是第三方函式庫的程式。照 &lt;a href=&quot;https://www.x.org/releases/X11R7.6/doc/libXmu/Xmu.html#window_utility_functions&quot;&gt;XmuClientWindow()&lt;/a&gt; 的說明，它可能會失敗，但它不該直接呼叫 exit()。先找 XmuClientWindow 的原始碼，看看有什麼線索。
&lt;/p&gt;&lt;h4&gt;用 Ubuntu 的 package 系統找出原始碼&lt;/h4&gt;&lt;p&gt;首先用 apt-file 找出 libXmu.so.6 在那個套件 (第一次執行需先跑 apt-file update 更新索引)：
&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ apt-file search /usr/lib/x86_64-linux-gnu/libXmu.so.6
libxmu6: /usr/lib/x86_64-linux-gnu/libXmu.so.6
libxmu6: /usr/lib/x86_64-linux-gnu/libXmu.so.6.2.0
libxmu6-dbg: /usr/lib/debug/usr/lib/x86_64-linux-gnu/libXmu.so.6.2.0
&lt;/pre&gt;&lt;p&gt;
然後用 apt-get source 取得原始碼：
&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ apt-get source libxmu6
&lt;/pre&gt;
&lt;p&gt;
在 src/ClientWin.c 裡找到 XmuClientWindow [*1]：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;Window
XmuClientWindow(Display *dpy, Window win)
{
    ...
    XGetWindowProperty(dpy, win, WM_STATE, 0, 0, False, AnyPropertyType,
               &amp;amp;type, &amp;amp;format, &amp;amp;nitems, &amp;amp;after, &amp;amp;data);
    ....
}
&lt;/pre&gt;&lt;p&gt;
沒什麼有用資訊，繼續找 XGetWindowProperty 的程式碼。從 backtrace 裡知道它在 ../../src/GetProp.c 裡，但我不知道 GetProp.c 是什麼套件的程式。用 Google 搜尋 GetProp.c 也許會有線索，不過這裡我用另一個作法。
&lt;/p&gt;
&lt;h4&gt;從 header 找出原始碼&lt;/h4&gt;
&lt;p&gt;
src/ClientWin.c 引入的 header 不多，只有這些：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;#include &amp;lt;X11/Xlib.h&amp;gt;
#include &amp;lt;X11/Xatom.h&amp;gt;

#include &amp;lt;X11/Xmu/WinUtil.h&amp;gt;
&lt;/pre&gt;&lt;p&gt;
先用 locate 查出完整路徑：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ locate X11/Xlib.h
/usr/include/X11/Xlib.h
&lt;/pre&gt;&lt;p&gt;
再用 apt-file 找套件名稱：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ apt-file search /usr/include/X11/Xlib.h
libx11-dev: /usr/include/X11/Xlib.h
&lt;/pre&gt;
&lt;p&gt;
取得原始碼：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ apt-get source libx11-dev
&lt;/pre&gt;
&lt;p&gt;附帶一提，可透過 gcc -E 展開 #include 驗證 XGetWindowProperty 確實出自 X11/Xlib.h：&lt;/p&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;$ echo &quot;#include &lt;X11/Xlib.h&gt;&quot; | gcc - -E | grep XGetWindowProperty
extern int XGetWindowProperty(
&lt;/pre&gt;
&lt;p&gt;header 太多的話，直接編譯原始檔比較快：&lt;/p&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;$ gcc -E src/ClientWin.c &gt; t
&lt;/pre&gt;
得到的資訊如下：
&lt;pre&gt;
1674 # 1303 &quot;/usr/include/X11/Xlib.h&quot; 3 4
...
3060 extern int XGetWindowProperty(
&lt;/pre&gt;
&lt;p&gt;找到目標後，往回找第一個 header 路徑就是來源了。如果找不到的話，可能是少指定 include header 的來源目錄。可以從 make 產生的結果得知完整的編譯訊息：&lt;/p&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
$ ./configure
$ make V=1 | grep ClientWin.c
/bin/bash ../libtool  --tag=CC   --mode=compile gcc -std=gnu99 -DHAVE_CONFIG_H \
-I. -I..  -I../include -I../include/X11/Xmu &amp;lt; ... 略 ...&amp;gt; -c -o ClientWin.lo ClientWin.c
...
&lt;/pre&gt;
&lt;p&gt;找出 -I 的部份，和 -E 一樣加在 gcc 的參數上展開 include。&lt;/p&gt;
&lt;p&gt;關於如何讓 make 顯示編譯時的參數 (cflags)，參見&lt;a href=&quot;http://stackoverflow.com/a/5820432/278456&quot;&gt;這裡&lt;/a&gt;。&lt;/p&gt;
&lt;h4&gt;繼續追踪程式碼&lt;/h4&gt;
&lt;p&gt;
在 src/GetProp.c 裡發現：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt; 69     if (!_XReply (dpy, (xReply *) &amp;amp;reply, 0, xFalse)) {
&lt;/pre&gt;&lt;p&gt;
這和前面的 backtrace 一致：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;#0  __GI_exit (...) at exit.c:104
#1  0x00007fdd27f60408 in _XDefaultError (...) at ../../src/XlibInt.c:1414
#2  0x00007fdd27f6054b in _XError (...) at ../../src/XlibInt.c:1463
#3  0x00007fdd27f5d5e7 in handle_error ...) at ../../src/xcb_io.c:213
#4  0x00007fdd27f5e687 in _XReply (...) at ../../src/xcb_io.c:699
#5  0x00007fdd27f45346 in XGetWindowProperty (...) at ../../src/GetProp.c:69
#6  0x00007fdd2825db30 in XmuClientWindow () from /usr/lib/x86_64-linux-gnu/libXmu.so.6
&lt;/pre&gt;&lt;p&gt;
表示尋找的方向正確。再繼續看 src/xcb_io.c 和 src/XlibInt.c 內相關函式，得知：&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;// src/XlibInt.c
1463     rtn_val = (*_XErrorFunction)(dpy, (XErrorEvent *)&amp;amp;event); /* upcall */
&lt;/pre&gt;&lt;p&gt;
對照 backtrace 得知執行 _XErrorFunction 後，是呼叫 _XDefaultError。_XDefaultError 程式如下：
&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;1409 int _XDefaultError(
1410     Display *dpy,
1411     XErrorEvent *event)
1412 {
1413     if (_XPrintDefaultError (dpy, event, stderr) == 0) return 0;
1414     exit(1);
1415     /*NOTREACHED*/
1416 }
&lt;/pre&gt;&lt;p&gt;
確認是這裡呼叫 exit()。所以換掉 _XErrorFunction 可以解決問題。
&lt;/p&gt;&lt;p&gt;
在程式碼裡搜尋 _XErrorFunction 發現可用 XSetErrorHandler() 設值。這樣至少不會造成程式結束了。
&lt;/p&gt;&lt;h4&gt;備註&lt;/h4&gt;&lt;p&gt;1. 可用 grep 尋找，我是用 &lt;a href=&quot;https://github.com/fcamel/gj&quot;&gt;gj&lt;/a&gt;，找起來更有效率。
&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/7656013417132606022/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2017/03/ubuntu.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/7656013417132606022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/7656013417132606022'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2017/03/ubuntu.html' title='除錯技巧：在 Ubuntu 上找出第三方函式庫的程式碼'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-6466361132524793259</id><published>2016-06-26T16:33:00.002+08:00</published><updated>2016-06-26T16:33:28.359+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ipv6"/><category scheme="http://www.blogger.com/atom/ns#" term="network"/><title type='text'>NAT64 和 DNS 64</title><content type='html'>&lt;p&gt;
參考資料&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&quot;http://www.slideshare.net/IOSHints/nat64-and-dns64-in-30-minutes&quot;&gt;NAT64 and DNS64 in 30 minutes&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;http://www.csie.nuk.edu.tw/~wuch/course/csc521/2014midterm/A1015506-18-RFC6146-6147(DNS64-NAT64)@37.pptx&quot;&gt;DNS64與NAT64的網路佈署圖&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
IPv4 要用完了 (喊了十幾年, 最近是玩真的了), 在骨幹和 server 都轉成 IPv6 的過渡期, 用戶端也要轉換, 不然 server 是轉心酸的。但是大量 server 還是用 IPv4, 所以要讓 client 能同時支援 IPv4 和 IPv6才行。&lt;/p&gt;
&lt;p&gt;如果你是網管, 希望讓用戶可以同時連 IPv4 和 IPv6 的位置, 
一個可能的作法是建置「純 IPv6 環境」, 永遠拿到 IPv6 位置, 永遠連 IPv6 的位置。但要讓 IPv6 client 也能連 IPv4 位置, 需要 NAT64+DNS64 幫忙處理。&lt;/p&gt;
&lt;p&gt;
原理是 DNS64 會同時查 IPv6 和 IPv4 的位置, 有 IPv6 就用 IPv6, 沒有就轉換 IPv4 為 IPv6, prefix 配合 NAT64 的設定, 讓 client 連往該 IP 的封包會先經過 NAT64 router。第二份 slide 有詳細的流程圖。&lt;/p&gt;
&lt;p&gt;
然後 NAT64 會再轉換 IPv6 的封包成 IPv4 出去, 無縫接上用 IPv4 位置的 server。至於 client 收到 DNS64 回應的正常 IPv6 位置, 就透過一般 router 直接出去, 不用經過 NAT 64。
&lt;/p&gt;
&lt;p&gt;對設計後端 server 的人來說, 有愈多用戶可以連 IPv6 位置, 有愈高的機會考慮用 IPv6 位置。&lt;/p&gt;
&lt;p&gt;對 app 開發者來說, 了解有些用戶的網路環境會用 DNS64+NAT64 的配置, 所以要用 domain name 連上自家 server, 這樣自家 server 用 IPv4 或 IPv6 都會通。&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/6466361132524793259/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2016/06/nat64-dns-64.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/6466361132524793259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/6466361132524793259'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2016/06/nat64-dns-64.html' title='NAT64 和 DNS 64'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-5689318846090309824</id><published>2016-04-16T11:54:00.002+08:00</published><updated>2016-04-24T11:53:38.913+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>查詢 Web 標準是否有支援</title><content type='html'>&lt;p&gt;備忘待補更多來源:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; &lt;a href=&quot;http://www.quirksmode.org/&quot;&gt;http://www.quirksmode.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://caniuse.com/&quot;&gt;http://caniuse.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;https://github.com/w3c/test-results&quot;&gt;https://github.com/w3c/test-results&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/5689318846090309824/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2016/04/web.html#comment-form' title='2 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/5689318846090309824'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/5689318846090309824'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2016/04/web.html' title='查詢 Web 標準是否有支援'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-2463067748151523264</id><published>2016-04-16T11:51:00.002+08:00</published><updated>2016-04-16T11:51:16.347+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="git"/><title type='text'>如何 git merge 更改檔名的檔案</title><content type='html'>&lt;p&gt;參考資料:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;  &lt;a href=&quot;http://stackoverflow.com/questions/4722423/how-to-merge-two-branches-with-different-directory-hierarchies-in-git&quot;&gt;http://stackoverflow.com/questions/4722423/how-to-merge-two-branches-with-different-directory-hierarchies-in-git&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
如果在 branch 裡有 rename file X 為 Y, 並且 master 和 branch 都有改 X ( Y ) 的內容。那麼, merge master 的時候, git 可能會回報 master 的 X 被刪除了, 造成 merge conflict (deleted in ... and modified in ...)。
&lt;/p&gt;&lt;p&gt;
解法是提高 merge.renameLimit, 比方說 &lt;tt&gt;git config merge.renameLimit 999999999&lt;/tt&gt;。merge 時間會久一點, 但 git 會找出 X 在 branch 裡已被 rename 成 Y。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/2463067748151523264/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2016/04/git-merge.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/2463067748151523264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/2463067748151523264'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2016/04/git-merge.html' title='如何 git merge 更改檔名的檔案'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-4140812250747805001</id><published>2016-04-16T11:30:00.002+08:00</published><updated>2016-04-16T11:30:58.268+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="git"/><category scheme="http://www.blogger.com/atom/ns#" term="vim"/><title type='text'>用 vimdiff 作 git merge</title><content type='html'>&lt;p&gt;參考文章&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; &lt;a href=&quot;http://www.rosipov.com/blog/use-vimdiff-as-git-mergetool/&quot;&gt;http://www.rosipov.com/blog/use-vimdiff-as-git-mergetool/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://yodalee.blogspot.tw/2013/03/vimdiffgit-merge-conflict_28.html&quot;&gt;http://yodalee.blogspot.tw/2013/03/vimdiffgit-merge-conflict_28.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
以前我用 &lt;a href=&quot;http://kdiff3.sourceforge.net/&quot;&gt;kdiff3&lt;/a&gt;, 後來需要在 terminal merge, 改試 vimdiff, 也很好用。
&lt;/p&gt;&lt;p&gt;
設定:&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ git config --global merge.tool vimdiff
&lt;/pre&gt;&lt;p&gt;
執行 (在 git merge 有 unsolved conflict 後):&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ git mergetool
&lt;/pre&gt;&lt;p&gt;
UI 顯示:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; 左上: LOCAL&lt;/li&gt;
&lt;li&gt; 中上: BASE&lt;/li&gt;
&lt;li&gt; 右上: REMOTE&lt;/li&gt;
&lt;li&gt; 下面: 編輯區&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
vim 指令:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; &lt;tt&gt;[c, ]c&lt;/tt&gt;: 在 hunk 間移動, 和 &lt;a href=&quot;https://github.com/airblade/vim-gitgutter&quot;&gt;vim-gitgutter&lt;/a&gt; 一樣&lt;/li&gt;
&lt;li&gt; 搜 &quot;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&quot; 找 conflict&lt;/li&gt;
&lt;li&gt; &lt;tt&gt;:diffget LOCAL&lt;/tt&gt;: 目前的 hunk 選用 LOCAL&lt;/li&gt;
&lt;li&gt; &lt;tt&gt;:diffget REMOTE&lt;/tt&gt;: 目前的 hunk 選用 REMOTE&lt;/li&gt;
&lt;li&gt; &lt;tt&gt;:diffupdate&lt;/tt&gt;: 更新 diff 畫面&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
修改後存檔離開, 會自動 resolved conflict。無修改離開會跳過 merge, 維持原本狀態。
&lt;/p&gt;&lt;h4&gt;其它&lt;/h4&gt;&lt;p&gt;查看 merge 狀態:&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ git status
&lt;/pre&gt;&lt;p&gt;
如果整份檔案都要選用 LOCAL, 改用 git 指令:&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ git checkout --ours
&lt;/pre&gt;&lt;p&gt;
全用 REMOTE:&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;$ git checkout --theirs
&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/4140812250747805001/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2016/04/vimdiff-git-merge.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/4140812250747805001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/4140812250747805001'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2016/04/vimdiff-git-merge.html' title='用 vimdiff 作 git merge'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-2611694462164415477</id><published>2016-03-27T22:18:00.001+08:00</published><updated>2016-03-27T22:18:30.116+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="android"/><title type='text'>Android 雜項備忘</title><content type='html'>&lt;p&gt;有需要就慢慢補記....&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解開 apk 內容 (包含解開 binary 的 AndroidManifest.xml) &lt;a href=&quot;http://stackoverflow.com/questions/2097813/how-to-parse-the-androidmanifest-xml-file-inside-an-apk-package&quot;&gt;android - How to parse the AndroidManifest.xml file inside an .apk package - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/2611694462164415477/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2016/03/android.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/2611694462164415477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/2611694462164415477'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2016/03/android.html' title='Android 雜項備忘'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-5615168337329195674</id><published>2016-01-10T15:43:00.002+08:00</published><updated>2016-01-10T15:43:39.450+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="license"/><title type='text'>software license 備忘</title><content type='html'>&lt;p&gt;這東西太複雜了, 看到不錯的連結就備忘一下:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://choosealicense.com/licenses/&quot;&gt;Licenses - Choose a License&lt;/a&gt;: 簡化的比較表, 容易看出差異&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses#General_comparison&quot;&gt;Comparison of free and open-source software licenses - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://programmers.stackexchange.com/questions/189633/what-sublicense-actually-means/189704#189704&quot;&gt;licensing - What &amp;quot;sublicense&amp;quot; actually means? - Programmers Stack Exchange&lt;/a&gt;: BSD 和 MIT 不同, 前者不允許改 license, 後者可 (更寛鬆)&lt;/li&gt;
&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/5615168337329195674/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2016/01/software-license.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/5615168337329195674'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/5615168337329195674'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2016/01/software-license.html' title='software license 備忘'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-5629275734935400969</id><published>2015-11-21T15:58:00.000+08:00</published><updated>2015-11-21T16:33:15.641+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="multithread"/><category scheme="http://www.blogger.com/atom/ns#" term="thread-safety"/><title type='text'>multiple thread 存取共享資料的小技巧</title><content type='html'>&lt;p&gt;最近看到不錯的寫法, 備忘一下。
&lt;/p&gt;&lt;p&gt;
通常我們不希望 main thread 被 block [*1], 所以會另外開 worker thread 處理耗 CPU 的工作。主要的需求是避免 block main thread, 其次是縮短完成工作的時間。
&lt;/p&gt;&lt;p&gt;
由於 main thread 和 worker thread 需要互相傳遞一些資料, 有可能造成 race condition, 導致程式行為有時不正確或是掛掉。這裡討論幾種避免 race condition 的作法。
&lt;/p&gt;&lt;h4&gt;用一個 &quot;global&quot; lock 保護整個 thread 的資料&lt;/h4&gt;&lt;p&gt;worker thread 全部函式都用同一個 lock 保護。
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; 優點: 容易實作。&lt;/li&gt;
&lt;li&gt; 缺點: main thread  在 worker thread 忙的時候存取共享資料, 還是會被 worker thread block。&lt;/li&gt;
&lt;/ul&gt;&lt;h4&gt;不同群共享的資料用不同的 lock 保護&lt;/h4&gt;&lt;p&gt;和上個作法類似, 不過資料分群用不同 lock 保護。
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; 優點: 減少 main thread 被 block 的機會。&lt;/li&gt;
&lt;li&gt; 缺點: 實作有些複雜, 可能會漏保護到新加的變數。並且 main thread 仍有可能被 block。&lt;/li&gt;
&lt;/ul&gt;&lt;h4&gt;避免共享資料&lt;/h4&gt;&lt;p&gt;如果資料只有一個 thread 會用到, 就直接轉交給另一個 thread (改變 ownership)。若兩個 thread 都會用到, 先複製一份再轉交給另一個 thread。&lt;b&gt;沒有共享的資料, 就不需用 lock 保護了&lt;/b&gt; (但仍需透過 &lt;a href=&quot;https://en.wikipedia.org/wiki/Event_loop&quot;&gt;message loop&lt;/a&gt; 或 queue 傳遞資料到另一個 thread)。
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; 優點: 複製的時間不長, 且可以掌控占據 main thread 的時間, &lt;strong&gt;完全不受 worker thread 影響&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt; 缺點: 需要仔細設計複製和傳遞資料的機製, 確保沒有共享資料。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
需要同步資料的時候, 記得只能允許一個方向的 block。比方說 main thread 可以透過 conditional variable 來 block worker thread, 但不允許反過來的操作, 這樣就不會有 dead lock。
&lt;/p&gt;&lt;h4&gt;不用 lock 的注意事項&lt;/h4&gt;&lt;p&gt;每個函式都要放 assert 確保函式在正確的 thread 執行。若一個 class 有兩份資料分別在兩個 thread 下執行的話, 可以考慮訂兩個 struct, 比方說 MainData 和 WorkerData, 然後透過 private method 存取資料, 藉此確保 thread safe:
&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;// C++
class MyClass
{
public:
   ...

private:
  MainData&amp;amp; GetMainData();
  WorkerData&amp;amp; GetWorkerData();

  struct MainData {
    ...
  };
  struct WorkerData {
    ...
  };
  MainData m_mainData;
  WorkerData m_workerData;
};

MainData&amp;amp; MyClass::GetMainData()
{
  assert(InMainThread());
  return m_mainData;
}

WorkerData&amp;amp; MyClass::GetWorkerData()
{
  assert(InWorkerThread());
  return m_workerData;
}

&lt;/pre&gt;
&lt;p&gt;這樣程式寫錯時會造成 assert failed, 可以很快地修正。&lt;/p&gt;

&lt;h4&gt;備註&lt;/h4&gt;&lt;p&gt;&lt;span class=&quot;escaped&quot;&gt;*&lt;/span&gt;1 比方說 daemon 的 main thread 會不斷收新的連線, GUI 的 main thread 會收使用者的輸入。main thread 被 block 而沒有反應的話, 使用者會覺得軟體有問題。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/5629275734935400969/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2015/11/multiple-thread.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/5629275734935400969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/5629275734935400969'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2015/11/multiple-thread.html' title='multiple thread 存取共享資料的小技巧'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-4270369291608275618</id><published>2015-10-03T19:18:00.000+08:00</published><updated>2015-10-03T19:18:42.955+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="node.js"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>node.js 開發備忘</title><content type='html'>&lt;p&gt;前陣子用 node.js 寫了一個上線的服務, 沒有意外的話接下來有一大段時間不會碰  node.js。備忘一下有用到和以後可能會用到的東西。
&lt;/p&gt;&lt;h4&gt;何時以及為什麼要用 node.js?&lt;/h4&gt;&lt;ul&gt;&lt;li&gt; &lt;a href=&quot;http://fcamel-life.blogspot.tw/2015/08/async-programming.html&quot;&gt;fcamel 技術隨手記: 為什麼需要 async programming 以及相關技術的演進&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://fcamel-life.blogspot.tw/2015/06/nodejs-server-push.html&quot;&gt;fcamel 技術隨手記: 使用 Node.js 實作 server push&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h4&gt;node.js 開發&lt;/h4&gt;&lt;p&gt;主要弱點&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; main loop 被 block: &lt;a href=&quot;http://fcamel-life.blogspot.tw/2015/08/nodejs-block-main-loop.html&quot;&gt;fcamel 技術隨手記: 找出 node.js 中 block main loop 的程式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; CPU bound: 參考 &lt;a href=&quot;http://neilk.net/blog/2013/04/30/why-you-should-use-nodejs-for-CPU-bound-tasks/&quot;&gt;Why you should use Node.js for CPU-bound tasks - Neil Kandalgaonkar&lt;/a&gt;。視不同需求可用不同解法:&lt;ul&gt;
&lt;li&gt; 用 multi-process 增加 throughput 還有減少被 slow response 拖慢其它 request: 用 &lt;a href=&quot;https://nodejs.org/api/cluster.html&quot;&gt;cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; 用 multi-thread 避免 slow response 拖慢其它 request: 用 &lt;a href=&quot;https://github.com/xk/node-threads-a-gogo&quot;&gt;threads_a_gogo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; 甚至可以將 CPU bound 的工作丟給 browser 的  WebWorker 作, 不過這樣架構也會變複雜, 沒強烈必要不會走到這步吧。&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt; callback hell: 用 Promise 解 (JS 6 新 API), &lt;a href=&quot;https://github.com/petkaantonov/bluebird&quot;&gt;bluebird&lt;/a&gt; 是效率不錯的 backport, 可用在目前的 JS 5 。對於既有沒用 Promise 的 module 和函式, 也有提供 API 直接轉換物件成使用 Promise API, 不用改程式。&lt;ul&gt;
&lt;li&gt; &lt;a href=&quot;http://spion.github.io/posts/why-i-am-switching-to-promises.html&quot;&gt;Why I am switching to promises&lt;/a&gt; 提到 &lt;a href=&quot;https://github.com/petkaantonov/bluebird&quot;&gt;bluebird&lt;/a&gt; 效率不錯以及其它好處。&lt;/li&gt;
&lt;li&gt; 還有 &lt;a href=&quot;https://github.com/caolan/async&quot;&gt;async&lt;/a&gt; 也有助於減少雜亂的 callback codes, 不確定 &lt;a href=&quot;https://github.com/petkaantonov/bluebird&quot;&gt;bluebird&lt;/a&gt; 是否可完全替代 &lt;a href=&quot;https://github.com/caolan/async&quot;&gt;async&lt;/a&gt; 提供的功能。&lt;/li&gt;
&lt;li&gt; 幾年後 JS 7 的新語法  &lt;a href=&quot;https://thomashunter.name/blog/the-long-road-to-asyncawait-in-javascript/&quot;&gt;async/await&lt;/a&gt; 普及後, 可以考慮用它們取代 promise。現階段要 transcompile 後才能在 JS 5 內用。&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
其它&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; coding style: callback 函式的第一個參數總是放 error, 沒有 error 時傳入 null。&lt;/li&gt;
&lt;li&gt; 用 &lt;a href=&quot;https://github.com/remy/nodemon&quot;&gt;nodemon&lt;/a&gt; 在更新程式後自動更新。&lt;/li&gt;
&lt;li&gt; logger: 有很多家, 我是用 &lt;a href=&quot;https://github.com/winstonjs/winston&quot;&gt;winston&lt;/a&gt;, 還不錯用, 寫入 JSON log, 再用 &lt;a href=&quot;https://stedolan.github.io/jq/&quot;&gt;jq&lt;/a&gt; 讀。&lt;/li&gt;
&lt;li&gt; 用 &lt;a href=&quot;https://www.npmjs.com/package/posix&quot;&gt;posix&lt;/a&gt; 使用缺少的 POSIX API (如 setrlimit), 還有可以參考它如何包 C/C++ 程式給 node.js 用。&lt;/li&gt;
&lt;li&gt; web server: 大家都用 &lt;a href=&quot;http://expressjs.com/&quot;&gt;express&lt;/a&gt;, 要用 HTTPS 或 POST 需要作些設定。看起來彈性很大的 web server。&lt;/li&gt;
&lt;li&gt; 用舊版 node.js (0.10以前) 記得設 &lt;a href=&quot;http://stackoverflow.com/questions/10307837/how-do-you-set-maxsockets-in-node-js-when-using-express&quot;&gt;http max socket&lt;/a&gt; 提高對每個 domain 送出的連線數量。&lt;/li&gt;&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/4270369291608275618/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2015/10/nodejs.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/4270369291608275618'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/4270369291608275618'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2015/10/nodejs.html' title='node.js 開發備忘'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-8046247115458831879</id><published>2015-10-02T23:17:00.001+08:00</published><updated>2015-10-02T23:17:53.784+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="network"/><title type='text'>用 whois 查 IP 發行者的資訊</title><content type='html'>&lt;p&gt;最近才知道可以用 whois IP 查 IP 的擁有者和擁有的網段, 不過&lt;a href=&quot;http://webmasters.stackexchange.com/a/6645&quot;&gt; whois 回傳的內容沒有標準格式&lt;/a&gt;, 不方便寫程式直接處理。
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/8046247115458831879/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2015/10/whois-ip.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/8046247115458831879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/8046247115458831879'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2015/10/whois-ip.html' title='用 whois 查 IP 發行者的資訊'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-6014923381082777065</id><published>2015-09-29T21:27:00.000+08:00</published><updated>2015-10-02T11:30:38.518+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="http"/><category scheme="http://www.blogger.com/atom/ns#" term="https"/><category scheme="http://www.blogger.com/atom/ns#" term="man-in-the-middle"/><category scheme="http://www.blogger.com/atom/ns#" term="proxy"/><title type='text'>觀察 HTTP/HTTPS 傳輸的內容以及防範 man-in-the-middle attack</title><content type='html'>&lt;p&gt;看到許多強者推薦 &lt;a href=&quot;https://mitmproxy.org/&quot;&gt;mitmproxy&lt;/a&gt; (MITM 是 man-in-the-middle 的意思), 試用了一下的確超好用的。它用 man-in-the-middle attack 的方式觀察 (甚至更改) HTTP/HTTPS 傳輸的內容。
&lt;/p&gt;
&lt;p&gt;
 &lt;a href=&quot;http://blog.philippheckel.com/2013/07/01/how-to-use-mitmproxy-to-read-and-modify-https-traffic-of-your-phone/&quot;&gt;How To: Use mitmproxy to read and modify HTTPS traffic&lt;/a&gt; 介紹原理和用法, 設定很簡單。
&lt;/p&gt;&lt;p&gt;
官方文件也很清楚, 可交叉參考:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; &lt;a href=&quot;http://mitmproxy.org/doc/howmitmproxy.html&quot;&gt;mitmproxy 0.13 - How mitmproxy works&lt;/a&gt;: 有附圖解說明 proxy 連線過程&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;https://mitmproxy.org/doc/transparent/linux.html&quot;&gt;mitmproxy 0.13 - Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;https://mitmproxy.org/doc/transparent/osx.html&quot;&gt;mitmproxy 0.13 - OSX&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
這裡摘要一下 mitmproxy 作為 Transparent Proxy 的方式和原理:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt; 備好一台機器 R 作為觀察目標裝置 D (如手機) 的 gateway, 這樣就完成 man-in-the-middle 的設置。&lt;/li&gt;
&lt;li&gt; 設定 R 的 firewall, 重導往外連往 port 80 和 443 的連線到在 R 上執行的 mitmproxy。&lt;/li&gt;
&lt;li&gt; 在 D 上安裝 mitmproxy 的 certificate, 這樣 D 會相信 mitmproxy 幫自己簽的 certificate。&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;測試 Android 手機在 MITM 情況下 App 的反應&lt;/h4&gt;&lt;p&gt;我是用 ASUS Zenfone 2, 不過 App 的測試結果應該和手機無關。
&lt;/p&gt;&lt;p&gt;結果如下:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; Android OS 有出現訊息說網路可能被監控了, 但清掉訊息後就沒再出現了, 使用者應該不會注意到。&lt;/li&gt;
&lt;li&gt; 沒裝 mitmproxy certificate 的情況, Google Play 會出現連線錯誤; 裝 certificate 後可以用 Google Play 安裝 app。&lt;/li&gt;
&lt;li&gt; 沒裝 mitmproxy certificate 的情況, Chrome 會警告有問題; 有裝 certificate 會放行。&lt;/li&gt;
&lt;li&gt; 沒裝 mitmproxy certificate 的情況, Gmail 會如常運作, 看不到 HTTPS 傳的內容。裝 certificate 的後可以看到 HTTPS 傳的內容。雖然 Gmail 用自己訂的資料格式, 在搜尋信件時, 可看到搜尋字、回傳信件的標題、內文前面幾句的文字。&lt;/li&gt;
&lt;li&gt; 另外在有裝 mitmproxy certificate 的情況測了 Hangouts, Facebook, Messenger, Skype, Line。它們的主要傳輸內容都不是用 HTTPS, 改天再用 &lt;a href=&quot;https://github.com/droe/sslsplit&quot;&gt;SSLsplit&lt;/a&gt; 看看它們傳什麼。&lt;ul&gt;
&lt;li&gt; Line 會說 Wi-Fi 有問題無法用需要連線的功能 (訊息、電話、桌機 QR Code 登入), 剩下的 App 都可以用。&lt;/li&gt;
&lt;li&gt; Facebook 有部份內容用 HTTPS 傳, 並且用 JSON 作為內文編碼。&lt;/li&gt;
&lt;li&gt; Facebook 和 Messenger 有時候會顯示連線問題, 但放一陣子又通了, 也許它們有偵測網路情況用不同方式連線吧。&lt;/li&gt;
&lt;li&gt; Hangouts 意外地都沒有安全警告, 有沒有裝 mitmproxy certificate 都一樣, 可以正常使用。&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;其它 Web Proxy&lt;/h4&gt;&lt;p&gt;這些也是很有名的 Web Proxy, 有看到朋友推薦。不確定作為 transparent proxy 的效果如何, 至少監控本機的訊息應該滿方便的, 留著備忘。&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; &lt;a href=&quot;http://www.telerik.com/fiddler&quot;&gt;Fiddler free web debugging proxy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://www.charlesproxy.com/&quot;&gt;Charles Web Debugging Proxy • HTTP Monitor / HTTP Proxy / HTTPS &amp;amp; SSL Proxy / Reverse Proxy&lt;/a&gt;: 大家都說比 Fiddle 更威的工具, 是付費軟體, 有提供 30 天試用版。&lt;/li&gt;
&lt;/ul&gt;&lt;h4&gt;防範 man-in-the-middle attack&lt;/h4&gt;&lt;p&gt;聽 &lt;a href=&quot;http://scottt.tw/&quot;&gt;Scott&lt;/a&gt; 提到可用 &lt;a href=&quot;http://security.stackexchange.com/questions/29988/what-is-certificate-pinning&quot;&gt;Certificate Pinning&lt;/a&gt; 防 MITM attack。原理是只相信自己指定的 certificate (或幫你簽 certificate 的 certificate), 這樣就會忽視攻擊者裝到作業系統的 root CA。當 SSL handshake 的 certificate 被替換的時候,  會變成 handshake error。Certificate Pinning 似乎有些不同的作法, 先記住關鍵字, 之後再來研究。
&lt;/p&gt;&lt;p&gt;另一個好像可行但可能有問題的解法是不用 OS 提供的 certificate, 直接用自己包在 app 裡的 certificate (放 server 用的 certificate 或簽它的 CA 的 certificate, 意思一樣)。雖然這樣不用擔心裝置被裝了有問題的 root certificate, 但和 self-signed certificate 一樣, 需要處理另一個問題 (原本的問題): 如何確定 app 內裝了正確的 certificate? 這個難題原本是由 OS 處理, 假設 OS 沒安裝有問題的 root certificate, 所以可以用它的 root certificate。&lt;/p&gt;
&lt;h4&gt;2015-10-01 更新&lt;/h4&gt;
&lt;p&gt;經 &lt;a href=&quot;https://plus.google.com/u/0/+VictorHsieh&quot;&gt;Victor&lt;/a&gt; 告知, 發現 Gmail 應該有檢查 certificate, 我又測了一下, 並更正內文。&lt;/p&gt;
&lt;p&gt;Gmail 和 Google Play 有檢查 certificate, 只是使用者從系統刪除信任的 certificate 時, app 沒有同步更新, 仍會相信這個 certificate。另一方面, Chrome 有同步更新。
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/6014923381082777065/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/httphttps.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/6014923381082777065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/6014923381082777065'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/httphttps.html' title='觀察 HTTP/HTTPS 傳輸的內容以及防範 man-in-the-middle attack'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-3211695801526612087</id><published>2015-09-29T20:09:00.000+08:00</published><updated>2015-09-29T20:12:26.202+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="gzip"/><category scheme="http://www.blogger.com/atom/ns#" term="http"/><title type='text'>HTTP library 支援 gzip response body 的情況</title><content type='html'>&lt;p&gt;為了節省 HTTP response 的傳輸量, HTTP 有提供 request header Accept-Encoding: gzip 要求 server 「可以的話, 用 gzip 壓縮 body 再傳回來」, 還有 response header Content-Encoding: gzip 告知 client 「response body 有用 gzip 壓縮」。
&lt;/p&gt;&lt;p&gt;
在這樣的規定下, 推測 HTTP library 可能會有兩種實作方式:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; 不處理。使用者自己加 request header &quot;Accept-Encoding: gzip&quot;, 然後自己解 response body&lt;/li&gt;
&lt;li&gt; 有個 flag 開啟後會自動處理。自動加上 &quot;Accept-Encoding: gzip&quot;, 然後自動解 response body 並且移掉 response header &quot;Content-Encoding: gzip&quot;。另外要註明 API 取得的 content length 是否會受 gzip 影響, 結果如何變化。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
不過實際情況是:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; iOS NSURLConnection 文件沒有提到這件事, 但是會自動加 request header 並自動解 response body, 不過沒有移掉 response header &quot;Content-Encoding: gzip&quot; (&lt;a href=&quot;http://stackoverflow.com/questions/2682483/nsurlconnection-nsurlrequest-gzip-support&quot;&gt;Ref.&lt;/a&gt;, 實測結果確實如此, 不需手動加 Accept-Encoding: gzip 就有作用)&lt;/li&gt;
&lt;li&gt; Android HttpURLConnection 指明預設就會作, 並會清掉 Content-Encoding 和 Content-Length。可以用 Accept-Encoding: identity 覆寫預設值 (&lt;a href=&quot;http://developer.android.com/reference/java/net/HttpURLConnection.html&quot;&gt;Ref.&lt;/a&gt;)。&lt;/li&gt;
&lt;li&gt; curl 開啟 flag CURLOPT_ACCEPT_ENCODING 後會支援, 也是自動加 request header 並自動解 response body, 不過沒有移掉 response header &quot;Content-Encoding: gzip&quot; (&lt;a href=&quot;http://curl.haxx.se/dev/readme-encoding.html&quot;&gt;Ref.&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
結果只有 Android 的實作符合預期。使用前還是要先測過才準啊。
&lt;/p&gt;&lt;p&gt;
附帶一提, &lt;a href=&quot;http://httpbin.org/&quot;&gt;http://httpbin.org/&lt;/a&gt; 會回傳 request/response header, 用來測試 HTTP library 很方便。或是&lt;a href=&quot;http://blog.philippheckel.com/2013/07/01/how-to-use-mitmproxy-to-read-and-modify-https-traffic-of-your-phone/&quot;&gt;用 mitmproxy 作 transparent proxy&lt;/a&gt; 也可以, 相對之下費工一點, 不過看得最清楚。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/3211695801526612087/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/http-library-gzip-response-body.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/3211695801526612087'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/3211695801526612087'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/http-library-gzip-response-body.html' title='HTTP library 支援 gzip response body 的情況'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-723500908427859242</id><published>2015-09-21T21:56:00.000+08:00</published><updated>2015-09-21T21:56:06.014+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="android"/><title type='text'>Android 線上原始碼</title><content type='html'>&lt;ul&gt;&lt;li&gt; 線上搜尋: &lt;a href=&quot;http://androidxref.com/&quot;&gt;AndroidXRef&lt;/a&gt;, 進版還滿新的&lt;/li&gt;
&lt;li&gt; 官方線上列表: &lt;a href=&quot;https://android.googlesource.com/&quot;&gt;https://android.googlesource.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; 更多說明: &lt;a href=&quot;http://stackoverflow.com/questions/449763/where-can-i-browse-android-source-code-on-line&quot;&gt;Where can I browse Android source code on-line?&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/723500908427859242/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/android.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/723500908427859242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/723500908427859242'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/android.html' title='Android 線上原始碼'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-3735600105568283212</id><published>2015-09-21T21:39:00.000+08:00</published><updated>2015-09-21T21:49:31.999+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="https"/><category scheme="http://www.blogger.com/atom/ns#" term="network"/><category scheme="http://www.blogger.com/atom/ns#" term="wifi"/><title type='text'>偵測網路環境是否有 Captive Portal (使用 Wi-Fi 時會先導到登入網頁)</title><content type='html'>&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Captive_portal&quot;&gt;Captive Portal&lt;/a&gt; 是指在可以正常用網路前, 先導到一個登入網頁, 登入後才可以正常上網。通常用在需要付費上網的地方, 或是飯店提供免費但需帳號登入的 Wi-Fi。不過不限於 Wi-Fi 連線
&lt;/p&gt;&lt;p&gt;
這樣作的好處是使用者可以透過網頁看到比較詳細的說明, 像是「使用者同意書」、「使用限制」、「收費標準」等。對於 Wi-Fi 連線的供應者, 在連上 Wi-Fi 時登入 Wi-Fi 帳號和密碼, 就無法提供這些資訊。
&lt;/p&gt;&lt;p&gt;
但缺點是使用裝置會誤以為有網路可用 (因為 Wi-Fi 層級有通), 但其實還不能用。如果用的 app 沒有作進一步的偵測, 不會開啟登入網頁, 就無法上網了。使用者會覺得「Wi-Fi 有通 app 卻不能用」, 而覺得是 app 的問題。
&lt;/p&gt;&lt;p&gt;
&lt;a href=&quot;http://stackoverflow.com/questions/13958614/how-to-check-for-unrestricted-internet-access-captive-portal-detection/14030276#14030276&quot;&gt;Android 4.0.1 開始有內建一套偵測機制&lt;/a&gt; [*1], 發現有 &lt;a href=&quot;https://en.wikipedia.org/wiki/Captive_portal&quot;&gt;Captive Portal&lt;/a&gt; 時會通知使用者:
&lt;/p&gt;&lt;pre class=&quot;prettyprint&quot;&gt;private boolean isCaptivePortal(InetAddress server) {
    HttpURLConnection urlConnection = null;
    if (!mIsCaptivePortalCheckEnabled) return false;

    mUrl = &quot;http://&quot; + server.getHostAddress() + &quot;/generate_204&quot;;
    if (DBG) log(&quot;Checking &quot; + mUrl);
    try {
        URL url = new URL(mUrl);
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setInstanceFollowRedirects(false);
        urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
        urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
        urlConnection.setUseCaches(false);
        urlConnection.getInputStream();
        // we got a valid response, but not from the real google
        return urlConnection.getResponseCode() != 204;
    } catch (IOException e) {
        if (DBG) log(&quot;Probably not a portal: exception &quot; + e);
        return false;
    } finally {
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
    }
}
&lt;/pre&gt;&lt;p&gt;
作法是送 HTTP request 到 &lt;a href=&quot;http://clients3.google.com/generate_204&quot;&gt;http://clients3.google.com/generate_204&lt;/a&gt; [*2], 然後看 HTTP response code 是否為 &lt;tt&gt;204 No Content&lt;/tt&gt;。另一個作法是連 &lt;a href=&quot;http://www.google.com/blank.html&quot;&gt;http://www.google.com/blank.html&lt;/a&gt;, 然後檢查 &lt;tt&gt;Content-Length&lt;/tt&gt; 是否為 0。
&lt;/p&gt;&lt;p&gt;
不過當使用者用 HTTPS 上網時, 這招就不管用了, 連線可能出現 certificate error 或是沒反應。原因是 &lt;a href=&quot;http://fcamel-life.blogspot.tw/2015/09/web-proxy-https.html&quot;&gt;HTTPS 會先加密連線才傳 HTTP, 提供 Wi-Fi 的 AP 無法偵測到有 HTTP 連線&lt;/a&gt;, 自然也就無法重導到登入頁面。這正是 HTTPS 要的結果: 使用者送出的連線不該被任意地導到其它地方 (會讓使用者誤以為那是他連往的頁面)。所以....這招應該會在 HTTPS 普及後而愈來愈少人用吧。另一方面也沒有更好的作法可以讓 Wi-Fi 供給者先顯示訊息再讓使用者登入使用。
&lt;/p&gt;
&lt;h4&gt;備註&lt;/h4&gt;&lt;p&gt;1. &lt;a href=&quot;http://androidxref.com/5.1.1_r6/xref/frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java#653&quot;&gt;5.1.1 r6的程式碼有點不同&lt;/a&gt;, 變得更一般化, 可自訂測試用的網址還有將 &lt;tt&gt;200&lt;/tt&gt; + 無資料視為 &lt;tt&gt;204&lt;/tt&gt;, 不過概念一樣。
&lt;/p&gt;&lt;p&gt;
2. 原始碼是用 &lt;a href=&quot;http://clients3.google.com/generate_204&quot;&gt;http://clients3.google.com/generate_204&lt;/a&gt;, 不過用 &lt;a href=&quot;http://www.google.com/generate_204&quot;&gt;http://www.google.com/generate_204&lt;/a&gt; 也通
&lt;/p&gt;&lt;h4&gt;參考資料&lt;/h4&gt;&lt;ul&gt;&lt;li&gt; &lt;a href=&quot;http://stackoverflow.com/questions/3615147/how-to-create-wifi-popup-login-page&quot;&gt;iphone - How to create WiFi popup login page&lt;/a&gt;: 提到一些參考資料。&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://stackoverflow.com/questions/13958614/how-to-check-for-unrestricted-internet-access-captive-portal-detection/14030276#14030276&quot;&gt;android - How to check for unrestricted Internet access? (captive portal detection)&lt;/a&gt;: 附有 Android 的測試方法&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://android.stackexchange.com/questions/100657/how-to-disable-captive-portal-detection-how-to-remove-exclamation-mark-on-wi-fi/105611#105611&quot;&gt;wi fi - How to disable captive portal detection? How to remove exclamation mark on Wi-Fi or signal icon in Lollipop?&lt;/a&gt;: 4.2.2 後的測試網頁從寫死的 client3.google.com 換成傳入一個參數, 這樣系統才可以換用別的偵測網址。適合用在 Google 被封鎖的地區。&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://serverfault.com/questions/596844/ssl-certificate-errors-in-captive-portals&quot;&gt;redirect - SSL Certificate errors in Captive Portals&lt;/a&gt;: Captive Portal 遇到 HTTPS 會出現 certificate error。&lt;/li&gt;&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/3735600105568283212/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/wi-fi-sign-in-captive-portal.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/3735600105568283212'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/3735600105568283212'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/wi-fi-sign-in-captive-portal.html' title='偵測網路環境是否有 Captive Portal (使用 Wi-Fi 時會先導到登入網頁)'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-7566570125352944990</id><published>2015-09-18T23:59:00.000+08:00</published><updated>2015-09-29T16:16:06.182+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="https"/><category scheme="http://www.blogger.com/atom/ns#" term="network"/><category scheme="http://www.blogger.com/atom/ns#" term="proxy"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>Web Proxy 是否能處理 HTTPS 連線?</title><content type='html'>&lt;p&gt;在討論 HTTPS Proxy 前, 要先分清楚是在講那件事:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; Proxy 和 client 之間的連線用 HTTPS&lt;/li&gt;
&lt;li&gt; Proxy 允許 client 透過它往外用 HTTPS 連線&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
前者的好處是保護 client 和 Proxy 之間的連線, client 若是用 Wi-Fi 的話, 可藉此加密所有送出的 HTTP 連線, 防止有人偷聽 Wi-Fi 連線。
&lt;/p&gt;&lt;p&gt;
後者是本篇要討論的對象: &lt;strong&gt;Web Proxy 是否能支援 client 對外的 HTTPS 連線? 又是如何作到的?&lt;/strong&gt; 畢竟 HTTPS 是基於 SSL 連線的 HTTP, 既然資料都用 SSL 加密了, Proxy 看不到 HTTP header 和 body, 要怎麼作好 Web Proxy 的角色?
&lt;/p&gt;&lt;h4&gt;用 HTTP tunnel&lt;/h4&gt;&lt;p&gt;Proxy 接受 HTTP 1.1 的 CONNECT method, 讓 client 用 CONNECT 對外打通一個 TCP 連線 (又稱 &lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP_tunnel&quot;&gt;HTTP tunnel&lt;/a&gt;), 然後只作 TCP packet forwarding, 於是 client 要作什麼都可以。比方說用 TCP 連線作 SSL handshake 轉成加密連線, 接著就可以走 HTTPS, SPDY 或 HTTP 2 了。
&lt;/p&gt;&lt;p&gt;
這個作法下, &lt;strong&gt;Proxy server 不知道 client 對外傳的內容&lt;/strong&gt;, 只能經手封包, 不能作其它事。
&lt;/p&gt;&lt;h4&gt;置換 HTTPS 的 certificate&lt;/h4&gt;&lt;p&gt;Proxy 一樣接受 CONNECT method 建立 HTTP tunnel, 不過在 SSL handshake 時, 換掉目的地 Web Server 回傳的 certificate, 改用 Proxy 自己的。然後分別和 client 和外部 Web Server 完成 SSL handshake, 形成這樣的連線:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; client &amp;lt;-&amp;gt; Proxy: 用 Proxy 的 certificate 建立 SSL 連線, 使用 key K1。&lt;/li&gt;
&lt;li&gt; Proxy &amp;lt;-&amp;gt; 外部 Web Server: 用 Web Server 的 certificate 建立 SSL 連線, 使用 key K2。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
Proxy 收到 client 的資料先用 K1 解密, 再用 K2 加密傳出去; 資料回來的時候反過來比照辦理。這樣 Proxy 就能得知傳送的內容, 可以作 content filtering (如學校可以擋掉成年人才能看的內容)。
&lt;/p&gt;&lt;p&gt;
&lt;strong&gt;這個作法和 SSL man-in-the-middle attack 一樣&lt;/strong&gt;, 瀏覽器為了避免這類攻擊, 會檢查 certificate 和對應的 public key 資訊是否一致, 不一致時會跳出警告叫使用者不要繼續使用。
&lt;/p&gt;&lt;p&gt;
不過在可控制的環境下, IT 部門可以在使用者的電腦上裝上 Proxy 的 certificate, 這樣瀏覽器就不會跳出警告。另一方面, 使用者還是可以點選 HTTPS 的圖示檢查 certificate 資訊, 確認 certificate 是否有被換過, 有的話表示傳輸資料有可能被監聽。有興趣的話可以自己&lt;a href=&quot;https://mitmproxy.org/doc/transparent/linux.html&quot;&gt;裝 mitmproxy 作一個 transparent proxy 試看看&lt;/a&gt;。
&lt;/p&gt;
&lt;p&gt;
自己實作 HTTP client 時, 可以用 &lt;a href=&quot;http://security.stackexchange.com/questions/29988/what-is-certificate-pinning&quot;&gt;certificate pinning&lt;/a&gt; 擋掉 man-in-the-middle attack, 原理是只相信自己指定的 certificate, 這樣就會忽視攻擊者裝到作業系統的 root CA。
&lt;/p&gt;
&lt;p&gt;
&lt;h4&gt;其它討論&lt;/h4&gt;
&lt;p&gt;藉由了解 Web Proxy 如何處理 HTTPS, 可以得知: &lt;b&gt;在沒有對使用者裝置動手腳的情況下, Web Proxy 只能放行但不知道內容, 或是依網域名稱決定是否放行&lt;/b&gt; (無法細到網址, 因為那在 HTTP header 裡)。另外, 
Proxy 可以透過 HTTP 1.1 CONNECT method 傳的參數或是 SSL handshake 傳的 certificate 資訊得知網域名稱。藉此決定是否要放行某些網域的 HTTPS 連線。
&lt;/p&gt;
&lt;p&gt;&lt;strike&gt;雖然 Google 鼓勵大家多用 HTTPS 並且朝向 HTTP 2 邁進, 不過反向思考一下: 用 HTTP 然後自己在 application logic 裡加密資料, 也許可以讓 API 能用在更廣的地方。在特殊的情境下 (比方回傳無法連線的資訊), 或許值得一試?&lt;/strike&gt;
&lt;/p&gt;
&lt;h4&gt;參考資料&lt;/h4&gt;&lt;p&gt;HTTPS Proxy 相關資料&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; &lt;a href=&quot;http://security.stackexchange.com/questions/61334/does-an-https-proxy-encrypt-traffic-between-proxy-client-and-server-for-http-req/61336#61336&quot;&gt;Does an HTTPS proxy encrypt traffic between proxy client and server for HTTP requests?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;https://www.howtoforge.com/filtering-https-traffic-with-squid&quot;&gt;Filtering HTTPS Traffic With Squid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://www.chromium.org/spdy/spdy-proxy-examples&quot;&gt;SPDY Proxy Examples - The Chromium Projects&lt;/a&gt;: 有 client, Proxy 和 Web Server 之間的詳細連線過程 (順便備份在&lt;a href=&quot;https://gist.github.com/fcamel/1ec37365850abd13b081&quot;&gt;這裡&lt;/a&gt;)。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
關於 HTTP 1.1 CONNECT method&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; &lt;a href=&quot;http://stackoverflow.com/questions/6594604/connect-request-to-a-forward-http-proxy-over-an-ssl-connection/6594880#6594880&quot;&gt;CONNECT request to a forward HTTP proxy over an SSL connection?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://tools.ietf.org/html/rfc2817#section-5.2&quot;&gt;RFC 2817 - Upgrading to TLS Within HTTP/1.1&lt;/a&gt;: CONNECT method 的正式文件。&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;關於 Certificate Pinning
&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://security.stackexchange.com/questions/29988/what-is-certificate-pinning&quot;&gt;tls - What is certificate pinning? - Information Security Stack Exchange&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ikust/hello-pinnedcerts&quot;&gt;ikust/hello-pinnedcerts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.rlove.org/2015/01/public-key-pinning-hpkp.html&quot;&gt;Robert Love · Blog: Everything you Need to Know about HTTP Public Key Pinning (HPKP)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2015-09-29 更新&lt;/h4&gt;
&lt;p&gt;依 &lt;a href=&quot;http://scottt.tw/&quot;&gt;Scott&lt;/a&gt; 的建議補上 certificate pinning 和 mitmproxy 的資訊。還有刪掉部份內容&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/7566570125352944990/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/web-proxy-https.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/7566570125352944990'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/7566570125352944990'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/web-proxy-https.html' title='Web Proxy 是否能處理 HTTPS 連線?'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-7063240584272064635</id><published>2015-09-17T23:48:00.000+08:00</published><updated>2015-09-17T23:48:08.276+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="hotspot"/><category scheme="http://www.blogger.com/atom/ns#" term="network"/><category scheme="http://www.blogger.com/atom/ns#" term="ubuntu"/><category scheme="http://www.blogger.com/atom/ns#" term="wifi"/><title type='text'>用 Ubuntu 14.04 作為 Hotspot</title><content type='html'>&lt;p&gt;最近需要模擬被擋封包的網路環境, 想想用筆電作為 hotspot 再設 firewall, 應該滿容易的。然後用手機連上 hotspot, 就可以模擬出各種擋封包的情況 (如立即回傳 connection error, 或只是 drop 封包)。
&lt;/p&gt;&lt;p&gt;
實際操作後的確很容易, 裝好 Ubuntu 14.04 後, 需要的東西都齊了, 內建的 Network Manager 有圖形介面, 點一點就可以分享 Wi-Fi。iPad/iPhone 可以連得上, 但是 Android 不行, 原因是 Ubuntu 預設 hotspot 用 &lt;a href=&quot;http://www.howtogeek.com/180649/htg-explains-whats-the-difference-between-ad-hoc-and-infrastructure-mode/&quot;&gt;Wi-Fi ad hoc mode&lt;/a&gt;, 但是 &lt;a href=&quot;https://code.google.com/p/android/issues/detail?id=82&quot;&gt;Android 不支援&lt;/a&gt;, 要改用 Wi-Fi infrastructure mode 才行。作法見 &lt;a href=&quot;https://www.quora.com/How-do-I-create-hotspots-in-Ubuntu-14-04&quot;&gt;How do I create hotspots in Ubuntu 14.04?&lt;/a&gt;。
&lt;/p&gt;&lt;h4&gt;其它&lt;/h4&gt;&lt;ul&gt;&lt;li&gt; &lt;a href=&quot;http://developer.android.com/guide/topics/connectivity/wifip2p.html&quot;&gt;Android 4.0 後支援 Wi-Fi P2P&lt;/a&gt;, 可以讓 Android 裝置互連, 不過還是不支援 Wi-Fi ad hoc mode。&lt;/li&gt;
&lt;li&gt; 用 Wi-Fi ad hoc mode 的時候, iPad/iPhone 會在 Wi-Fi 列表的「裝置」看到筆電用的 SSID; 若改用 infrastructure mode 的時候, 會在 AP 列表看到筆電用的 SSID。&lt;/li&gt;
&lt;li&gt; 我有試過用 Mac OS X 作一樣的事, 但是新版 (我用 10.10.4) 內建只能用 pf 作 firewall, 和這東西不熟, 看起來沒 iptables 直覺。另外開啟 Internet Sharing 後 pf 就失效了, 就沒再深入研究。想想以後還是專心用 Linux 就好, 時間有限, 搞熟兩套 OS 的成本太高了。&lt;/li&gt;&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/7063240584272064635/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/ubuntu-1404-hotspot.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/7063240584272064635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/7063240584272064635'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/ubuntu-1404-hotspot.html' title='用 Ubuntu 14.04 作為 Hotspot'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-4054652443315277595</id><published>2015-09-16T23:26:00.000+08:00</published><updated>2015-09-16T23:44:29.091+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="http"/><category scheme="http://www.blogger.com/atom/ns#" term="protocol"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>HTTP POST 使用的編碼格式</title><content type='html'>&lt;p&gt;三不五時會用到相關的東西, 所以花了點時間查清楚, 本篇重點是參考資料。
&lt;/p&gt;&lt;p&gt;
用 HTTP 的 POST 的時候, 參數會放在 request body, 然後用 HTTP header &lt;tt&gt;Content-Type&lt;/tt&gt; 決定 request body 編碼方式。
&lt;/p&gt;&lt;p&gt;
常用的格式 ( Content-Type) 有兩種:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; application/x-www-form-urlencoded: HTML form 的預設值。和 GET 在 URL 後面帶的參數差不多, 用 &lt;a href=&quot;https://en.wikipedia.org/wiki/Percent-encoding&quot;&gt;URL encoding&lt;/a&gt; 編碼 (主要差異是&lt;a href=&quot;https://en.wikipedia.org/wiki/Percent-encoding#The_application.2Fx-www-form-urlencoded_type&quot;&gt;空白字元用 &#39;+&#39; 取代&lt;/a&gt;), 適合用在簡單的資料。&lt;/li&gt;
&lt;li&gt; multipart/form-data: 適合用在 binary data, 上傳檔案時要改用這個, 比較省空間 (檔案內容不需另外編碼)。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
application/x-www-form-urlencoded 用 &lt;a href=&quot;https://en.wikipedia.org/wiki/Percent-encoding&quot;&gt;URL encoding&lt;/a&gt;  編碼, 遇到大量 binary 資料時, 資料會變大很多 (可能會到三倍)。若先用 &lt;a href=&quot;https://en.wikipedia.org/wiki/Base64#Variants_summary_table&quot;&gt;base64&lt;/a&gt; [*1] 編碼再套 URL encoding 會好一些 (變成 4/3 倍), 但還是沒有 multipart/form-data 省。
&lt;/p&gt;&lt;p&gt;
除了上述的格式也可以用 application/json 或其它格式, 不過得看 web server 是否支援。JSON 不支援 binary data, 所以有 binary data 時要先用 base64 編碼, binary 資料一樣會變 4/3 倍大。雖然 base64 會增加 1/3 倍的資料量, 但它簡單易用, 我個人滿喜歡 base64 的設計。
&lt;/p&gt;&lt;p&gt;
若目的是要壓縮上傳的文字資料, 除了 application layer 自己壓縮資料外, 也可以在 transport layer 作: 使用 HTTP header &lt;tt&gt;Content-Encoding: gzip&lt;/tt&gt; 然後用 gzip 壓縮 request body [*2]。雖然 response 很常用 Content-Encoding: gzip, 但 request 很少這麼用的樣子, 需要改 web server 設定才能作用。如 &lt;a href=&quot;http://httpd.apache.org/docs/2.4/mod/mod_deflate.html&quot;&gt;apache2: 需另外設定 Input Decompression&lt;/a&gt;。
&lt;/p&gt;&lt;h4&gt;備註&lt;/h4&gt;&lt;p&gt;1. 嚴格來說, 要用 base64url (最後兩個 byte 不同), 對 URL encoding 比較安全。Google API 有用這個, 轉換的時候要留意。
&lt;/p&gt;&lt;p&gt;
2. 另一個選擇是用 &lt;tt&gt;Transfer-Encoding: gzip&lt;/tt&gt;, 這個作法語意比較正確, 但一來實作不如 &lt;tt&gt;Content-Encoding: gzip&lt;/tt&gt; 常見, 二來 &lt;tt&gt;Content-Encoding&lt;/tt&gt; 是 end-to-end, 不用擔心 Proxy 改變內容, 所以還是用 &lt;tt&gt;Content-Encoding&lt;/tt&gt; 較好。
&lt;/p&gt;&lt;h4&gt;參考資料&lt;/h4&gt;&lt;p&gt;POST &lt;tt&gt;Content-Type&lt;/tt&gt; 相關:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; &lt;a href=&quot;http://www.w3schools.com/jsref/prop_form_enctype.asp&quot;&gt;HTML DOM Form enctype Property&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://stackoverflow.com/a/28380690/278456&quot;&gt;What does enctype=&#39;multipart/form-data&#39; mean?&lt;/a&gt; 有 multipart/form、application/x-www-form-urlencoded 傳送內容的例子。&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data&quot;&gt;http - application/x-www-form-urlencoded or multipart/form-data?&lt;/a&gt;: 前幾個回答都值得看。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
&lt;tt&gt;Content-Encoding&lt;/tt&gt; 和 &lt;tt&gt;Transfer-Encoding&lt;/tt&gt; 相關:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; &lt;a href=&quot;http://stackapps.com/questions/916/why-content-encoding-gzip-rather-than-transfer-encoding-gzip&quot;&gt;support - Why Content-Encoding gzip rather than Transfer-Encoding gzip - Stack Apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://stackoverflow.com/questions/11641923/transfer-encoding-gzip-vs-content-encoding-gzip&quot;&gt;http - Transfer-Encoding: gzip vs. Content-Encoding: gzip - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;
其它:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_HTTP_header_fields&quot;&gt;List of HTTP header fields - Wikipedia, the free encyclopedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://www.pataliebre.net/howto-make-nginx-decompress-a-gzipped-request.html#.VfgySp2qqko&quot;&gt;Howto make Nginx decompress a gzipped request&lt;/a&gt;: 用 lua 解開 gzip 壓縮的 request body, 可減少 http client 上傳的資料量。&lt;/li&gt;
&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/4054652443315277595/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/http-post.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/4054652443315277595'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/4054652443315277595'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/http-post.html' title='HTTP POST 使用的編碼格式'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-8618358739944665661</id><published>2015-09-03T00:55:00.000+08:00</published><updated>2015-09-03T00:55:08.189+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="mac"/><title type='text'>Mac OS X 雜項常識</title><content type='html'>&lt;p&gt;備忘用, 持續更新。&lt;/p&gt;
&lt;h4&gt;硬體列表 (裝置沒反應時可看硬體形號找 driver)&lt;/h4&gt;
&lt;p&gt;
左上角 Apple 圖示 -&gt; About This Mac -&gt; System Report -&gt; Hardware
&lt;/p&gt;
&lt;h4&gt;分享無線網路 (當作 hotspot )&lt;/h4&gt;
&lt;p&gt;System Preference -&gt; Internet -&gt; Internet Sharing。開啟 Internet Sharing 後, 自訂的  pf rules 會失效。&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/8618358739944665661/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/mac-os-x.html#comment-form' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/8618358739944665661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/8618358739944665661'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/mac-os-x.html' title='Mac OS X 雜項常識'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4982215793068879977.post-3265269099913651110</id><published>2015-09-03T00:53:00.000+08:00</published><updated>2015-09-03T00:53:07.304+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="nginx"/><category scheme="http://www.blogger.com/atom/ns#" term="web"/><title type='text'>備忘 nginx 設定</title><content type='html'>&lt;p&gt;未來的某天應該會需要更了解 nginx, 現階段先隨便備忘用到的東西。&lt;/p&gt;
&lt;h4&gt;request route&lt;/h4&gt;
&lt;p&gt;
參考資料
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nullis.net/weblog/2011/05/nginx-rewrite-remove-file-extension/&quot;&gt;Using nginx rewrite&amp;apos;s to remove the file extension and still work with PHP-FastCGI — nullis.net&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_core_module.html#try_files&quot;&gt;Module ngx_http_core_module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite&quot;&gt;Module ngx_http_rewrite_module&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;
&lt;p&gt;
用 try_files 設定 URI 對到不同 URI 的順序; 用 rewrite 改寫多種組合到同一位置。兩者都可用來實現 request route。
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://fcamel-life.blogspot.com/feeds/3265269099913651110/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/nginx.html#comment-form' title='1 個意見'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/3265269099913651110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4982215793068879977/posts/default/3265269099913651110'/><link rel='alternate' type='text/html' href='http://fcamel-life.blogspot.com/2015/09/nginx.html' title='備忘 nginx 設定'/><author><name>fcamel</name><uri>http://www.blogger.com/profile/04792244455260595133</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>