﻿<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Xiaoxia[PG]</title>
	<atom:link href="http://xiaoxia.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://xiaoxia.org</link>
	<description>Yesterday is history, tomorrow is mistery, today is a gift!</description>
	<lastBuildDate>Tue, 21 Feb 2012 04:08:44 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>udpip: 用UDP封装IP数据包建立VPN</title>
		<link>http://xiaoxia.org/2012/02/21/udpip-vpn/</link>
		<comments>http://xiaoxia.org/2012/02/21/udpip-vpn/#comments</comments>
		<pubDate>Tue, 21 Feb 2012 03:57:48 +0000</pubDate>
		<dc:creator>Xiaoxia</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[我的代码]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tun]]></category>
		<category><![CDATA[udp]]></category>
		<category><![CDATA[vpn]]></category>

		<guid isPermaLink="false">http://xiaoxia.org/?p=4836</guid>
		<description><![CDATA[原理 使用Linux内核提供的tun设备建立可以在脚本读写的虚拟网卡，然后通过UDP将两个网卡的数据连接。 此方法能够使用以下特殊环境下： 1、客户端所在网络的路由不支持ppp，或者网络受到限制 2、TCP数据包被劫持或者受到限制 3、服务器是OpenVZ等不支持建立pptp，像我的burst的VPS就是这样子。 使用 服务器： # python udptun.py -s 86 -l 10.0.0.1/24 Configuring interface t0 with ip 10.0.0.1/24 客户端： # python udptun.py -c xiaoxia.org,86 -l 10.0.0.2/24 Configuring interface t0 with ip 10.0.0.2/24 Setting up new gateway ... Do login ... Logged in server succefully! 脚本代码 udptun.py: #!/usr/bin/python ''' UDP Tunnel VPN Xiaoxia [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://xiaoxia.org/upfiles/2012/02/6b36f1c6jw1dpzmluhd0kj.jpg"><img src="http://xiaoxia.org/upfiles/2012/02/6b36f1c6jw1dpzmluhd0kj.jpg" alt="" title="6b36f1c6jw1dpzmluhd0kj" width="440" height="248" class="alignnone size-full wp-image-4840" /></a></p>
<p><strong>原理</strong></p>
<p>使用Linux内核提供的tun设备建立可以在脚本读写的虚拟网卡，然后通过UDP将两个网卡的数据连接。</p>
<p><span id="more-4836"></span><br />
此方法能够使用以下特殊环境下：</p>
<p>1、客户端所在网络的路由不支持ppp，或者网络受到限制<br />
2、TCP数据包被劫持或者受到限制<br />
3、服务器是OpenVZ等不支持建立pptp，像我的burst的VPS就是这样子。</p>
<p><strong>使用</strong></p>
<p>服务器：</p>
<blockquote><p>
# python udptun.py -s 86 -l 10.0.0.1/24<br />
Configuring interface t0 with ip 10.0.0.1/24
</p></blockquote>
<p>客户端：</p>
<blockquote><p>
# python udptun.py -c xiaoxia.org,86 -l 10.0.0.2/24<br />
Configuring interface t0 with ip 10.0.0.2/24<br />
Setting up new gateway ...<br />
Do login ...<br />
Logged in server succefully!
</p></blockquote>
<p><strong>脚本代码</strong></p>
<p>udptun.py:</p>
<pre name="code" class="python">
#!/usr/bin/python

'''
    UDP Tunnel VPN
    Xiaoxia (xiaoxia@xiaoxia.org)
    Updated: 2012-2-21
'''

import os, sys
import hashlib
import getopt
import fcntl
import time
import struct
import socket, select
import traceback
import signal
import ctypes
import binascii

SHARED_PASSWORD = hashlib.sha1(&quot;xiaoxia&quot;).digest()
TUNSETIFF = 0x400454ca
IFF_TUN   = 0x0001

BUFFER_SIZE = 8192
MODE = 0
DEBUG = 0
PORT = 0
IFACE_IP = &quot;10.0.0.1/24&quot;
MTU = 1500
TIMEOUT = 60*10 # seconds

class Tunnel():
    def create(self):
        try:
            self.tfd = os.open(&quot;/dev/net/tun&quot;, os.O_RDWR)
        except:
            self.tfd = os.open(&quot;/dev/tun&quot;, os.O_RDWR)
        ifs = fcntl.ioctl(self.tfd, TUNSETIFF, struct.pack(&quot;16sH&quot;, &quot;t%d&quot;, IFF_TUN))
        self.tname = ifs[:16].strip(&quot;\x00&quot;)

    def close(self):
        os.close(self.tfd)

    def config(self, ip):
        print &quot;Configuring interface %s with ip %s&quot; % (self.tname, ip)
        os.system(&quot;ip link set %s up&quot; % (self.tname))
        os.system(&quot;ip link set %s mtu 1000&quot; % (self.tname))
        os.system(&quot;ip addr add %s dev %s&quot; % (ip, self.tname))

    def config_routes(self):
        if MODE == 1: # Server
            pass
        else: # Client
            print &quot;Setting up new gateway ...&quot;
            # Look for default route
            routes = os.popen(&quot;ip route show&quot;).readlines()
            defaults = [x.rstrip() for x in routes if x.startswith(&quot;default&quot;)]
            if not defaults:
                raise Exception(&quot;Default route not found, maybe not connected!&quot;)
            self.prev_gateway = defaults[0]
            self.prev_gateway_metric = self.prev_gateway + &quot; metric 2&quot;
            self.new_gateway = &quot;default dev %s metric 1&quot; % (self.tname)
            self.tun_gateway = self.prev_gateway.replace(&quot;default&quot;, IP)
            self.old_dns = file(&quot;/etc/resolv.conf&quot;, &quot;rb&quot;).read()
            # Remove default gateway
            os.system(&quot;ip route del &quot; + self.prev_gateway)
            # Add default gateway with metric
            os.system(&quot;ip route add &quot; + self.prev_gateway_metric)
            # Add exception for server
            os.system(&quot;ip route add &quot; + self.tun_gateway)
            # Add new default gateway
            os.system(&quot;ip route add &quot; + self.new_gateway)
            # Set new DNS to 8.8.8.8
            file(&quot;/etc/resolv.conf&quot;, &quot;wb&quot;).write(&quot;nameserver 8.8.8.8&quot;)

    def restore_routes(self):
        if MODE == 1: # Server
            pass
        else: # Client
            print &quot;Restoring previous gateway ...&quot;
            os.system(&quot;ip route del &quot; + self.new_gateway)
            os.system(&quot;ip route del &quot; + self.prev_gateway_metric)
            os.system(&quot;ip route del &quot; + self.tun_gateway)
            os.system(&quot;ip route add &quot; + self.prev_gateway)
            file(&quot;/etc/resolv.conf&quot;, &quot;wb&quot;).write(self.old_dns)

    def run(self):
        global PORT
        self.udpfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        if MODE == 1:
            self.udpfd.bind((&quot;&quot;, PORT))
        else:
            self.udpfd.bind((&quot;&quot;, 0))

        self.clients = {}
        self.logged = False
        self.try_logins = 5
        self.log_time = 0

        while True:
            if MODE == 2 and not self.logged and time.time() - self.log_time &gt; 2.:
                print &quot;Do login ...&quot;
                self.udpfd.sendto(&quot;LOGIN:&quot; + SHARED_PASSWORD + &quot;:&quot; +
                    IFACE_IP.split(&quot;/&quot;)[0], (IP, PORT))
                self.try_logins -= 1
                if self.try_logins == 0:
                    raise Exception(&quot;Failed to log in server.&quot;)
                self.log_time = time.time()

            rset = select.select([self.udpfd, self.tfd], [], [], 1)[0]
            for r in rset:
                if r == self.tfd:
                    if DEBUG: os.write(1, &quot;&gt;&quot;)
                    data = os.read(self.tfd, MTU)
                    if MODE == 1: # Server
                        src, dst = data[16:20], data[20:24]
                        for key in self.clients:
                            if dst == self.clients[key][&quot;localIPn&quot;]:
                                self.udpfd.sendto(data, key)
                        # Remove timeout clients
                        curTime = time.time()
                        for key in self.clients.keys():
                            if curTime - self.clients[key][&quot;aliveTime&quot;] &gt; TIMEOUT:
                                print &quot;Remove timeout client&quot;, key
                                del self.clients[key]
                    else: # Client
                        self.udpfd.sendto(data, (IP, PORT))
                elif r == self.udpfd:
                    if DEBUG: os.write(1, &quot;&lt;&quot;)
                    data, src = self.udpfd.recvfrom(BUFFER_SIZE)
                    if MODE == 1: # Server
                        key = src
                        if key not in self.clients:
                            # New client comes
                            try:
                                if data.startswith(&quot;LOGIN:&quot;) and data.split(&quot;:&quot;)[1]==SHARED_PASSWORD:
                                    localIP = data.split(&quot;:&quot;)[2]
                                    self.clients[key] = {&quot;aliveTime&quot;: time.time(),
                                                        &quot;localIPn&quot;: socket.inet_aton(localIP)}
                                    print &quot;New Client from&quot;, src, &quot;request IP&quot;, localIP
                                    self.udpfd.sendto(&quot;LOGIN:SUCCESS&quot;, src)
                            except:
                                print &quot;Need valid password from&quot;, src
                                self.udpfd.sendto(&quot;LOGIN:PASSWORD&quot;, src)
                        else:
                            # Simply write the packet to local or forward them to other clients ???
                            os.write(self.tfd, data)
                            self.clients[key][&quot;aliveTime&quot;] = time.time()
                    else: # Client
                        if data.startswith(&quot;LOGIN&quot;):
                            if data.endswith(&quot;PASSWORD&quot;):
                                self.logged = False
                                print &quot;Need password to login!&quot;
                            elif data.endswith(&quot;SUCCESS&quot;):
                                self.logged = True
                                self.try_logins = 5
                                print &quot;Logged in server succefully!&quot;
                        else:
                            os.write(self.tfd, data)

def usage(status = 0):
    print &quot;Usage: %s [-s port|-c serverip] [-hd] [-l localip]&quot; % (sys.argv[0])
    sys.exit(status)

def on_exit(no, info):
    raise Exception(&quot;TERM signal caught!&quot;)

if __name__==&quot;__main__&quot;:
    opts = getopt.getopt(sys.argv[1:],&quot;s:c:l:hd&quot;)
    for opt,optarg in opts[0]:
        if opt == &quot;-h&quot;:
            usage()
        elif opt == &quot;-d&quot;:
            DEBUG += 1
        elif opt == &quot;-s&quot;:
            MODE = 1
            PORT = int(optarg)
        elif opt == &quot;-c&quot;:
            MODE = 2
            IP, PORT = optarg.split(&quot;,&quot;)
            IP = socket.gethostbyname(IP)
            PORT = int(PORT)
        elif opt == &quot;-l&quot;:
            IFACE_IP = optarg

    if MODE == 0 or PORT == 0:
        usage(1)

    tun = Tunnel()
    tun.create()
    tun.config(IFACE_IP)
    signal.signal(signal.SIGTERM, on_exit)
    tun.config_routes()
    try:
        tun.run()
    except KeyboardInterrupt:
        pass
    except:
        print traceback.format_exc()
    finally:
        tun.restore_routes()
        tun.close()
</pre>
]]></content:encoded>
			<wfw:commentRss>http://xiaoxia.org/2012/02/21/udpip-vpn/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Linux下zergRush给Android2.3.6(ME865)获取root权限</title>
		<link>http://xiaoxia.org/2012/02/13/root-android-2-3-under-linux-with-zerg/</link>
		<comments>http://xiaoxia.org/2012/02/13/root-android-2-3-under-linux-with-zerg/#comments</comments>
		<pubDate>Mon, 13 Feb 2012 06:24:08 +0000</pubDate>
		<dc:creator>Xiaoxia</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[adb]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[me865]]></category>
		<category><![CDATA[root]]></category>
		<category><![CDATA[zerg]]></category>

		<guid isPermaLink="false">http://xiaoxia.org/?p=4824</guid>
		<description><![CDATA[昨天跟金华同学在大学城寻找新片景点，在路上，遇到一棵骨骼精奇的植物。掏出手机拍下以记之！ 按照我的个人习惯，使用Linux系统喜欢用root，主要是方便干活。所以，拿到了新手机，不用root权限，怎么去深入探究它系统的工作原理呢？ 下面马上动手，使用国外大牛利用系统bug写的获取root权限的程序。 1. 下载工具 su，Superuser.apk，提权程序zerg 各程序的用途： su：切换用户运行进程。 Superuser.apk：在Android上管理具有root权限的应用程序。 zerg：使用系统漏洞获取root权限，有兴趣的朋友可以研究github上的源代码 https://github.com/revolutionary/zergRush/blob/master/zergRush.c。 我尝试使用新版的zergRush，但是失败了 为了不误导后人，我这里只给出成功的方案。 2. 设置调试模式 打开在手机上的“设置->应用程序->开发->USB调试”，同时开启“设置->应用程序->未知源”。使用USB接入电脑，USB连接模式选择“仅充电”。 3. 复制文件 在终端里切换到工具目录下。首先，检查设备是否已连接。 root@xiaoxia-pc:/media/Data/moto/Exploit# adb devices List of devices attached 0149BD1711001013 device 从PC上复制所需的3个文件到设备上，例如/data/local目录下。 root@xiaoxia-pc:/media/Data/moto/Exploit# ls su Superuser.apk zerg root@xiaoxia-pc:/media/Data/moto/Exploit# adb push zerg /data/local 505 KB/s (22776 bytes in 0.043s) root@xiaoxia-pc:/media/Data/moto/Exploit# adb push su /data/local 561 KB/s (22228 bytes in [...]]]></description>
			<content:encoded><![CDATA[<p>昨天跟金华同学在大学城寻找新片景点，在路上，遇到一棵骨骼精奇的植物。掏出手机拍下以记之！</p>
<p><a href="http://xiaoxia.org/upfiles/2012/02/2012-02-12_15-25-09_273.jpg"><img src="http://xiaoxia.org/upfiles/2012/02/2012-02-12_15-25-09_273.jpg" alt="" title="2012-02-12_15-25-09_273" width="500" height="282" class="alignnone size-full wp-image-4826" /></a><br />
<span id="more-4824"></span></p>
<p>按照我的个人习惯，使用Linux系统喜欢用root，主要是方便干活。所以，拿到了新手机，不用root权限，怎么去深入探究它系统的工作原理呢？</p>
<p>下面马上动手，使用国外大牛利用系统bug写的获取root权限的程序。</p>
<p><strong>1. 下载工具</strong></p>
<p><a href="http://forum.xda-developers.com/attachment.php?attachmentid=767802&#038;stc=1&#038;d=1320119485">su，Superuser.apk，提权程序zerg</a></p>
<p>各程序的用途：</p>
<p>su：切换用户运行进程。<br />
Superuser.apk：在Android上管理具有root权限的应用程序。<br />
zerg：使用系统漏洞获取root权限，有兴趣的朋友可以研究github上的源代码 <a href="https://github.com/revolutionary/zergRush/blob/master/zergRush.c">https://github.com/revolutionary/zergRush/blob/master/zergRush.c</a>。</p>
<p>我尝试使用新版的<a href="http://cloud.github.com/downloads/revolutionary/zergRush/zergRush.zip">zergRush</a>，但是失败了 <img src='http://xiaoxia.org/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' />  为了不误导后人，我这里只给出成功的方案。</p>
<p><strong>2. 设置调试模式</strong></p>
<p>打开在手机上的“设置->应用程序->开发->USB调试”，同时开启“设置->应用程序->未知源”。使用USB接入电脑，USB连接模式选择“仅充电”。</p>
<p><strong>3. 复制文件</strong></p>
<p>在终端里切换到工具目录下。首先，检查设备是否已连接。</p>
<blockquote><p>root@xiaoxia-pc:/media/Data/moto/Exploit# adb devices<br />
List of devices attached<br />
0149BD1711001013        device</p>
</blockquote>
<p>从PC上复制所需的3个文件到设备上，例如/data/local目录下。</p>
<blockquote><p>root@xiaoxia-pc:/media/Data/moto/Exploit# ls<br />
su  Superuser.apk  zerg<br />
root@xiaoxia-pc:/media/Data/moto/Exploit# adb push zerg /data/local<br />
505 KB/s (22776 bytes in 0.043s)<br />
root@xiaoxia-pc:/media/Data/moto/Exploit# adb push su /data/local<br />
561 KB/s (22228 bytes in 0.038s)<br />
root@xiaoxia-pc:/media/Data/moto/Exploit# adb push Superuser.apk /data/local<br />
3339 KB/s (754980 bytes in 0.220s)</p></blockquote>
<p><strong>4. 获取root权限</strong></p>
<p>连接远程shell，设置文件为可执行。</p>
<blockquote><p>root@xiaoxia-pc:/media/Data/moto/Exploit# adb shell<br />
shell@edison:/$ cd /data/local<br />
shell@edison:/data/local$ chmod 777 zerg</p></blockquote>
<p>运行zerg，围观攻击结果。</p>
<blockquote><p>shell@edison:/data/local$ ./zerg</p>
<p>[**] Zerg rush - Android 2.2/2.3 local root<br />
[**] (C) 2011 Revolutionary. All rights reserved.</p>
<p>[**] Parts of code from Gingerbreak, (C) 2010-2011 The Android Exploid Crew.</p>
<p>[+] Found a GingerBread ! 0x00016118<br />
[*] Scooting ...<br />
[*] Sending 149 zerglings ...<br />
[*] Sending 189 zerglings ...<br />
[+] Zerglings found a way to enter ! 0x18<br />
[*] Sending 189 zerglings ...<br />
[*] Trying a new path ...<br />
[*] Sending 189 zerglings ...<br />
[*] Trying a new path ...<br />
[*] Sending 189 zerglings ...<br />
[*] Trying a new path ...<br />
[*] Sending 189 zerglings ...<br />
[+] Zerglings caused crash (good news): 0x40122c5c 0x0074<br />
[*] Researching Metabolic Boost ...<br />
[+] Speedlings on the go ! 0xafd25ac9 0xafd397f7<br />
[*] Sending 185 zerglings ...</p>
<p>[+] Rush did it ! It's a GG, man !<br />
[+] Killing ADB and restarting as root... enjoy!<br />
root@xiaoxia-pc:/media/Data/moto/Exploit#</p></blockquote>
<p>成功获取root权限并且切断了adb连接。重新运行adb shell，就应该变为root了。</p>
<blockquote><p>root@xiaoxia-pc:/media/Data/moto/Exploit# adb shell<br />
root@edison:/#</p></blockquote>
<p>提示符从$变为#。（如果没有#，提权失败。）</p>
<p><strong>5. 安装su，Superuser.apk</strong></p>
<p>把文件系统挂载为可写入。</p>
<blockquote><p>root@edison:/# mount -o rw,remount /dev/null /system</p></blockquote>
<p>复制su，Superuser到安装目录。不知道为何，我这里复制文件不可以用cp，只能用cat？</p>
<blockquote><p>root@edison:/# cat /data/local/su > /system/bin/su<br />
root@edison:/# cat /data/local/Superuser.apk > /system/app/Superuser.apk<br />
root@edison:/# chmod 4755 /system/bin/su<br />
root@edison:/# chmod 4755 /system/app/Superuser.apk</p></blockquote>
<p>重启手机。话说，用reboot命令重启很快，屏幕一下子就黑了。</p>
<blockquote><p>root@edison:/# reboot</p></blockquote>
<p><strong>6. 效果</strong></p>
<p>重启手机后，可以使用adb连接shell，然后测试su命令是否可以使用！</p>
<blockquote><p>root@xiaoxia-pc:/media/Data/moto/Exploit# adb shell<br />
shell@edison:/$ su</p></blockquote>
<p>Superuser.apk提示</p>
<p><a href="http://xiaoxia.org/upfiles/2012/02/device-2012-02-13-020305.jpg"><img src="http://xiaoxia.org/upfiles/2012/02/device-2012-02-13-020305-396x700.jpg" alt="" title="device-2012-02-13-020305" width="396" height="700" class="alignnone size-medium wp-image-4825" /></a></p>
<p>允许之后，shell得到root权限。可以做任何事情了！</p>
<blockquote><p>root@edison:/#</p></blockquote>
<p>此次root是为了下次我在Android2.3建立VPN（类似Android4.0的VpnService.Builder实现的功能）提供环境基础。</p>
<p>参考资料：</p>
<p>http://forum.xda-developers.com/showthread.php?t=1327741</p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoxia.org/2012/02/13/root-android-2-3-under-linux-with-zerg/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Android开发笔记：编写运行第一个手机程序如此简单</title>
		<link>http://xiaoxia.org/2012/02/11/android-note-first-app/</link>
		<comments>http://xiaoxia.org/2012/02/11/android-note-first-app/#comments</comments>
		<pubDate>Fri, 10 Feb 2012 18:50:28 +0000</pubDate>
		<dc:creator>Xiaoxia</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[android-x86]]></category>
		<category><![CDATA[eclipse]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[me865]]></category>

		<guid isPermaLink="false">http://xiaoxia.org/?p=4811</guid>
		<description><![CDATA[我与Java 大二的时候，修过Java的选修课，考了90多分。虽说如此，但一直以来，我的Java水平仅停留在只会写一个简单的Hello world。可能是我对Java一向没有什么好感，平时也不会用到它！Kubuntu里的OpenJDK和Eclipse，都是为了交作业而安装的:) 开发环境配置 昨天就搭建好了Android开发环境了。我发现我之前安装的Eclipse Galileo版本是安装不了官网上下载的ADT插件的。所以，我被迫去Eclipse官网重新下载了最新的Eclipse Indigo Classic 3.7.1。问题解决了。另外，用OpenJDK代替Sun JDK完全没有问题！ 开发环境的搭建参考官网的详细教程： http://developer.android.com/sdk/installing.html 参考这篇教程，我在Android模拟器上成功运行了一个的Hello world： http://developer.android.com/resources/tutorials/hello-world.html 一个选择题程序 在Hello world的基础上，稍加修改，做了一个简单的选择题程序。得益于Eclipse便捷的UI编辑工具，像堆砌积木一样，不到1分钟就做好了界面！请大家无视界面内容，以免影响你的情绪。 界面的XML配置内容如下，在按钮里，指定了响应onClick事件的函数。 &#60;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&#62; &#60;LinearLayout xmlns:android=&#34;http://schemas.android.com/apk/res/android&#34; android:layout_width=&#34;fill_parent&#34; android:layout_height=&#34;fill_parent&#34; android:orientation=&#34;vertical&#34; &#62; &#60;TextView android:layout_width=&#34;fill_parent&#34; android:layout_height=&#34;wrap_content&#34; android:text=&#34;@string/question&#34; /&#62; &#60;RadioGroup android:id=&#34;@+id/radioGroup1&#34; android:layout_width=&#34;wrap_content&#34; android:layout_height=&#34;wrap_content&#34; &#62; &#60;RadioButton android:id=&#34;@+id/radio0&#34; android:layout_width=&#34;wrap_content&#34; android:layout_height=&#34;wrap_content&#34; android:checked=&#34;true&#34; android:text=&#34;小虾&#34; /&#62; &#60;RadioButton android:id=&#34;@+id/radio1&#34; android:layout_width=&#34;wrap_content&#34; android:layout_height=&#34;wrap_content&#34; android:text=&#34;iceboy&#34; /&#62; &#60;RadioButton android:id=&#34;@+id/radio2&#34; android:layout_width=&#34;wrap_content&#34; android:layout_height=&#34;wrap_content&#34; [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://xiaoxia.org/upfiles/2012/02/android_logo.gif"><img src="http://xiaoxia.org/upfiles/2012/02/android_logo-700x525.gif" alt="" title="android_logo" width="400" height="300" class="alignnone size-medium wp-image-4812" /></a><br />
<span id="more-4811"></span></p>
<p><strong>我与Java</strong></p>
<p>大二的时候，修过Java的选修课，考了90多分。虽说如此，但一直以来，我的Java水平仅停留在只会写一个简单的Hello world。可能是我对Java一向没有什么好感，平时也不会用到它！Kubuntu里的OpenJDK和Eclipse，都是为了交作业而安装的:)</p>
<p><strong>开发环境配置</strong></p>
<p>昨天就搭建好了Android开发环境了。我发现我之前安装的Eclipse Galileo版本是安装不了官网上下载的ADT插件的。所以，我被迫去Eclipse官网重新下载了最新的Eclipse Indigo Classic 3.7.1。问题解决了。另外，用OpenJDK代替Sun JDK完全没有问题！</p>
<p>开发环境的搭建参考官网的详细教程：</p>
<p><a href="http://developer.android.com/sdk/installing.html" target="_blank">http://developer.android.com/sdk/installing.html</a></p>
<p>参考这篇教程，我在Android模拟器上成功运行了一个的Hello world：</p>
<p><a href="http://developer.android.com/resources/tutorials/hello-world.html" target="_blank">http://developer.android.com/resources/tutorials/hello-world.html</a></p>
<p><strong>一个选择题程序</strong></p>
<p>在Hello world的基础上，稍加修改，做了一个简单的选择题程序。得益于Eclipse便捷的UI编辑工具，像堆砌积木一样，不到1分钟就做好了界面！请大家无视界面内容，以免影响你的情绪。</p>
<p><a href="http://xiaoxia.org/upfiles/2012/02/197.png"><img src="http://xiaoxia.org/upfiles/2012/02/197.png" alt="" title="197" width="695" height="567" class="alignnone size-full wp-image-4813" /></a></p>
<p>界面的XML配置内容如下，在按钮里，指定了响应onClick事件的函数。</p>
<pre name="code" class="xml">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:layout_width=&quot;fill_parent&quot;
    android:layout_height=&quot;fill_parent&quot;
    android:orientation=&quot;vertical&quot; &gt;

    &lt;TextView
        android:layout_width=&quot;fill_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;@string/question&quot; /&gt;

    &lt;RadioGroup
        android:id=&quot;@+id/radioGroup1&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot; &gt;

        &lt;RadioButton
            android:id=&quot;@+id/radio0&quot;
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:checked=&quot;true&quot;
            android:text=&quot;小虾&quot; /&gt;

        &lt;RadioButton
            android:id=&quot;@+id/radio1&quot;
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:text=&quot;iceboy&quot; /&gt;

        &lt;RadioButton
            android:id=&quot;@+id/radio2&quot;
            android:layout_width=&quot;wrap_content&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:text=&quot;小白&quot; /&gt;
    &lt;/RadioGroup&gt;

    &lt;Button
        android:id=&quot;@+id/button1&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:text=&quot;确定&quot;
        android:onClick=&quot;clickOK&quot;/&gt;

&lt;/LinearLayout&gt;
</pre>
<p>在Activity代码里给按钮加入响应事件，打完代码，按Ctrl+Shift+O自动补全import，Eclipse的代码补全强大的惊人。</p>
<p>完整的Activity代码如下：</p>
<pre name="code" class="java">
public class HelloAndroidActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.main);
    }

    public void clickOK(View view){
    	RadioGroup rg = (RadioGroup)findViewById(R.id.radioGroup1);
    	int id = rg.getCheckedRadioButtonId();
    	String text = "";
    	switch(id){
    	case R.id.radio0:
    		text = "你太聪明了！";
    		break;
    	case R.id.radio1:
    	case R.id.radio2:
    		text = "你太愚昧了！";
    		break;
    	}
    	new AlertDialog.Builder(this)
	    	.setTitle("结果")
	    	.setMessage(text)
	    	.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
	    		public void onClick(DialogInterface dialog, int which) {
	    			dialog.dismiss();
	    			HelloAndroidActivity.this.finish();
	    		}
	    	}).show();
    }
}
</pre>
<p>按钮事件由clickOK函数响应，判断RadioGroup中被选中的按钮ID，使用AlertDialog输出提示结果信息。</p>
<p>这个AlertDialog的show方法不会阻塞当前线程，而弹出的对话框的按钮响应事件是异步的。</p>
<p>使用Activity的this.finish();方法来退出程序。不过这样做好像没有结束进程，请教Java大牛，如果要结束进程，是使用System.exit()不？</p>
<p><strong>模拟器运行</strong></p>
<p>使用SDK自带的模拟器运行的效果：</p>
<p><a href="http://xiaoxia.org/upfiles/2012/02/198.png"><img src="http://xiaoxia.org/upfiles/2012/02/198.png" alt="" title="198" width="576" height="540" class="alignnone size-full wp-image-4814" /></a></p>
<p><strong>手机运行</strong></p>
<p>然后，我想试试放在手机上运行。一开始，我在Eclipse的菜单里，通过导出未签名的Package，用蓝牙传输到手机上，运行之后，提示“应用程序未安装”。十分奇怪，难道是因为没有签名？？？上网查了一下，看来就是这个原因！</p>
<p>我直接复制工程目录下bin下的apk文件到手机上，意料之中，运行成功！</p>
<p><a href="http://xiaoxia.org/upfiles/2012/02/IMG_3210.jpg"><img src="http://xiaoxia.org/upfiles/2012/02/IMG_3210.jpg" alt="" title="IMG_3210" width="700" height="856" class="alignnone size-full wp-image-4815" /></a></p>
<p><strong>Android-x86运行</strong></p>
<p>另外，我把apk文件复制到了我的Android-x86虚拟机上！这个Android-x86比起java那个模拟器，速度相差太大了。大家可以看看：</p>
<p><a href="http://xiaoxia.org/upfiles/2012/02/200.png"><img src="http://xiaoxia.org/upfiles/2012/02/200.png" alt="" title="200" width="590" height="251" class="alignnone size-full wp-image-4816" /></a></p>
<p>java模拟器CPU一直是100%，导致我的笔记本风扇一直在高速运转。而在VirtualBox的Android-x86只用了1%的CPU。相差甚远！另外，Android-x86的开机速度非常之快。不过，我还是习惯使用Virtualbox的休眠功能，连开机过程都免了。</p>
<p>因为我还没有配置好Eclipse与虚拟机的连接，所以我这里用了另外一种方法来复制文件到x86的虚拟机里。非常实用！！！</p>
<p>在终端里切换到apk所在的目录下，然后运行</p>
<blockquote><p>
root@xiaoxia-pc:/java/HelloAndroid/bin# python -m SimpleHTTPServer<br />
Serving HTTP on 0.0.0.0 port 8000 ...
</p></blockquote>
<p>然后在虚拟机的Android里，输入网址 http://192.168.56.1:8000/</p>
<p><a href="http://xiaoxia.org/upfiles/2012/02/201.png"><img src="http://xiaoxia.org/upfiles/2012/02/201.png" alt="" title="201" width="437" height="359" class="alignnone size-full wp-image-4817" /></a></p>
<p>Android-x86里的运行效果：</p>
<p><a href="http://xiaoxia.org/upfiles/2012/02/199.png"><img src="http://xiaoxia.org/upfiles/2012/02/199-700x581.png" alt="" title="199" width="700" height="581" class="alignnone size-medium wp-image-4818" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoxia.org/2012/02/11/android-note-first-app/feed/</wfw:commentRss>
		<slash:comments>27</slash:comments>
		</item>
		<item>
		<title>网购Motorola ME865</title>
		<link>http://xiaoxia.org/2012/02/11/mymoto-me865/</link>
		<comments>http://xiaoxia.org/2012/02/11/mymoto-me865/#comments</comments>
		<pubDate>Fri, 10 Feb 2012 17:19:03 +0000</pubDate>
		<dc:creator>Xiaoxia</dc:creator>
				<category><![CDATA[我的生活]]></category>
		<category><![CDATA[me865]]></category>
		<category><![CDATA[taobao]]></category>

		<guid isPermaLink="false">http://xiaoxia.org/?p=4806</guid>
		<description><![CDATA[上学期跟雄哥商量好一起编写一个手机玩的3D游戏。我觉得他的Idea非常好，于是就决定跟他一起干了！一开始定位在Apple的IOS上，为了给iPhone写程序，还真不容易，雄哥弄来一台苹果笔记本，还需要向苹果公司交开发费用才能够编写程序。所以最终还是放弃了IOS平台，改为Android平台了。雄哥效率还是挺高的，一下子把Android开发平台也搭建好了，开始测试Bullet引擎，制作了Tutorial方便我快速配置开发环境。 不幸的是，上年11月左右我的Acer Liquid手机在广州的公交车上被偷了。此后我借用了荣哥的旧手机，仅有简单的电话和短信功能，一直用到现在。Android的开发热情也没了。春节后，凑合着，在淘宝网购了一台Moto ME865，卖家是“我的名字叫什么_tb”。说到这网购，还真不容易！之前给别人网购过两次水货手机，都用不到一年就出问题了。这次购机特别谨慎！ 大前天收到了手机，做了全面的检查，还下载了屏幕检测软件来给手机体检。这一测，还真被我发现了问题！手机有一坏点。在黑屏的时候，看到中央附近有一个蓝色的亮点。只要那块地方不是全黑，就看不到这个坏点。同时，在解除屏幕锁定或者通话时候从耳边拿开，亮屏的瞬间，闪出一道白色的竖线。非常难看的，上机锋论坛看了一下，似乎不少人也遇到这种问题！于是，我决定，退货！ 第二天就退货了，这退货还真麻烦，邮费卖家说是AA的。那卖家说“一个坏点不影响正常使用吧”，我回复“严重影响正常使用，每次解锁屏幕超级难看，屏幕质量无法忍受”，卖家只好给同意换货了。卖家收到货后，给我打电话已经发货，第二天我就能收到新手机了。结果，第二天我被卖家忽悠了，因为他根本没有发货，理由是，暂时缺货，叫我多等一天。我耐心还是挺好的，也不急，有时间去网上看一下Android开发的一些资料，给系统配置好了开发环境。 今天终于收到货了，经检查，包装比上次多了一个外面的套（上次那个估计被人用过了的）。 拿出机子，比之前那台新很多！图片是我晚上拍的，上图用ME860拍，感觉太近了，对焦不准。下图是用相机拍的，左边开了闪光灯，可以看出我拍的时候手抖了！ 现在这台机用起来很完美。下面是卖家给我第一台机和第二台机的信息。在MOTO官网上核实过是正确的。 第一台： IMEI : 359488040065504 生产日期 : 2011年12月01日 MSN : L64AQY23XZ 交货日期 : 2011年12月10日 S/N : KAUG0081AA 保修截止 : 2012年12月09日 客户型号 : HG3379AE7U2 保修状态 : 在保修期内 维修次数 : 0 次 维修时间 : 无维修记录 生产地 : 印度 销售地 : 阿联酋 第一台退货后，拿到的第二台： IMEI : 358552040617498 生产日期 : 2011年12月01日 MSN [...]]]></description>
			<content:encoded><![CDATA[<p>上学期跟<a href="http://www.afailure.tk" target="_blank">雄哥</a>商量好一起编写一个手机玩的3D游戏。我觉得他的Idea非常好，于是就决定跟他一起干了！一开始定位在Apple的IOS上，为了给iPhone写程序，还真不容易，雄哥弄来一台苹果笔记本，还需要向苹果公司交开发费用才能够编写程序。所以最终还是放弃了IOS平台，改为Android平台了。雄哥效率还是挺高的，一下子把Android开发平台也搭建好了，开始测试Bullet引擎，制作了Tutorial方便我快速配置开发环境。</p>
<p>不幸的是，上年11月左右我的Acer Liquid手机在广州的公交车上被偷了。此后我借用了荣哥的旧手机，仅有简单的电话和短信功能，一直用到现在。Android的开发热情也没了。春节后，凑合着，在淘宝网购了一台Moto ME865，卖家是“我的名字叫什么_tb”。说到这网购，还真不容易！之前给别人网购过两次水货手机，都用不到一年就出问题了。这次购机特别谨慎！<br />
<span id="more-4806"></span><br />
大前天收到了手机，做了全面的检查，还下载了屏幕检测软件来给手机体检。这一测，还真被我发现了问题！手机有一坏点。在黑屏的时候，看到中央附近有一个蓝色的亮点。只要那块地方不是全黑，就看不到这个坏点。同时，在解除屏幕锁定或者通话时候从耳边拿开，亮屏的瞬间，闪出一道白色的竖线。非常难看的，上机锋论坛看了一下，似乎不少人也遇到这种问题！于是，我决定，退货！</p>
<p>第二天就退货了，这退货还真麻烦，邮费卖家说是AA的。那卖家说“一个坏点不影响正常使用吧”，我回复“严重影响正常使用，每次解锁屏幕超级难看，屏幕质量无法忍受”，卖家只好给同意换货了。卖家收到货后，给我打电话已经发货，第二天我就能收到新手机了。结果，第二天我被卖家忽悠了，因为他根本没有发货，理由是，暂时缺货，叫我多等一天。我耐心还是挺好的，也不急，有时间去网上看一下Android开发的一些资料，给系统配置好了开发环境。</p>
<p>今天终于收到货了，经检查，包装比上次多了一个外面的套（上次那个估计被人用过了的）。</p>
<p><a href="http://xiaoxia.org/upfiles/2012/02/phonebox.jpg"><img src="http://xiaoxia.org/upfiles/2012/02/phonebox.jpg" alt="" title="phonebox" width="548" height="304" class="alignnone size-full wp-image-4807" /></a></p>
<p>拿出机子，比之前那台新很多！图片是我晚上拍的，上图用ME860拍，感觉太近了，对焦不准。下图是用相机拍的，左边开了闪光灯，可以看出我拍的时候手抖了！</p>
<p><a href="http://xiaoxia.org/upfiles/2012/02/IMG_3200.jpg"><img src="http://xiaoxia.org/upfiles/2012/02/IMG_3200-700x445.jpg" alt="" title="IMG_3200" width="700" height="445" class="alignnone size-medium wp-image-4808" /></a></p>
<p>现在这台机用起来很完美。下面是卖家给我第一台机和第二台机的信息。在MOTO官网上核实过是正确的。</p>
<p>第一台：</p>
<blockquote>
<pre>
IMEI : 	359488040065504	生产日期 : 	2011年12月01日
MSN : 	L64AQY23XZ	交货日期 : 	2011年12月10日
S/N : 	KAUG0081AA	保修截止 : 	2012年12月09日
客户型号 : 	HG3379AE7U2	保修状态 : 	在保修期内
维修次数 : 	0 次	维修时间 : 	无维修记录
生产地 : 	印度	销售地 : 	阿联酋
</pre>
</blockquote>
<p>第一台退货后，拿到的第二台：</p>
<blockquote>
<pre>
IMEI : 	358552040617498	生产日期 : 	2011年12月01日
MSN : 	L646QY2W5W	交货日期 : 	2011年12月15日
S/N : 	CHWG3220AA	保修截止 : 	2013年06月14日
客户型号 : 	AP3021AE7H8	保修状态 : 	在保修期内
维修次数 : 	0 次	维修时间 : 	无维修记录
生产地 : 	天津	销售地 : 	泰国
</pre>
</blockquote>
<p>有没有发现，我第二次得到的机器，保修期比第一台多了半年？据论坛信息说，天津生产的机器比印度阿三做的质量要好！</p>
<p>我听说，这款机器的屏幕坏点问题比较频繁。买行货的朋友，会遇到有一个坏点，卖家不给换的问题。看来，我这次还是比较走运的。</p>
<p>所以，网络购机需谨慎！</p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoxia.org/2012/02/11/mymoto-me865/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>转换M8的XML通讯录为通用的VCard(VCF)格式</title>
		<link>http://xiaoxia.org/2012/02/08/m8-xml-contact-to-vcard-vcf/</link>
		<comments>http://xiaoxia.org/2012/02/08/m8-xml-contact-to-vcard-vcf/#comments</comments>
		<pubDate>Tue, 07 Feb 2012 18:54:58 +0000</pubDate>
		<dc:creator>Xiaoxia</dc:creator>
				<category><![CDATA[我的代码]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[vcard]]></category>
		<category><![CDATA[vcf]]></category>
		<category><![CDATA[xml]]></category>

		<guid isPermaLink="false">http://xiaoxia.org/?p=4801</guid>
		<description><![CDATA[自从2011年某月某日，在广州大学城的公交上丢了手机之后，就一直用者同学的旧手机。过年后，钱够了，就打算买手机！于是，把以前M8的通讯录转换为Android可以导入的VCF文件。 Android2.3使用的VCard版本为3.0，我导出了一个样例用于分析： BEGIN:VCARD VERSION:3.0 N:黄;小虾;;; FN:黄小虾 TEL;TYPE=CELL:13800138000 TEL;TYPE=WORK:02010086 EMAIL;TYPE=WORK:xiaoxia@xiaoxia.org ADR;TYPE=WORK;CHARSET=UTF-8:;;c12-151;广州;广东;510006; URL:xiaoxia.org PHOTO;ENCODING=B;TYPE=PNG:iVBOR(... 此处省略编码后的图片数据)CYII= X-QQ:10000 END:VCARD 从上面样例可以猜出VCard的大致格式。使用(\r\n)换行表示多个键，冒号(:)把键名和键值分开。分号(;)用来加入参数。 Name[;Attribute=AttributeValue]:Value[;MoreValue] 对上述样例中用到的键名的解释： BEGIN:VCARD 用于开始一个VCard记录。 VERSION:3.0 VCard的版本。 N:姓氏;名字;中间名;尊称（先生、女士、小姐）;不知道 FN 格式化的显示全名 TEL 电话号码 EMAIL 电子邮件 URL 网站 ADR 地址 PHOTO 个人照片 X-QQ QQ号码 X-TWITTER Twitter用户名 END:VCARD 一个VCard记录的结束。 再来研究一下M8里备份的通讯录的格式，下面是我抽取的一个示例。 文件名 mycontact.xml &#60;?xml version="1.0" encoding="UTF-8" standalone="yes" ?&#62; &#60;ContactTable&#62; &#60;Contact&#62; &#60;FileAs&#62;楚中天&#60;/FileAs&#62; &#60;FirstName&#62;中天&#60;/FirstName&#62; &#60;LastName&#62;楚&#60;/LastName&#62; &#60;Phone&#62; [...]]]></description>
			<content:encoded><![CDATA[<p>自从2011年某月某日，在广州大学城的公交上丢了手机之后，就一直用者同学的旧手机。过年后，钱够了，就打算买手机！于是，把以前M8的通讯录转换为Android可以导入的VCF文件。</p>
<p>Android2.3使用的VCard版本为3.0，我导出了一个样例用于分析：</p>
<blockquote><p>
BEGIN:VCARD<br />
VERSION:3.0<br />
N:黄;小虾;;;<br />
FN:黄小虾<br />
TEL;TYPE=CELL:13800138000<br />
TEL;TYPE=WORK:02010086<br />
EMAIL;TYPE=WORK:xiaoxia@xiaoxia.org<br />
ADR;TYPE=WORK;CHARSET=UTF-8:;;c12-151;广州;广东;510006;<br />
URL:xiaoxia.org<br />
PHOTO;ENCODING=B;TYPE=PNG:iVBOR(... 此处省略编码后的图片数据)CYII=<br />
X-QQ:10000<br />
END:VCARD
</p></blockquote>
<p><span id="more-4801"></span></p>
<p>从上面样例可以猜出VCard的大致格式。使用(\r\n)换行表示多个键，冒号(:)把键名和键值分开。分号(;)用来加入参数。</p>
<p>Name[;Attribute=AttributeValue]:Value[;MoreValue]</p>
<p>对上述样例中用到的键名的解释：</p>
<blockquote>
<pre>
BEGIN:VCARD    用于开始一个VCard记录。
VERSION:3.0    VCard的版本。
N:姓氏;名字;中间名;尊称（先生、女士、小姐）;不知道
FN             格式化的显示全名
TEL            电话号码
EMAIL          电子邮件
URL            网站
ADR            地址
PHOTO          个人照片
X-QQ           QQ号码
X-TWITTER      Twitter用户名
END:VCARD      一个VCard记录的结束。
</pre>
</blockquote>
<p>再来研究一下M8里备份的通讯录的格式，下面是我抽取的一个示例。</p>
<p>文件名 mycontact.xml</p>
<blockquote>
<pre>
&lt;?xml version="1.0" encoding="UTF-8" standalone="yes" ?&gt;
&lt;ContactTable&gt;
    &lt;Contact&gt;
        &lt;FileAs&gt;楚中天&lt;/FileAs&gt;
        &lt;FirstName&gt;中天&lt;/FirstName&gt;
        &lt;LastName&gt;楚&lt;/LastName&gt;
        &lt;Phone&gt;
            &lt;PhoneElement IsPrimary="1" Value="10086" Type="0" /&gt;
            &lt;PhoneElement IsPrimary="0" Value="13800138000" Type="0" /&gt;
        &lt;/Phone&gt;
    &lt;/Contact&gt;
    &lt;Contact&gt;
        &lt;FileAs&gt;林蛋大&lt;/FileAs&gt;
        &lt;FirstName&gt;蛋大&lt;/FirstName&gt;
        &lt;LastName&gt;林&lt;/LastName&gt;
        &lt;Phone&gt;
            &lt;PhoneElement IsPrimary="1" Value="10010" Type="0" /&gt;
        &lt;/Phone&gt;
    &lt;/Contact&gt;
&lt;/ContactTable&gt;
</pre>
</blockquote>
<p>看起来，好像XML的更加易读，但是体积也会比VCF的体积要大。</p>
<p>下面写一小段Python代码来做这个格式转换的工作（又是Python哦，要是有别的语言写起来更简洁，我会考虑学习一下）。</p>
<pre name="code" class="python">
#!/usr/bin/env python
# coding: utf-8

from xml.etree import ElementTree as ET

# 输出到的VCF文件
out = file("mycontact.vcf", "wb")

# 从M8的XML通讯录读取每条记录。
root = ET.parse(file("mycontact.xml", "r")).getroot()
for e in root.findall('Contact'):
    out.write('BEGIN:VCARD\r\nVERSION:3.0\r\n')
    out.write('N:%s;%s;;;\r\n' %
        (e.findtext('LastName', '').encode('utf8'), e.findtext('FirstName', '').encode('utf8')))
    out.write('FN:%s\r\n' % (e.findtext('FileAs', '').encode('utf8')))
    # 枚举号码条目
    for ee in e.find('Phone').findall('PhoneElement'):
        primary = int(ee.get('IsPrimary'))
        out.write('TEL;TYPE=CELL%s:%s\r\n' %
            ( (';TYPE=PREF' if primary else ''), ee.get('Value')))
    out.write('END:VCARD\r\n')

out.close()
</pre>
<p>一开始运行遇到异常，原因是e.findtext('LastName', '')得到的值本应是utf8的，结果被Python转换成了unicode，所以最后还是要自己再encode为utf8。因为VCard文件编码必须为utf8。</p>
<p>执行转换后，结果如下：</p>
<p>文件名 mycontact.vcf</p>
<blockquote><p>
BEGIN:VCARD<br />
VERSION:3.0<br />
N:楚;中天;;;<br />
FN:楚中天<br />
TEL;TYPE=CELL;TYPE=PREF:10086<br />
TEL;TYPE=CELL:13800138000<br />
END:VCARD<br />
BEGIN:VCARD<br />
VERSION:3.0<br />
N:林;蛋大;;;<br />
FN:林蛋大<br />
TEL;TYPE=CELL;TYPE=PREF:10010<br />
END:VCARD
</p></blockquote>
<p>怎么样？体积小巧了，但是没有XML的那么有层次，那么容易看懂。</p>
<p>如果有时间的话，我打算用HTML5做一个在线给VCF通讯录的记录添加自定义照片的。觉得有需要的，<strong>请支持我</strong> <img src='http://xiaoxia.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>参考文献：</p>
<p><a href="http://en.wikipedia.org/wiki/VCard">http://en.wikipedia.org/wiki/VCard</a></p>
<p><a href="http://tools.ietf.org/html/rfc2426">RFC2426</a></p>
<p><a href="http://tools.ietf.org/html/rfc6350">RFC6350</a></p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoxia.org/2012/02/08/m8-xml-contact-to-vcard-vcf/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>帮菜汤弄的《有字有谋》网页小游戏</title>
		<link>http://xiaoxia.org/2012/02/05/characters-and-skills-web-game-for-jessica/</link>
		<comments>http://xiaoxia.org/2012/02/05/characters-and-skills-web-game-for-jessica/#comments</comments>
		<pubDate>Sun, 05 Feb 2012 14:21:48 +0000</pubDate>
		<dc:creator>Xiaoxia</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[我的代码]]></category>
		<category><![CDATA[我的分享]]></category>
		<category><![CDATA[摄影艺术]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[game]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[jessica]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://xiaoxia.org/?p=4792</guid>
		<description><![CDATA[RT! 菜汤要写活动策划，里面有一个小节目，我出的鬼主意，也用到了这个小网页作为工具。 一开始不知道用啥工具写比较好，我会点VB，但是又不能在Linux写。.Net先排除了，发布了还要别人安装框架，更麻烦。Python嘛，对它的图形处理还不是很熟悉，写起来也不怎么容易。选什么GUI库也是一个难题。 所以，最后选择了一窍不通HTML5。用网页写比较容易跨平台，所以我在Linux下写好，别人也可以用她的Win7来用了。HTML5的入门也不难，花了一个小时看了一篇教程，就开始写了（因为我只用到Canvas）。 我的第一个HTML5写的网页哦。 展示地址： http://xiaoxia.org/upfiles/2012/02/index.html 源码下载： 直接保存页面即可。 玩法很简单，自己摸索吧！！！]]></description>
			<content:encoded><![CDATA[<p><a href="http://xiaoxia.org/upfiles/2012/02/IMG_3110_1.jpg"><img src="http://xiaoxia.org/upfiles/2012/02/IMG_3110_1-700x499.jpg" alt="" title="IMG_3110_1" width="500" height="356" class="alignnone size-medium wp-image-4794" /></a><br />
<span id="more-4792"></span><br />
RT! 菜汤要写活动策划，里面有一个小节目，我出的鬼主意，也用到了这个小网页作为工具。</p>
<p>一开始不知道用啥工具写比较好，我会点VB，但是又不能在Linux写。.Net先排除了，发布了还要别人安装框架，更麻烦。Python嘛，对它的图形处理还不是很熟悉，写起来也不怎么容易。选什么GUI库也是一个难题。</p>
<p>所以，最后选择了一窍不通HTML5。用网页写比较容易跨平台，所以我在Linux下写好，别人也可以用她的Win7来用了。HTML5的入门也不难，花了一个小时看了一篇教程，就开始写了（因为我只用到Canvas）。</p>
<p>我的第一个HTML5写的网页哦。</p>
<p>展示地址：</p>
<p><a href='http://xiaoxia.org/upfiles/2012/02/index.html'>http://xiaoxia.org/upfiles/2012/02/index.html</a></p>
<p>源码下载：</p>
<p>直接保存页面即可。</p>
<p>玩法很简单，自己摸索吧！！！</p>
<p><a href="http://xiaoxia.org/upfiles/2012/02/187.png"><img src="http://xiaoxia.org/upfiles/2012/02/187-700x578.png" alt="" title="187" width="700" height="578" class="alignnone size-medium wp-image-4798" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoxia.org/2012/02/05/characters-and-skills-web-game-for-jessica/feed/</wfw:commentRss>
		<slash:comments>31</slash:comments>
		</item>
		<item>
		<title>读ThinkPHP的Example有感</title>
		<link>http://xiaoxia.org/2012/01/30/codeignter-thinkphp-example-problems/</link>
		<comments>http://xiaoxia.org/2012/01/30/codeignter-thinkphp-example-problems/#comments</comments>
		<pubDate>Sun, 29 Jan 2012 17:34:16 +0000</pubDate>
		<dc:creator>Xiaoxia</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[我的分享]]></category>
		<category><![CDATA[codeigniter]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[thinkphp]]></category>

		<guid isPermaLink="false">http://xiaoxia.org/?p=4783</guid>
		<description><![CDATA[前几天下载了ThinkPHP的代码来看，给我的印象没有CodeIgniter(CI)的那么好。或许是因为我下载的是最新的RC版本吧！里面的Examples不全，打开几个提示404，因为比较关心数据库操作方面的代码。另外，跑了一下里面的Blog的Example，功能都挺完善的。不过花了十几分钟看了一下代码，就害怕了。不大喜欢的原因有下： 1、代码很大，这么一个小blog在用了框架之后，还需要敲那么多代码，开发时间也不短吧。 2、把HTML、CSS、Script写在controller里，让controller变得很臃肿，代码也有点混乱。为何不写到View里面去呢？ protected function ajaxUploadResult($info) { // Ajax方式附件上传提示信息设置 // 默认使用mootools opacity效果 //alert($info); $show = '&#60;script language="JavaScript" src="' . WEB_PUBLIC_PATH . '/Js/mootools.js"&#62;&#60;/script&#62;&#60;script language="JavaScript" type="text/javascript"&#62;' . "\n"; $show .= ' var parDoc = window.parent.document;'; $show .= ' var result = parDoc.getElementById("' . $info['uploadResult'] . '");'; if (isset($info['uploadFormId'])) { $show .= ' parDoc.getElementById("' . $info['uploadFormId'] . '").reset();'; [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://xiaoxia.org/upfiles/2012/01/IMG_25582_500x333.jpg"><img src="http://xiaoxia.org/upfiles/2012/01/IMG_25582_500x333.jpg" alt="" title="IMG_25582_500x333" width="500" height="333" class="alignnone size-full wp-image-4785" /></a></p>
<p>前几天下载了ThinkPHP的代码来看，给我的印象没有CodeIgniter(CI)的那么好。或许是因为我下载的是最新的RC版本吧！里面的Examples不全，打开几个提示404，因为比较关心数据库操作方面的代码。另外，跑了一下里面的Blog的Example，功能都挺完善的。不过花了十几分钟看了一下代码，就害怕了。不大喜欢的原因有下：<br />
<span id="more-4783"></span><br />
1、代码很大，这么一个小blog在用了框架之后，还需要敲那么多代码，开发时间也不短吧。</p>
<p>2、把HTML、CSS、Script写在controller里，让controller变得很臃肿，代码也有点混乱。为何不写到View里面去呢？</p>
<pre name="code" class="php">
    protected function ajaxUploadResult($info) {
        // Ajax方式附件上传提示信息设置
        // 默认使用mootools opacity效果
        //alert($info);
        $show = '&lt;script language="JavaScript" src="' . WEB_PUBLIC_PATH . '/Js/mootools.js"&gt;&lt;/script&gt;&lt;script language="JavaScript" type="text/javascript"&gt;' . "\n";
        $show .= ' var parDoc = window.parent.document;';
        $show .= ' var result = parDoc.getElementById("' . $info['uploadResult'] . '");';
        if (isset($info['uploadFormId'])) {
            $show .= ' parDoc.getElementById("' . $info['uploadFormId'] . '").reset();';
        }
        $show .= ' result.style.display = "block";';
        $show .= " var myFx = new Fx.Style(result, 'opacity',{duration:600}).custom(0.1,1);";
        if ($info['success']) {
            // 提示上传成功
            $show .= 'result.innerHTML = "&lt;div style=\"color:#3333FF\"&gt; 文件上传成功！&lt;/div&gt;";';
            // 如果定义了成功响应方法，执行客户端方法
            // 参数为上传的附件id，多个以逗号分割
            if (isset($info['uploadResponse'])) {
                $show .= 'window.parent.' . $info['uploadResponse'] . '("' . $info['uploadId'] . '","' . $info['savename'] . '");';
            }
        } else {
            // 上传失败
            // 提示上传失败
            $show .= 'result.innerHTML = "&lt;div style=\"color:#FF0000\"&gt; 上传失败：' . $info['message'] . '&lt;/div&gt;";';
        }
        $show .= "\n" . '&lt;/script&gt;';
        //$this-&gt;assign('_ajax_upload_',$show);
        header("Content-Type:text/html; charset=utf-8");

        exit($show);
        return;
    }
</pre>
<p>3、混合使用了Java、微软.Net、PHP三种代码风格（或者确切的说，从Java、微软.Net借鉴了函数、文件或变量的命名风格，但是没有PHP化）。不过在使用上比较一致，出问题几率也不会太大，只是我不是很习惯。</p>
<p>4、在controller代码里写Business Logic和数据库操作。我看Model里的代码基本都很短，看来是基本上博客的功能都写在controller里面去了。比较像Fat Controller的写法，但是数据库的操作写到Model里应该好些吧（按我理解的MVC来说）。Fat Model比Fat Controller多很多好处，便于代码重用。</p>
<p>5、这个要举例说明一下，在看代码的时候，发现一句注释。</p>
<pre name="code" class="php">
    if (!empty($id)) {
        $Blog = D("BlogView");
        $result = $Blog-&gt;where('Blog.id=' . $id)-&gt;find();  // 这里为什么用select()就读不出来
        if ($result) {
            $this-&gt;assign('vo', $result);
        } else {
            $this-&gt;redirect('index');
            return;
        }
    } else {
        $this-&gt;redirect('index');
    }
</pre>
<p>因为我对数据库操作方面比较关心，之前看过部分ThinkPHP的文档。拜托，写这代码的朋友，TP里select读出来的是记录集，find得到的是记录，你这么assign过去，当然读不出来啦。要把$result改成$result[0]才可以读出来的嘛。这样我感觉TP的Example编写者也对使用者太不负责任了。不过也没多大问题，只是一个RC版本。</p>
<p>6、混合有字符串式拼凑的SQL请求，有些我没读懂！！！可能需要时间深入探究。过多使用这类SQL，会有安全隐患吧（例如SQL注入）。</p>
<pre class="php" name="code">
    public function tag() {
        $Tag = M("Tag");
        if (!empty($_GET['name'])) {
            $name = trim($_REQUEST['name']);
            $list = $Tag-&gt;where("module='Blog' and name='$name'")-&gt;field('id,count')-&gt;find();
            $tagId = $list['id'];
            $count = $list['count'];
            import("@.ORG.Page");
            $listRows = 10;
            $fields = 'a.id,a.userId,a.categoryId,a.cTime,a.readCount,a.commentCount,a.title,c.title as category';
            $p = new Page($count, $listRows);
            $p-&gt;setConfig('header', '篇日志 ');
            $dao = D("Blog");
            $list = $dao-&gt;query("select " . $fields . " from " . C('DB_PREFIX') . 'blog as a,' . C('DB_PREFIX') . 'tagged as b, ' . C('DB_PREFIX') . 'category as c where b.tagId  in (' . $tagId . ') and a.categoryId= c.id and a.status=1  and a.id=b.recordId order by a.id desc limit ' . $p-&gt;firstRow . ',' . $p-&gt;listRows);
            if ($list) {
                $page = $p-&gt;show();
                $this-&gt;assign("page", $page);
                $this-&gt;assign('list', $list);
            }
            $this-&gt;assign('tag', $name);
            $this-&gt;assign("count", $count);
        } else {
            $list = $Tag-&gt;where("module='Blog'")-&gt;select();
            //dump($list);
            $this-&gt;assign('tags', $list);
        }
        $this-&gt;display();
    }
</pre>
<p>7、代码风格能体现一个程序员的水平，跟CI的Examples相比，还是有一定差距。注释写的比较随意。我有时写代码，注释也挺随意的。有时候为了尊重其他人，还是需要像写代码一样很大耐心地去写注释。</p>
<p>8、跟代码无关。附带的用户文档不是很人性化，在我本本上都看到字体很大，行距很大，一段简单功能的PHP代码就显示了两页。有些代码为了语法高亮使用了图片，不过图片的质量太低了，可能我玩多了单反。作成HTML其实也方便用户使用、查找的嘛，弄个PDF多了些麻烦。</p>
<p>以上观点仅针对ThinkPHP 3.0 RC1的Example而言。我没有看过核心代码，所以无权评论。同时也没有否定国内MVC框架实力的意思，只不过我觉得，TP的开发者应该在一些细节上多下些功夫，多灌注一些心血，让TP的手册和例子的质量有所提高！</p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoxia.org/2012/01/30/codeignter-thinkphp-example-problems/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>用CodeIgniter写了第一个企业网站</title>
		<link>http://xiaoxia.org/2012/01/29/use-codeigniter-for-my-first-framework-based-website/</link>
		<comments>http://xiaoxia.org/2012/01/29/use-codeigniter-for-my-first-framework-based-website/#comments</comments>
		<pubDate>Sat, 28 Jan 2012 20:15:21 +0000</pubDate>
		<dc:creator>Xiaoxia</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[我的代码]]></category>

		<guid isPermaLink="false">http://xiaoxia.org/?p=4779</guid>
		<description><![CDATA[某个晚上，发现使用PHP Web Framework来开发一些功能比较一般的商业网站效率挺高的。因此，有兴趣接触一下这方面的东西，免得我每次给自己的应用编写站点都Copy一大堆重复使用过的代码。搞Web的人太多了，同时涌现出来的Web框架也太多了，不知道选择哪一个好，怕选到一个不适合自己的，就浪费了不少时间了（虽说也能从中学到一些东西）。 上网搜索了一下，国外很多人推CakePHP，Zend Framework，Yii，Symfony，CodeIgniter等。在某论坛上看到有很多人推ThinkPHP（TP）的，好像人气挺高的。之前有个同学跟我推荐过ThinkPHP，我当时对基于别人的基础上开发站点还不怎么依赖，没多大兴趣。这次上官网看了一下，没找到在线的Example代码，所以没有理会。 后面看到有人推荐CodeIgniter（CI），听起来也挺不错的，于是上官网看了一个20分钟建博客的视频教程。从此就感觉到用它的框架的确比我之前自己写的那个框架方便多了！虽然要遵循一些规矩比较多，但是功能很强大，很全！点这里可以观看视频“Create a Blog in 20 Minutes"。 第二天在家里，就把CodeIgniter下载下来，为了学习一下，开始写一个新的小站来连连手。第一时间想到的是，给以前初二的时候帮亲戚写的一个家俱网站改版！需求比较简单，就是一个类似CMS的站点，能够支持对新闻、产品信息的CRUD基本功能就行了。 接触新的东西总是兴趣很大，劲头十足的。所以花了不到三天时间，实现了基本功能，就罢工了。目前仅作练习而已。在Coding的过程中，基本上可以脱离网络，因为下载的CodeIgniter压缩包里已经包含了完整的HTML的用户文档，每个功能都给出了具体示例，所以用起来得心应手。特别是数据库操作、分页、上传和图片处理方面，直接从例子里copy代码，稍微改动一下就很足够了。 三天Coding在controller、model上的代码行数大概为500行左右。包括一个200行左右代码的controller以及每个数据库表对应的model。 网站的版面如下： 可以访问此站点，测试各方面的功能。感觉CI写出来的东西，应该没有多少漏洞的吧～特别是这种小站 http://furshop.xiaoxia.org/]]></description>
			<content:encoded><![CDATA[<p>某个晚上，发现使用PHP Web Framework来开发一些功能比较一般的商业网站效率挺高的。因此，有兴趣接触一下这方面的东西，免得我每次给自己的应用编写站点都Copy一大堆重复使用过的代码。搞Web的人太多了，同时涌现出来的Web框架也太多了，不知道选择哪一个好，怕选到一个不适合自己的，就浪费了不少时间了（虽说也能从中学到一些东西）。</p>
<p>上网搜索了一下，国外很多人推CakePHP，Zend Framework，Yii，Symfony，CodeIgniter等。在某论坛上看到有很多人推ThinkPHP（TP）的，好像人气挺高的。之前有个同学跟我推荐过ThinkPHP，我当时对基于别人的基础上开发站点还不怎么依赖，没多大兴趣。这次上官网看了一下，没找到在线的Example代码，所以没有理会。</p>
<p>后面看到有人推荐CodeIgniter（CI），听起来也挺不错的，于是上官网看了一个20分钟建博客的视频教程。从此就感觉到用它的框架的确比我之前自己写的那个框架方便多了！虽然要遵循一些规矩比较多，但是功能很强大，很全！点<a href="http://codeigniter.com/tutorials/watch/blog/">这里</a>可以观看视频“Create a Blog in 20 Minutes"。<br />
<span id="more-4779"></span><br />
第二天在家里，就把CodeIgniter下载下来，为了学习一下，开始写一个新的小站来连连手。第一时间想到的是，给以前初二的时候帮亲戚写的一个<a href="www.gdwxjj.com">家俱网站</a>改版！需求比较简单，就是一个类似CMS的站点，能够支持对新闻、产品信息的CRUD基本功能就行了。</p>
<p>接触新的东西总是兴趣很大，劲头十足的。所以花了不到三天时间，实现了基本功能，就罢工了。目前仅作练习而已。在Coding的过程中，基本上可以脱离网络，因为下载的CodeIgniter压缩包里已经包含了完整的HTML的用户文档，每个功能都给出了具体示例，所以用起来得心应手。特别是数据库操作、分页、上传和图片处理方面，直接从例子里copy代码，稍微改动一下就很足够了。</p>
<p>三天Coding在controller、model上的代码行数大概为500行左右。包括一个200行左右代码的controller以及每个数据库表对应的model。</p>
<p>网站的版面如下：</p>
<p><a href="http://xiaoxia.org/upfiles/2012/01/Furniture-Shop.png"><img src="http://xiaoxia.org/upfiles/2012/01/Furniture-Shop-700x629.png" alt="" title="Furniture Shop" width="700" height="629" class="alignnone size-medium wp-image-4780" /></a></p>
<p>可以访问此站点，测试各方面的功能。感觉CI写出来的东西，应该没有多少漏洞的吧～特别是这种小站 <img src='http://xiaoxia.org/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> </p>
<p><a href="http://furshop.xiaoxia.org/">http://furshop.xiaoxia.org/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoxia.org/2012/01/29/use-codeigniter-for-my-first-framework-based-website/feed/</wfw:commentRss>
		<slash:comments>33</slash:comments>
		</item>
		<item>
		<title>2012</title>
		<link>http://xiaoxia.org/2012/01/23/2012/</link>
		<comments>http://xiaoxia.org/2012/01/23/2012/#comments</comments>
		<pubDate>Sun, 22 Jan 2012 16:52:22 +0000</pubDate>
		<dc:creator>Xiaoxia</dc:creator>
				<category><![CDATA[我的生活]]></category>

		<guid isPermaLink="false">http://xiaoxia.org/?p=4773</guid>
		<description><![CDATA[在这新的一年来临之际，我谨代表Panda Group全体成员祝大家新年快乐，事事顺心。 新的一年，新的开始，Panda Group也已经步入了第四个年头。感谢大家对我们的关注和支持，也希望大家和我们一起，记住过去一年来的美好时光，忘记过去的不快与烦恼，从新的起点出发，创造新的传说！ root@xiaoxia-pc:~/project/icmptun# cowsay 'Happy New Year!!!' ___________________ < Happy New Year!!! > ------------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ &#124;&#124;----w &#124; &#124;&#124; &#124;&#124;]]></description>
			<content:encoded><![CDATA[<p><a href="http://xiaoxia.org/upfiles/2012/01/4ffed6d7gw1dp9a6kkx5rj.jpg"><img src="http://xiaoxia.org/upfiles/2012/01/4ffed6d7gw1dp9a6kkx5rj.jpg" alt="" title="4ffed6d7gw1dp9a6kkx5rj" width="440" height="293" class="alignnone size-full wp-image-4774" /></a></p>
<p>在这新的一年来临之际，我谨代表Panda Group全体成员祝大家新年快乐，事事顺心。</p>
<p>新的一年，新的开始，Panda Group也已经步入了第四个年头。感谢大家对我们的关注和支持，也希望大家和我们一起，记住过去一年来的美好时光，忘记过去的不快与烦恼，从新的起点出发，创造新的传说！<br />
<span id="more-4773"></span></p>
<blockquote>
<pre>
root@xiaoxia-pc:~/project/icmptun# cowsay 'Happy New Year!!!'
 ___________________
< Happy New Year!!! >
 -------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
</pre>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://xiaoxia.org/2012/01/23/2012/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>手动创建ICMP Tunnel实现VPN上网（附Python实现代码）</title>
		<link>http://xiaoxia.org/2012/01/16/cmwap-vpn-over-icmp-tunnel-python-code-2/</link>
		<comments>http://xiaoxia.org/2012/01/16/cmwap-vpn-over-icmp-tunnel-python-code-2/#comments</comments>
		<pubDate>Sun, 15 Jan 2012 22:01:35 +0000</pubDate>
		<dc:creator>Xiaoxia</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[我的代码]]></category>
		<category><![CDATA[我的分享]]></category>
		<category><![CDATA[cmcc]]></category>
		<category><![CDATA[cmwap]]></category>
		<category><![CDATA[icmp tunnel]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[vpn]]></category>

		<guid isPermaLink="false">http://xiaoxia.org/?p=4759</guid>
		<description><![CDATA[其实这是一篇讲解利用中国移动CMWAP的一些特性来实现免费上网的博文，但是没有以这个为标题，因为 1、用的是2G的EDGE网络，跟2G手机上网一样，速度有限. 2、各地区的移动设备有差别，例如在广州，这种方法行不通。但是广州的移动Wifi是可以通过UDP建立VPN来免费使用的。 3、不想吸引太多人的注意。 我见过很多公共网络都对UDP和TCP有不少限制，以致于我们不能自由地访问互联网。为了摆脱这种束缚，很多人都为此付出了很多努力，例如各种代理软件，各种隧道，各种VPN等。本文介绍的是一种比较罕见的ICMP隧道方式建立VPN。 背景 一年前，因为在县城里没有网络使用，又不想晚上跑到外面上，所以经常使用笔记本连接手机的GPRS网络来上网。打开一些网页或者聊天工具之类的，网速的快慢并不是很重要。但是流量有限，当时开通了300MB的套餐也很快被消耗尽了。有一次，在Ubuntu下创建了一个cmwap网络，cmwap是需要设置代理IP为10.0.0.172才能上网的。但是奇怪的是，我竟然可以ping通我自己博客服务器的IP。所以我想cmwap对外网IP的访问只是限制了TCP和UDP类型的数据包进出而已。 为了验证我的想法是否正确，我在网上查找到了一些现成的Ping Tunnel工具，例如ptunnel。ptunnel至今还可以使用，但是问题很多， 1、ptunnel只支持TCP。 2、我使用了ptunnel之后，我ping不了自己的服务器了。显然ptunnel截获了所有ping包。（或者我用的版本太低？） 3、速度不稳定，可能是因为自己实现的可靠协议不是很完善？ 用cmwap连接后，我使用ptunnel从我博客上下载一个5MB大小的文件。然后用手机查询流量，发现没有少。 由此说明，移动的cmwap是不计算ping的流量的。同时可以大胆猜测，流量计费功能应该是在10.0.0.172的代理服务器上进行的。这就是为何访问移动的一些服务（例如飞信网站）不会收流量费的原因。 我在国内似乎还没有搜索到有文章关于这方面的介绍。同时我在广州用cmwap连接后，无法发送ping包，难道是这个漏洞修补了？有兴趣的朋友可以在自己的地区测试一下。先把连接方式更改为cmwap，然后连上后，ping一下8.8.8.8，看是否收到pong！！！如果能收到，恭喜！你可以利用此特性免费上网。（另外，移动的路由很多，每次连接后得到的IP都不一样，路由也不一定一样。多换几次IP，可以得到能够ping的路由。——2012年1月29日补充） 我发现这个问题后本来打算发博文的，后来不知道为何忘记了。只是偶尔跟身边一些朋友说了一下。 原理 回到主题上！ 前段时间使用UDP隧道建立了一个VPN，把我学校里的一个VPS跟美国的VPS接入了同一个网络。参考了这篇文章《SSH_VPN》，有兴趣的同学可以看一下如何使用系统工具手动建立一个VPN。 这次要做的是，让从笔记本输出的IP包，使用ICMP协议封装后，像ping包一样发送到我的服务器，然后我的服务器解除ICMP的封装，把得到的IP包写入本地路由。接着，把捕获到的发送给笔记本的IP包，使用ICMP封装，像pong包（ping的reply）一样发送到我的笔记本。到达笔记本之后，接触ICMP封装，把得到的IP包写入本地路由。这样就在两个机器之间建立了点对点网络了。在此基础上，使用ip，iptables命令设置一下规则，就建立了一个VPN。 模拟一个网页请求流程， 1、Firefox在笔记本发出一个请求。 2、内核使用默认路由发送这个请求的IP包。 3、因为默认路由的设备设置的是tun0，所以被tunnel程序捕获。 4、tunnel程序读取ip包后，用icmp封装，发送到远程vps。 5、icmp无障碍地通过cmwap网络，发送到远程vps上。 6、远程vps收到后，被服务器端的tunnel程序捕获（tunnel程序捕获所有的icmp数据包）。 7、tunnel程序读取icmp包，获取里面的ip包，写入到本地网络中。 8、因为通过iptables设置了nat，所以该ip包的源地址被改为vps ip后，发送到了所请求的服务器上。 9、一个IP包从请求的服务器上返回到vps，经过nat后，进入tunnel程序建立的网络，被tunnel程序捕获。 10、tunnel程序读取ip包后，用icmp封装，发送到笔记本。 11、icmp回应包无障碍地通过cmwap网络，发送到笔记本上。 12、笔记本接收到该icmp包，被笔记本上的tunnel程序捕获。 13、tunnel程序读取icmp包，获取里面的ip包，写入到本地网络中。 14、内核得到这个ip包，通知指定的应用程序响应。 15、Firefox收到了回应。 很详细吧！！！完整的工作流程就是这样。但关键需要解决的是封装ip包和解除封装。 步骤 需要解决以下一些问题： 1、如何捕获与发送icmp包 用socket的RAW模式即可。 2、如何不影响vps上正常的ping回应 给icmp里的code字段设置一个固定值，默认是0，这个值可以随便设置。例如86。这样我们只捕获与发送code值为86的icmp数据包。跟普通的ping区别开来，互不影响。 同时，避免vps的内核回应我们的icmp包。添加下面的iptables规则。使用到--icmp-type type/code选项。type的值中，8是ping请求，0是ping响应，所以只针对响应包屏蔽。但是为了让服务器端的tunnel程序的icmp能发出去，服务器端在发送的时候，可以把code+1，也就变为87，发送出去。 iptables -A OUTPUT -p icmp --icmp-type 0/86 -j DROP [...]]]></description>
			<content:encoded><![CDATA[<p>其实这是一篇讲解利用中国移动CMWAP的一些特性来实现免费上网的博文，但是没有以这个为标题，因为</p>
<p>1、用的是2G的EDGE网络，跟2G手机上网一样，速度有限.</p>
<p>2、各地区的移动设备有差别，例如在广州，这种方法行不通。但是广州的移动Wifi是可以通过UDP建立VPN来免费使用的。</p>
<p>3、不想吸引太多人的注意。</p>
<p>我见过很多公共网络都对UDP和TCP有不少限制，以致于我们不能自由地访问互联网。为了摆脱这种束缚，很多人都为此付出了很多努力，例如各种代理软件，各种隧道，各种VPN等。本文介绍的是一种比较罕见的ICMP隧道方式建立VPN。</p>
<p><strong>背景</strong></p>
<p>一年前，因为在县城里没有网络使用，又不想晚上跑到外面上，所以经常使用笔记本连接手机的GPRS网络来上网。打开一些网页或者聊天工具之类的，网速的快慢并不是很重要。但是流量有限，当时开通了300MB的套餐也很快被消耗尽了。有一次，在Ubuntu下创建了一个cmwap网络，cmwap是需要设置代理IP为10.0.0.172才能上网的。但是奇怪的是，我竟然可以ping通我自己博客服务器的IP。所以我想cmwap对外网IP的访问只是限制了TCP和UDP类型的数据包进出而已。<br />
<span id="more-4759"></span><br />
为了验证我的想法是否正确，我在网上查找到了一些现成的Ping Tunnel工具，例如<a href="http://www.cs.uit.no/~daniels/PingTunnel/">ptunnel</a>。ptunnel至今还可以使用，但是问题很多，</p>
<p>1、ptunnel只支持TCP。</p>
<p>2、我使用了ptunnel之后，我ping不了自己的服务器了。显然ptunnel截获了所有ping包。（或者我用的版本太低？）</p>
<p>3、速度不稳定，可能是因为自己实现的可靠协议不是很完善？</p>
<p>用cmwap连接后，我使用ptunnel从我博客上下载一个5MB大小的文件。然后用手机查询流量，发现没有少。<br />
由此说明，移动的cmwap是不计算ping的流量的。同时可以大胆猜测，流量计费功能应该是在10.0.0.172的代理服务器上进行的。这就是为何访问移动的一些服务（例如飞信网站）不会收流量费的原因。</p>
<p>我在国内似乎还没有搜索到有文章关于这方面的介绍。同时我在广州用cmwap连接后，无法发送ping包，难道是这个漏洞修补了？有兴趣的朋友可以在自己的地区测试一下。先把连接方式更改为cmwap，然后连上后，ping一下8.8.8.8，看是否收到pong！！！如果能收到，恭喜！你可以利用此特性免费上网。（另外，移动的路由很多，每次连接后得到的IP都不一样，路由也不一定一样。多换几次IP，可以得到能够ping的路由。——2012年1月29日补充）</p>
<p>我发现这个问题后本来打算发博文的，后来不知道为何忘记了。只是偶尔跟身边一些朋友说了一下。</p>
<p><strong>原理</strong></p>
<p>回到主题上！</p>
<p>前段时间使用UDP隧道建立了一个VPN，把我学校里的一个VPS跟美国的VPS接入了同一个网络。参考了这篇文章<a href="https://help.ubuntu.com/community/SSH_VPN">《SSH_VPN》</a>，有兴趣的同学可以看一下如何使用系统工具手动建立一个VPN。</p>
<p>这次要做的是，让从笔记本输出的IP包，使用ICMP协议封装后，像ping包一样发送到我的服务器，然后我的服务器解除ICMP的封装，把得到的IP包写入本地路由。接着，把捕获到的发送给笔记本的IP包，使用ICMP封装，像pong包（ping的reply）一样发送到我的笔记本。到达笔记本之后，接触ICMP封装，把得到的IP包写入本地路由。这样就在两个机器之间建立了点对点网络了。在此基础上，使用ip，iptables命令设置一下规则，就建立了一个VPN。</p>
<p>模拟一个网页请求流程，</p>
<p>1、Firefox在笔记本发出一个请求。<br />
2、内核使用默认路由发送这个请求的IP包。<br />
3、因为默认路由的设备设置的是tun0，所以被tunnel程序捕获。<br />
4、tunnel程序读取ip包后，用icmp封装，发送到远程vps。<br />
5、icmp无障碍地通过cmwap网络，发送到远程vps上。<br />
6、远程vps收到后，被服务器端的tunnel程序捕获（tunnel程序捕获所有的icmp数据包）。<br />
7、tunnel程序读取icmp包，获取里面的ip包，写入到本地网络中。<br />
8、因为通过iptables设置了nat，所以该ip包的源地址被改为vps ip后，发送到了所请求的服务器上。<br />
9、一个IP包从请求的服务器上返回到vps，经过nat后，进入tunnel程序建立的网络，被tunnel程序捕获。<br />
10、tunnel程序读取ip包后，用icmp封装，发送到笔记本。<br />
11、icmp回应包无障碍地通过cmwap网络，发送到笔记本上。<br />
12、笔记本接收到该icmp包，被笔记本上的tunnel程序捕获。<br />
13、tunnel程序读取icmp包，获取里面的ip包，写入到本地网络中。<br />
14、内核得到这个ip包，通知指定的应用程序响应。<br />
15、Firefox收到了回应。</p>
<p>很详细吧！！！完整的工作流程就是这样。但关键需要解决的是封装ip包和解除封装。</p>
<p><strong>步骤</strong></p>
<p>需要解决以下一些问题：</p>
<p><strong>1、如何捕获与发送icmp包</strong></p>
<p>用socket的RAW模式即可。</p>
<p><strong>2、如何不影响vps上正常的ping回应</strong></p>
<p>给icmp里的code字段设置一个固定值，默认是0，这个值可以随便设置。例如86。这样我们只捕获与发送code值为86的icmp数据包。跟普通的ping区别开来，互不影响。</p>
<p>同时，避免vps的内核回应我们的icmp包。添加下面的iptables规则。使用到--icmp-type type/code选项。type的值中，8是ping请求，0是ping响应，所以只针对响应包屏蔽。但是为了让服务器端的tunnel程序的icmp能发出去，服务器端在发送的时候，可以把code+1，也就变为87，发送出去。</p>
<blockquote><p>iptables -A OUTPUT -p icmp --icmp-type 0/86 -j DROP</p></blockquote>
<p>了解更多关于ICMP的选项，请参见RFC792.</p>
<p><strong>3、MTU问题</strong></p>
<p>因为IP包被封装到ICMP里之后，体积肯定会变大，如果超出网络的MTU，内核就会用两个IP包来装。导致第一个IP包装满了，第二个IP包可能只有几十个字节。十分浪费。为了避免这种现象，可以设置虚拟网卡的mtu为1000或更少。</p>
<blockquote><p>ip link set t0 mtu 1000</p></blockquote>
<p><strong>4、Python里处理ICMP</strong></p>
<p>我自己写了一段代码，checksum的算法参考自 <a href="http://code.activestate.com/recipes/409689-icmplib-library-for-creating-and-reading-icmp-pack/">http://code.activestate.com/recipes/409689-icmplib-library-for-creating-and-reading-icmp-pack/</a></p>
<p>icmp.py</p>
<pre name="code" class="python">
#!/usr/bin/env python
import socket
import binascii
import struct
import ctypes

BUFFER_SIZE = 8192

class IPPacket():
    def _checksum(self, data):
        if len(data) % 2:
            odd_byte = ord(data[-1])
            data = data[:-1]
        else:
            odd_byte = 0
        words = struct.unpack("!%sH" %(len(data)/2), data)
        total = 0
        for word in words:
            total += word
        else:
            total += odd_byte
        total = (total>>16) + (total &#038; 0xffff)
        total += total>>16
        return ctypes.c_ushort(~total).value

    def parse(self, buf, debug = True):
        self.ttl, self.proto, self.chksum = struct.unpack("!BBH", buf[8:12])
        self.src, self.dst = buf[12:16], buf[16:20]
        if debug:
            print "parse IP ttl=", self.ttl, "proto=", self.proto, "src=", socket.inet_ntoa(self.src), \
                "dst=", socket.inet_ntoa(self.dst)

class ICMPPacket(IPPacket):
    def parse(self, buf, debug = True):
        IPPacket.parse(self, buf, debug)
        self.type, self.code, self.chksum, self.id, self.seqno = struct.unpack("!BBHHH", buf[20:28])
        if debug:
            print "parse ICMP type=", self.type, "code=", self.code, "id=", self.id, "seqno=", self.seqno
        return buf[28:]

    def create(self, type_, code, id_, seqno, data):
        packfmt = "!BBHHH%ss" % (len(data))
        args = [type_, code, 0, id_, seqno, data]
        args[2] = IPPacket._checksum(self, struct.pack(packfmt, *args))
        return struct.pack(packfmt, *args)
</pre>
<p><strong>4、我写的Tunnel程序</strong></p>
<p>实现了以下功能：</p>
<p>1）支持多人同时使用这个VPN。每个客户端通过外网IP与ICMP里的ID的组合来决定。所以，即使多个使用者在同一个局域网下，也不会相互使用。参见代码中key的计算。</p>
<p>2）使用密码登录以限制他人访问。初次连接服务器要求密码才能使用该VPN。默认为10分钟收不到来自客户端的数据包就删除会话。这个密码登录做的有点简单，当然只要稍加修改，让服务器返回一个随机字符串，客户端用密码跟这个随机字符串一起hash一下，就很无敌了。</p>
<p>3）服务器端和客户端共用一个tunnel程序。通过参数来指定工作模式。</p>
<p>tunnel.py</p>
<pre name="code" class="python">
#!/usr/bin/env python

import os, sys
import hashlib
import getopt
import fcntl
import icmp
import time
import struct
import socket, select

SHARED_PASSWORD = hashlib.md5("password").digest()
TUNSETIFF = 0x400454ca
IFF_TUN   = 0x0001

MODE = 0
DEBUG = 0
PORT = 0
IFACE_IP = "10.0.0.1"
MTU = 1500
CODE = 86
TIMEOUT = 60*10 # seconds

class Tunnel():
  def create(self):
    self.tfd = os.open("/dev/net/tun", os.O_RDWR)
    ifs = fcntl.ioctl(self.tfd, TUNSETIFF, struct.pack("16sH", "t%d", IFF_TUN))
    self.tname = ifs[:16].strip("\x00")

  def close(self):
    os.close(self.tfd)

  def config(self, ip):
    os.system("ip link set %s up" % (self.tname))
    os.system("ip link set %s mtu 1000" % (self.tname))
    os.system("ip addr add %s dev %s" % (ip, self.tname))

  def run(self):
    self.icmpfd = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname("icmp"))

    self.clients = {}
    packet = icmp.ICMPPacket()
    self.client_seqno = 1

    while True:
      rset = select.select([self.icmpfd, self.tfd], [], [])[0]
      for r in rset:
        if r == self.tfd:
          if DEBUG: os.write(1, "&gt;")
          data = os.read(self.tfd, MTU)
          if MODE == 1: # Server
            for key in self.clients:
              buf = packet.create(0, CODE+1, self.clients[key]["id"], self.clients[key]["seqno"], data)
              self.clients[key]["seqno"] += 1
              self.icmpfd.sendto(buf, (self.clients[key]["ip"], 22))
            # Remove timeout clients
            curTime = time.time()
            for key in self.clients.keys():
              if curTime - self.clients[key]["aliveTime"] > TIMEOUT:
                print "Remove timeout client", self.clients[key]["ip"]
                del self.clients[key]
          else: # Client
            buf = packet.create(8, CODE, PORT, self.client_seqno, data)
            self.client_seqno += 1
            self.icmpfd.sendto(buf, (IP, 22))
        elif r == self.icmpfd:
          if DEBUG: os.write(1, "&lt;")
          buf = self.icmpfd.recv(icmp.BUFFER_SIZE)
          data = packet.parse(buf, DEBUG)
          ip = socket.inet_ntoa(packet.src)
          if packet.code in (CODE, CODE+1):
            if MODE == 1: # Server
              key = struct.pack("4sH", packet.src, packet.id)
              if key not in self.clients:
                # New client comes
                if data == SHARED_PASSWORD:
                  self.clients[key] = {"aliveTime": time.time(),
                            "ip": ip,
                            "id": packet.id,
                            "seqno": packet.seqno}
                  print "New Client from %s:%d" % (ip, packet.id)
                else:
                  print "Wrong password from %s:%d" % (ip, packet.id)
                  buf = packet.create(0, CODE+1, packet.id, packet.seqno, "PASSWORD"*10)
                  self.icmpfd.sendto(buf, (ip, 22))
              else:
                # Simply write the packet to local or forward them to other clients ???
                os.write(self.tfd, data)
                self.clients[key]["aliveTime"] = time.time()
            else: # Client
              if data.startswith("PASSWORD"):
                # Do login
                buf = packet.create(8, CODE, packet.id, self.client_seqno, SHARED_PASSWORD)
                self.client_seqno += 1
                self.icmpfd.sendto(buf, (ip, 22))
              else:
                os.write(self.tfd, data)

def usage(status = 0):
  print "Usage: icmptun [-s code|-c serverip,code,id] [-hd] [-l localip]"
  sys.exit(status)

if __name__=="__main__":
  opts = getopt.getopt(sys.argv[1:],"s:c:l:hd")
  for opt,optarg in opts[0]:
    if opt == "-h":
      usage()
    elif opt == "-d":
      DEBUG += 1
    elif opt == "-s":
      MODE = 1
      CODE = int(optarg)
    elif opt == "-c":
      MODE = 2
      IP,CODE,PORT = optarg.split(",")
      CODE = int(CODE)
      PORT = int(PORT)
    elif opt == "-l":
      IFACE_IP = optarg

  if MODE == 0 or CODE == 0:
    usage(1)

  tun = Tunnel()
  tun.create()
  print "Allocated interface %s" % (tun.tname)
  tun.config(IFACE_IP)
  try:
    tun.run()
  except KeyboardInterrupt:
    tun.close()
    sys.exit(0)
</pre>
<p>用法：</p>
<blockquote><p>root@244754:~/lab/icmptun# ./tunnel.py<br />
Usage: icmptun [-s code|-c serverip,code,id] [-hd] [-l localip]</p></blockquote>
<p><strong><br />
5、VPS服务器端部署</strong></p>
<p>把icmp.py和tunnel.py都copy到vps上去。注意要设置为可执行文件。然后用下面的命令来运行。</p>
<blockquote><p>./tunnel.py -s 86 -l 10.1.2.1/24<br />
Allocated interface t1</p></blockquote>
<p>tunnel.py会创建一个虚拟网卡（tun）。上述命令中，虚拟网卡的IP为10.1.2.1，子网掩码为255.255.255.0。</p>
<p>查看已经建立的网卡，我这里显示为t1. 因为t0已经被我用作udp隧道。</p>
<blockquote>
<pre>
root@244754:~/lab/icmptun# ip route show
184.22.224.0/24 dev venet0  proto kernel  scope link  src 184.22.224.212
10.1.1.0/24 dev t0  proto kernel  scope link  src 10.1.1.1
10.1.2.0/24 dev t1  proto kernel  scope link  src 10.1.2.1
default dev venet0  scope link </pre>
</blockquote>
<p><strong>6、笔记本上客户端部署</strong></p>
<p>以客户端模式启动tunnel.py，</p>
<blockquote><p>
root@xiaoxia-pc:~/project/icmptun# ./tunnel.py -c 184.22.224.212,86,2012 -l 10.1.2.2/24<br />
Allocated interface t0
</p></blockquote>
<p>-c的参数指定三项内容，用道号分隔，分别是远程服务器端的IP，发送ping时所使用的code，发送ping时所使用的id。code是区别普通的ping包，id是区别不同的客户端。</p>
<p>注意，如果在局域网环境下，经过网关后，这个id可能会变化，但不影响使用，因为回应包进入内网时，id会变回原值。</p>
<p>启动客户端后，在本地可以ping一下IP。</p>
<blockquote><pre>root@xiaoxia-pc:~/project/icmptun# ping 10.1.2.2
PING 10.1.2.2 (10.1.2.2) 56(84) bytes of data.
64 bytes from 10.1.2.2: icmp_req=1 ttl=64 time=0.065 ms
64 bytes from 10.1.2.2: icmp_req=2 ttl=64 time=0.065 ms
64 bytes from 10.1.2.2: icmp_req=3 ttl=64 time=0.059 ms</pre>
</blockquote>
<p>很快就响应了，本地直接返回。</p>
<p>此时ping一下在vps的虚拟网卡，正常情况下，应该能得到回应了。</p>
<blockquote><pre>root@xiaoxia-pc:~/project/icmptun# ping 10.1.2.1
PING 10.1.2.1 (10.1.2.1) 56(84) bytes of data.
64 bytes from 10.1.2.1: icmp_req=1 ttl=64 time=322 ms
64 bytes from 10.1.2.1: icmp_req=2 ttl=64 time=545 ms
64 bytes from 10.1.2.1: icmp_req=3 ttl=64 time=400 ms</pre>
</blockquote>
<p>到目前为止，已经在两台机器之间通过icmp建立了点对点隧道。</p>
<p><strong>7、构建VPN</strong></p>
<p>先在服务器端设置NAT。</p>
<blockquote><p>
iptables -t nat -A POSTROUTING -s 10.1.2.0/24 -j SNAT --to-source 184.22.224.212</p></blockquote>
<p>再在本地设置路由表，让默认网关为新创建的t0. 同时注意把vps的ip设置为例外。</p>
<blockquote><p>ip route add 184.22.224.212 via 10.64.64.64 dev ppp0<br />
ip route del default<br />
ip route add default dev t0</p></blockquote>
<p>我的路由表如下：</p>
<blockquote><pre>root@xiaoxia-pc:~/project/icmptun# ip route show
10.64.64.64 dev ppp0  proto kernel  scope link  src 10.134.75.35
184.22.224.212 via 10.64.64.64 dev ppp0
10.1.2.0/24 dev t0  proto kernel  scope link  src 10.1.2.2
169.254.0.0/16 dev ppp0  scope link  metric 1000
default dev t0  scope link </pre>
</blockquote>
<p>到此，已经可以通过t0访问网络了。</p>
<blockquote><p>root@xiaoxia-pc:~/project/icmptun# telnet www.google.com 80<br />
Trying 203.208.46.180...<br />
Connected to www.google.com.<br />
Escape character is '^]'.
</p></blockquote>
<p><strong>最后</strong></p>
<p>可以把启动客户端以及设置路由的命令，写进一个脚本文件里，这样只需要一个命令，就能使用VPN了！像在我这个地方，就可以用这个工具实现在CMWAP的网络上免费上网，没有流量的限制。</p>
<p>当然，ICMP Tunnel在很多场合下都可以使用，只要ICMP没有被封，就有办法通过ICMP来建立隧道和VPN来摆脱网络限制。</p>
<p>我使用中的抓包，</p>
<p><a href="http://xiaoxia.org/upfiles/2012/01/177.png"><img src="http://xiaoxia.org/upfiles/2012/01/177-700x393.png" alt="" title="177" width="700" height="393" class="alignnone size-medium wp-image-4761" /></a></p>
<p>Wireshark很好用，在我分析ICMP的过程中，帮助很大。当然在服务器上使用tcpdump -n icmp来查看ICMP是否工作也很有用。</p>
<p>184.22.224.213是我的VPS上IP之一。</p>
<p><a href="http://xiaoxia.org/upfiles/2012/01/176.png"><img border="1" src="http://xiaoxia.org/upfiles/2012/01/176.png" alt="" title="176" width="629" height="272" class="alignnone size-full wp-image-4760" /></a></p>
<p>现在已经夜深了，EDGE的速度还行吧。</p>
<blockquote><pre>root@xiaoxia-pc:~/project/icmptun# wget http://xiaoxia.org/upfiles/a.zip
--2012-01-16 04:22:54--  http://xiaoxia.org/upfiles/a.zip
正在解析主机 xiaoxia.org... 184.22.224.213
正在连接 xiaoxia.org|184.22.224.213|:80... 已连接。
已发出 HTTP 请求，正在等待回应... 200 OK
长度： 262194 (256K) [application/zip]
正在保存至: “a.zip.1”

100%[============================================>] 262,194     18.8K/s   花时 14s   

2012-01-16 04:23:09 (18.5 KB/s) - 已保存 “a.zip.1” [262194/262194])
</pre>
</blockquote>
<p>本文主要用于学习交流，欢迎与大家共同探讨！！！</p>
<p>附本文所用代码文件下载：</p>
<p><a href='http://xiaoxia.org/upfiles/2012/01/icmptun.tar.gz'>icmptun.tar</a></p>
<p><strong>参考文献</strong></p>
<p><a href="http://www.ietf.org/rfc/rfc792.txt">http://www.ietf.org/rfc/rfc792.txt</a></p>
<p><a href="http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers">http://en.wikipedia.org/wiki/List_of_IP_protocol_numbers</a></p>
<p><a href="http://en.wikipedia.org/wiki/IPv4">http://en.wikipedia.org/wiki/IPv4</a></p>
<p><a href="http://code.activestate.com/recipes/409689-icmplib-library-for-creating-and-reading-icmp-pack/">http://code.activestate.com/recipes/409689-icmplib-library-for-creating-and-reading-icmp-pack/</a></p>
<p><a href="http://www.cs.uit.no/~daniels/PingTunnel/">http://www.cs.uit.no/~daniels/PingTunnel/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://xiaoxia.org/2012/01/16/cmwap-vpn-over-icmp-tunnel-python-code-2/feed/</wfw:commentRss>
		<slash:comments>50</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 0.439 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2012-02-23 08:33:14 -->

