工作,学习,生活,这里将会有一些记录. 备用域名:http://meisw.wdlinux.cn 注册 | 登陆
浏览模式: 标准 | 列表全部文章

把物理系统搬入OpenVZ中

OpenVZ真的非常好用,配合上GUI的图形管理很方便,效率也相当好。使用时间长了,一直有个问题困扰着我,就是如何把一些部署在物理服务器上的系统(简称物理系统)搬入OpenVZ中呢?
     OpenVZ默认只提供了几个简单的centos、fedora的模板,虽然官方网站上也提供了大量precreated(预创建)的各种系统模板供使用,但毕竟这些template(模板)都是别人创建的。以前,我就曾写过:[原]创建VPS OS模板的文章,告诉大家如何创建自己的模板。但这还不能满足我的要求,试想想,如果物理系统上已经部署了大量的应用,重新在VPS里面部署、迁移数据,这工作量将是非常庞大的。OpenVZ本身似乎没有提供这样的迁移工具。
     经过查询相关资料,我终于实现了从物理系统到VPS的迁移工作。以下步骤,适用于部署在实际物理服务器上,或VMware、VirtualBox、Xen等虚拟环境中,而物理系统为红旗DC Server 5.0/Asianux 3.0,Centos/RedHat 4.0/5.0,Fedora 7.0/8.0/9.0等操作系统的环境。其余发行版也可参考类似的步骤进行,但因各发行版对配置文件的定义有所差异,需要根据各自的情况进行改动。Debian系列的系统,可以参考底下附录部分的文章处理。

一、准备工作
系统环境:

引用
server1:红旗 DC Server 5.0 192.168.228.99(被迁移系统)
server2:红旗 DC Server 5.0 + OpenVZ 2.6.9-023stab048.4   192.168.228.90(虚拟机HW)


这里,我希望把server1上的物理系统迁移到server2的其中一个VPS中。迁移前,确认:

引用
1、server1上的物理系统可正常运行;
2、server2提供的VPS环境可以支持原server1的操作系统运行,例如核心为2.6.9以上;
3、server1与server2的操作系统应为同一平台,即都为x86或x86_64;
4、server2上已经搭建好OpenVZ环境,最好也把Vtonf等GUI管理配置好;
5、确保server2上有足够的空间供vz使用,建议把/vz作为一个单独分区划分,方便控制磁盘配额等资源。


二、迁移数据
1、创建VPS环境
可以用vzctl create借助模板创建一个VPS环境,然后清空/vz/private/$veid目录。也可以手动创建:

# mkdir /vz/root/101 /vz/private/101
# cat /etc/vz/conf/ve-vtonf.512MB.conf-sample > /etc/vz/conf/101.conf


2、准备拷贝环境
迁移数据的方法有好多,tar、dd、rsync等都可以。我这里使用rsync,所以需要做到:

引用
1、server1和server2两边都已经安装rsync软件包;
2、server1上需提供网络和sshd远程访问;
3、若server1部署了比较多的应用,建议暂时全部停掉;我的做法是,把server1切换到init 1模式,然后启动网络和sshd服务;


3、迁移数据
从server1拷贝数据到server2上:

# rsync -arvpz --numeric-ids --exclude lost+found --exclude /dev --exclude /mnt --exclude /tmp --exclude /proc --exclude /sys --exclude /usr/src --exclude /boot -e "ssh -l root@192.168.228.99" root@192.168.228.99:/ /vz/private/101/


※ 注意,原物理系统上有些目录对于VPS来说是没用的,甚至是有害的,拷贝时请把他们排除
4、设置VPS参数
前面若使用OpenVZ提供的sample配置文件,则还需要对该VPS设置,在server2上执行:

# vzctl set 101 --ostemplate centos-4-i386-default --save
# vzctl set 101 --onboot yes --save
# vzctl set 101 --hostname 101.linuxfly.cn --save
# vzctl set 101 --ipadd 192.168.228.101 --save
# vzctl set 101 --numothersock 120 --save
# vzctl set 101 --nameserver 202.96.134.133 --nameserver 202.96.128.86 --save
# vzctl set 101 --diskspace 10000000:11000000 --save
# vzcpucheck
# vzctl set 101 --cpuunits 40000 --save


这部分的工作,实际是根据今后该VPS运行的情况而设置的,可以参考原来物理系统的配置进行。注意,若修改了网络等参数的话,今后启动VPS后,记得也要修改应用上的设定咯。

三、调整VPS环境
物理系统上的启动与VPS中启动有不少地方是有差异,或冲突的。这也是最重要改动的地方:
1、修改etc/inittab文件

# sed -i -e '/getty/d' /vz/private/101/etc/inittab


2、修改etc/mtab文件

# rm -f /vz/private/101/etc/mtab
# ln -s /proc/mounts /vz/private/101/etc/mtab


3、修改etc/fstab文件
仅保留挂载/dev/pts的哪行:

# cp /vz/private/101/etc/fstab /vz/private/101/etc/fstab.old
# grep devpts /vz/private/101/etc/fstab.old > /vz/private/101/etc/fstab
# rm -f /vz/private/101/etc/fstab.old


4、修改etc/rc.d/rc.sysinit文件
把运行/sbin/start_udev的一行删除或注释掉:

# sed -i -e '/udev/d' /vz/private/101/etc/rc.d/rc.sysinit


5、创建device设备符号

# cd 101
# mkdir dev
# mknod -m 666 dev/ptmx c 5 2
# mkdir dev/pts
# /sbin/MAKEDEV -d /vz/private/101/dev ttyp ptyp
# chmod 660 /vz/private/101/dev/ttyp*
# chmod 660 /vz/private/101/dev/ptyp*
# mknod -m 666 dev/null c 1 3
# mknod -m 666 dev/random c 1 8
# mknod -m 444 dev/urandom c 1 9


6、删除网卡配置文件
因VPS使用虚拟网卡,不再需要启动物理网卡了。你可以修改配置文件中的ONBOOT为no,或直接删掉:

# rm -f /vz/private/101/etc/sysconfig/network-scripts/ifcfg-eth0
# cat /dev/null > /vz/private/101/etc/sysconfig/network


7、创建缺省目录

# mkdir /vz/private/101/proc
# mkdir /vz/private/101/tmp
# chmod 1777 /vz/private/101/tmp


四、启动VPS
启动:

引用
# vzctl start 101
Starting container ...
Container is mounted
Adding IP address(es): 192.168.228.101
Setting CPU limit: 0
Setting CPU units: 1000
Configure meminfo: 32768
Set hostname: 101.linuxfly.cn
File resolv.conf was modified
Setting quota ugidlimit: 0
Container start in progress...
# vzctl enter 101
entered into CT 101


至此,迁移工作全部完成。
您可以进入VPS中看看应用运行是否正常。由于VPS与物理系统环境仍存在一点差异,可能会导致应用启动失败,这需要逐一分析日志解决。最常见的是,提供给VPS的资源不足问题。因物理系统可完全独立获得全部系统资源,而在VPS中,受HW控制资源的分配,这可通过在VPS中:

# cat /proc/user_beancounters


查看failcnt列的信息来判断。然后根据实际情况增加资源即可。

五、参考资料
How To Convert Physical Systems And Xen VMs Into OpenVZ Containers (Debian Etch)
Creating a CentOS 5.0 Template

pure-ftpd的参数设置

-0: 当存在同名文件时,旧版本的文件不被删除或者截断,而是采用临时文件。当整个文件上传完毕之后,才指向新的文件。这个开关选项跟虚拟配额不兼容。
-1: 在系统日志的输出中,记录每个会话( Session )的 PID 。
-4: 只监听 IPv4 的连接
-6: 只监听 IPv6 的连接
-a <gid>: 只有通过认证的用户才可以直接访问它的 home 目录。该选项对没有 shell 的系统用户特别有用。注意,这里的 <gid> 是组的数组,而非字母显示。并且, root 始终对整个文件系统有完整的访问权。
-A: 除了 root 之外,都可以使用 chroot()
-b: 忽略部分 RFC 的标准,以处理完全坏掉的客户端、穿越防火墙或者 NAT 盒
-B: 以标准的方式在后台启动服务器
-c <num>: 允许同时连接的客户端数列。默认是 50
-C <num>: 同一 IP 最大连接数
-d: 在系统日志中记录各种信息,口令不会记入日志中,推荐只有需要 debug 时使用。如果两个 -d ,相应信息也一并记入日志。
-D: 即使当客户端没有使用 -a 选项时,同样列出所有以 ”.” 开头的隐藏文件。不推荐使用。
-e: 只开放匿名用户访问
-E: 只开放认证用户使用,匿名访问被禁止
-f <facility>: 使用指定程序记录系统日志,默认时 ’ftp’ 。如果使用 ’-f none’ 则不记录日志。
-F <fortune file>: 在登陆的时候显示一个 fortune 信息,而不是固定的登陆信息。其中 <fortune file> 是符合 fortune 格式的文本文件,fortune信息之间用'%'号隔开。 这是需要在编译时加入 ’—with-cookie’ 选项。如果仅仅是一个简单的文本文件,则在每次登陆是显示相同的信息。
-g <pid file>: 改变默认 pid 文件的位置。默认是位于 /var/run/pure-ftpd.pid
-G: 不允许重命名
-H: 系统默认是要对 IP 地址进行解析,才记入日志文件中。使用这个选项,则可以避免这样做。从而避免带宽的浪费。
-i: 不论目录的权限设置,匿名用户始终不能进行上传
-I <timeout>: 发呆的时间,以分钟为单位。默认是 15 分钟
-j: 如果用户的 home 目录不存在,就自动创建
-k <percentage>: 当 FTP 服务器占用空间超过规定的百分比,则不能再上传文件了。不用加 ’%
-K: 允许用户 resume 和 upload 文件,但是不可以删除和重命名这些文件。空的文件夹也可以被删除。可以通过 ’-r’ 禁止该选项。
-l <authentication> 或者 -l <authentication>:<config file>: 添加一个新的规则
-L <max files>:<max depth>: 默认的, pure-ftpd 不会显示超过 2000 个文件或者深度大于 5 的路径
-m <cpu load>: 如果 CPU 负载超过指定值,则不运行匿名用户下载。但是上传依然允许
-M: 允许匿名用户创建目录
-n <max files>:<max size>: 如果服务器被编译成支持虚拟配额,则该选项可以约束所有的用户(新任组的用户出外)。其中,最大的文件大小以 M 为单位
-N:NAT 模式,强制 Active 。当 ftp 服务器位于 NAT 、伪装网关或者路由器后面时,如果无法正常访问,可以采用此选项。
-o: 将所有上传的文件写入 ’/var/run/pure-ftpd.upload.pipe’ ,使得 ’pure-uploadscipte’ 程序可以运行。
-O <format>:<log file>: 以指定格式将文件传输记入日志文件中。目前支持的格式包括: CLF 、 Stats 、 W3C 和 xferlog
-p <first port>:<last port>: 包括被动模式在内,服务器只会选择从开始到结束的端口进行监听。
-P <ip address or host name>: 对 PASV 、 EPSV 和 SPSV 命令的相应,强制以指定的 IP 地址或 hostname 相应。
-q <upload ratio>:<download ratio>: 为匿名用户指定上传和下载 ration
-Q <upload ratio>:<download ratio>: 为除了 root 组的用户外的所有人指定上传和下载 ratio 。 root 组的用户没有 ratio 限制
-r: 永远不覆盖已经存在的文件。这是,上传一个已经存在的文件时,会自动为其重命名,入: xyz 、 xyz.1 、 xyz.2 。如果编译时使用 ” make AUTORENAME_REVERSE_ORDER=1” ,则重命名的文件是 1.xyz 、 2.xyz
-R: 即使是非匿名用户(除了 root 之外),也不允许使用 chmod 命令
-s: 不允许匿名用户下载属主是 ftp 的文件(其它匿名用户上传的文件)。这样可以保证必须经过管理员的修改,这些文件才能被匿名用户下载。
-S  绑定到指定的地址和端口。例:
/usr/local/sbin/pure-ftpd -S 21
/usr/local/sbin/pure-ftpd -S 192.168.0.1
/usr/local/sbin/pure-ftpd -S 192.168.0.1,21
/usr/local/sbin/pure-ftpd -S mci.uestc.edu.cn,21
-t <bandwidth> 和 -T <bandwidth>: 带宽限制。 <bandwidth> 是以 k/s 为单位,同时可以指定上传和下载的带宽,支持 [<upload>]:[<download>] 语法
-u <uid>: 禁止 uid 小于 <uid> 的用户登陆。 -u 1 可以禁止 root 用户登陆, -u 100 可以禁止绝大多数系统虚拟用户登陆。
-U <umask for files>:<umask for dirs>: 改变默认的掩码,默认的是 133:022 。如果希望上传的文件只能被上传该文件的用户读,使用 ’-U 177:077’ 。如果希望上传的文件可以被执行,使用 022:022 (文件可以被其它用户读,但是不能写)。 077:077 文件只能被所有者执行或者读。 Pure-ftpd 支持 SITE CHMOD ,使得用户可以被改变自己文件的属性。
-V <ip address>: 只允许在指定地址的非匿名的 FTP 访问。这时,可以将公网 IP 路由到一个指定的内网 IP 地址,或者将新任的 IP 地址路由到指定的内网 IP 地址。
-v <name>: 支持苹果的 Bonjor ,只有当 Bonjor 选择在编译的时候支持是,苹果的 MacOS X 才能有此属性。
-w: 只对认证的用户支持 FXP 协议
-W: 支持 FXP 协议
-x: 默认时,非匿名用户可以读和写以 ’.’ 开头的隐藏文件,而匿名用户则不可以。当该选项指定时,用户只能下载这些文件,但是不能覆盖和创建,即使该用户时是该文件的属主。如果希望是用户可以访问特定的以 ’.’ 开头的文件,可以通过建立该文件的链接(非 ’.’ 开头的文件名)达到目的
-X: 用户除了不能写以 ’.’ 开头的文件,如果使用了该选项,用户不能读这样的文件,也不能进入这样的目录。(当以 ’-a’ 参数启动,受信的用户可以绕开 ’-x’ 和 ’-X’ 的限制)
-y <max user logins>:<max anonymous logins>: 如果编译时加入了 --with-peruserlimits 选项,该选项限制了同一个用户可以同时拥有的 session 数目。空值 ’0’ 意味着没有限制
-z: 允许匿名用户读以 ’.’ 开头的文件和目录
-Z: 避免用户犯简单的错误。当前,该选项可以避免用户错误的使用 chmod 命令,防止他们不能访问自己的文件或者目录。该选项在将来还会有更多的功能。所以,推荐 host 服务器打开该参数

Mysql的几个默认配置

mysql的安装目录里有support-files,里面有几份默认的配置 分别是 huge large medium 等,还有高压力的InnoDB的参考: [root@aslibra support-files]# grep -v ^# my-huge.cnf key_buffer = 384M table_cache = 512 sort_buffer_size = 2M read_buffer_size = 2M read_rnd_buffer_size = 8M myisam_sort_buffer_size = 64M thread_cache_size = 8 query_cache_size = 32M thread_concurrency = 8 [root@aslibra support-files]# grep -v ^# my-large.cnf key_buffer = 256M table_cache = 256 sort_buffer_size = 1M read_buffer_size = 1M read_rnd_buffer_size = 4M myisam_sort_buffer_size = 64M thread_cache_size = 8 query_cache_size= 16M thread_concurrency = 8 [root@aslibra support-files]# grep -v ^# my-medium.cnf key_buffer = 16M table_cache = 64 sort_buffer_size = 512K net_buffer_length = 8K read_buffer_size = 256K read_rnd_buffer_size = 512K myisam_sort_buffer_size = 8M [root@aslibra support-files]# cat my-innodb-heavy-4G.cnf|grep -v ^#|grep -v "^$" back_log = 50 max_connections = 100 max_connect_errors = 10 table_cache = 2048 max_allowed_packet = 16M binlog_cache_size = 1M max_heap_table_size = 64M sort_buffer_size = 8M join_buffer_size = 8M thread_cache_size = 8 thread_concurrency = 8 query_cache_size = 64M query_cache_limit = 2M ft_min_word_len = 4 default_table_type = MYISAM thread_stack = 192K transaction_isolation = REPEATABLE-READ tmp_table_size = 64M log-bin=mysql-bin log_slow_queries long_query_time = 2 log_long_format server-id = 1 key_buffer_size = 32M read_buffer_size = 2M read_rnd_buffer_size = 16M bulk_insert_buffer_size = 64M myisam_sort_buffer_size = 128M myisam_max_sort_file_size = 10G myisam_max_extra_sort_file_size = 10G myisam_repair_threads = 1 myisam_recover 一般情况下,自己根据机器的情况,复制一份到默认的配置文件的位置,替代默认配置 比如纯数据库的机器,有1-2G以上的内存,可以拷贝huge的那份,要不然默认配置效率不高,比如查询时容易用到磁盘做临时文件,这份可以提高内存的使用率。 对比一下几份配置的同一参数: key_buffer = 16M key_buffer = 256M key_buffer = 384M myisam_sort_buffer_size = 8M myisam_sort_buffer_size = 64M myisam_sort_buffer_size = 64M myisam_sort_buffer_size = 128M query_cache_size= 16M query_cache_size = 32M query_cache_size = 64M read_buffer_size = 256K read_buffer_size = 1M read_buffer_size = 2M read_buffer_size = 2M read_rnd_buffer_size = 512K read_rnd_buffer_size = 4M read_rnd_buffer_size = 8M read_rnd_buffer_size = 16M sort_buffer_size = 512K sort_buffer_size = 1M sort_buffer_size = 2M sort_buffer_size = 8M table_cache = 64 table_cache = 256 table_cache = 512 table_cache = 2048 没有细节的技术可以解析,细节信息还是得查资料,这里只是列举对比一下,也许可以提醒大家使用内存和修改配置来优化mysql

squid限速

编译加-enable-delay-pools选项

添加以下行:
    delay_pools 1     
   delay_class 1 1
   delay_access 1 allow all
   delay_parameters 1 50000/50000 # 限制网速在50K以内
    delay_initial_bucket_level 50

--------------------------------------

今天测试了一下squid使用delay_pools来对源网站的访问进行限速.非常好用,如下

使用squid建二个源网站

 

1
2
3
4
5
6
acl php-oa url_regex -i ^http://.*php-oa.com/.*
acl sudo-u url_regex -i ^http://.*sudo-u.com/.*
http_access allow mysite
http_access allow sudo-u
http_access deny all
icp_access allow all

下面开始正文,我们要用到squid中的delay_pools,delay_pools里可以定义多个容器(多个源定义多个),而这个容器就是我们要控制的带宽,当容器到达所设定的容量时,这个容器的所有者就无法超过我们所设定的带宽限制.

 

开始设置squid的delay_pools

 

01
02
03
04
05
06
07
08
09
10
11
delay_pools 2  #设置二个pools来对二个源进行控制
  
delay_class 1 2    #设置第一个pools中的地址为C类网段中的每个IP地址流量
delay_access 1 allow php-oa
delay_access 1 deny all
delay_parameters 1  64000/64000 64000/32000   #连接php-oa总速度64000,每个ip可以3200的速度
  
delay_class 2 1
delay_access 2 allow sudo-u
delay_access 2 deny all
delay_parameters 2 32000/16000 #客户端下载sudo-u.com这个网站的总速度为1600,但squid连接源网站速度为32000

 

class定义:

class类型1为单个IP地址流量
class类型2为C类网段中的每个IP地址流量
class类型3为B类网段中的每个C类网段中的每个IP地址流量

delay_parameters语法:

类型1只有一个总带宽流量实际也就是这个IP地址的流量
delay_parameters pool total
例:delay_parameters 1 64000/64000

类型2有两个带宽流量参数,第一个为整个C类型网段流量,第二个为每个IP流量
delay_parameters pool tatal per-host
例:delay_parameters 1 -1/-1 64000/64000

类型3有三个带宽流量参数,第一个为整个B类网总流量,第二个为每个B类网段中的C类网段总流量,第三个为了B类网段中每个C类网段中的每个IP流量
delay_parameters pool total network per-host
例:delay_parameters 1 -1/-1 -1/-1 64000/64000

注:  -1/-1表示流量无限制.每个delay_parameters的数值是由"回源站的速度/客户最大下载速度"组成
  另外,对HIT的文件没有作用

 

----------------------------

UserDefine
acl vip src 192.168.24.0/255.255.255.0
acl normal_users src 192.168.11.232 192.168.11.211 192.168.11.231
acl vip_sc200 src 192.168.23.211 192.168.23.112
acl banneduser src  192.168.9.100
acl vip_me src 192.168.9.30
acl test src 192.168.100.250
acl all src 0.0.0.0/0
 
# define download files extension
acl disable_extension urlpath_regex -i \.mp3$ \.avi$ \.rmvb$ \.rm$ \.ra$ \.ram$ \.mpe$ \.smi$ \.mpeg$ \.wmv$ \.wma$ \.3gp$ \.td$ \.t
 
# define disable site dstdomain files.
 
# define disable ip for domain file
 
#Port Define
http_port 88
cache_mgr 5111
visible_hostname ifc-proxy
 
 
#HttpAccess
http_access allow  vip
http_access allow vip_me
http_access deny disable_extension
 
#MaxConn Define
acl num_conn maxconn 5
acl num_conn2 maxconn 20
 
#HttpAccess MaxConn Define
http_access deny normal_users num_conn
http_access deny test num_conn
http_access deny vip_sc200 num_conn2
 
#SpeedControl
delay_pools 4
delay_class 1 3
delay_class 2 3
delay_class 3 2
delay_class 4 3
delay_access 1 allow vip_sc200
delay_access 2 allow normal_users
delay_access 3 allow test
delay_access 4 allow all
delay_parameters 1 128000/128000 -1/-1 32000/64000
delay_parameters 2 64000/64000 -1/-1 8000/64000
delay_parameters 3 64000/64000 8000/64000
delay_parameters 4 1/1 1/1 1/1
前面一长段可以不看,看最后的一段,下面是注释
delay_pools 4 //表示有4个限制池,或者说对4种不同的目标限制
delay_class 1 3
delay_class 2 3
delay_class 3 2
delay_class 4 3 //上面的4行分别声明了四个池的类型,池1,2,4用于B类网段控制,池3用于C类网段控制,另外如果声明类型为class n 1表示控制单个ip地址
delay_access 1 allow vip_sc200
delay_access 2 allow normal_users
delay_access 3 allow test
delay_access 4 allow all //这几行分别对一开始定义的四个用户组进行限速,分别应用1,2,3,4号限制池
delay_parameters 1 128000/128000 -1/-1 32000/64000
delay_parameters 2 64000/64000 -1/-1 8000/64000
delay_parameters 3 64000/64000 8000/64000
delay_parameters 4 1/1 1/1 1/1
这四行分别声明了1,2,3,4号限制池的速度限制大小,比如第一个,我们前面声明的是B类网段,所以他的格式大概就是
delay_parameters 限制池号 本池最大速度 网段最大速度 单个ip最大速度
至于限制C类段的就少个网段最大速度,格式为
delay_parameters 限制池号 本池最大速度 单个ip最大速度

可能说的不太清楚,如果看不懂的可以参考下面的内容
 

关于设定SQUID带宽限制和流量整形,刻利用squid.conf种的delay_pools字段来完成.

delay pools里的bucket就像是一个容器,而这个容器就是squid要控制带宽用的,当容器到达所设定的容量时,这个容器的所有者就无法超过我们所设定的带宽限制,所有的bucket则称之为unified bucket.

Class分为三种:
(1)Class 1:包含一个unified bucket,而这个bucket是给这个class里所定义的host使用.
(2)Class 2:包含一个unified bucket和255个buckets,每一个bucket分配给8bit网络的使用者(255 hosts)使用IPv4 class C).
(3)Class 3:包含255个buckets,每一个bucket分配给16bit网络的使用者(65535 hosts)使用(IPv4 class B).

(1)Class 1:contains a single unified bucket which is used for all requests from hosts subject to the pool
(2)Class 2:contains one unified bucket and 255 buckets, one for each host on an 8-bit network (IPv4 class C)
(3)Class 3:contains 255 buckets for the subnets in a 16-bit network, and individual buckets for every host on these networks (IPv4 class B)

推测:如果ACL只定义一个class C字段,要限制每个host的单一带宽,可以使用Class 2来做;但如果ACL有定义好几个class C字段,使用Class 3可再对各个class C字段做个别的总带宽限制
delay_parameters语法:

class 1 delay pool;
delay_parameters pool total
class 2 delay pool;
delay_parameters pool tatal per-host
class 3 delay pool;
delay_parameters pool total network per-host

每个delay_parameters的数值是由restore(byte/sec)/max(bytes)组成,restore是表示以bytes/sec的速度下载object到bucket里,而max则表示bucket的bytes值.
备注1:如果要设定为unilit speed的话,将数值设定为-1即可

备注2:SQUID FAQ中有提到,建议max至少要设为restore的两倍(It is recommended that the maximum is at least twice the restore value)

[设定文档格式说明]
acl all src 0.0.0.0/0.0.0.0
acl lan src 192.168.1.0/255.255.255.0 # 定义 ACL
delay_pools n # 总共有几个 delay_pools
delay_class n1 1 # 第 n1 个 delay_pool 的种类是 Class 1
delay_class n2 3 # 第 n2 个 delay_pool 的种类是 Class 3
delay_class n3 2 # 第 n3 个 delay_pool 的种类是 Class 2
delay_access n1 allow lan
delay_access n1 deny all # 定义 delay_pool n1 的 access rule
delay_parameters n1 64000/64000 # 定义 delay_pool n1 的速度限制,依 class 的不同有不同的定义方式 (请参照上面的说明)

[范例说明]
1. 限制限制带宽为 512 Kbps
acl all src 0.0.0.0/0.0.0.0 # might already be defined
delay_pools 1
delay_class 1 1
delay_access 1 allow all
delay_parameters 1 64000/64000 # 512 kbits == 64 kbytes per second

2. 限制限制单一的带宽为 128 Kbps
acl only128kusers src 192.168.1.0/255.255.192.0
acl all src 0.0.0.0/0.0.0.0
delay_pools 1
delay_class 1 3
delay_access 1 allow only128kusers
delay_access 1 deny all
delay_parameters 1 64000/64000 -1/-1 16000/64000

3. 对某些特定的网站设置不通的带宽限制 (自己尝试一下,如果有错误请自行修改)
acl lan_use src 192.168.1.0/255.255.255.0 # 设置 LAN 使用者的 ACL
acl kkbox dstdomain .kkbox.com.tw # 设置特定域名的 ACL
delay_pools 2 # 设置两个 delay_pools
delay_class 1 1 # 第一个是 Class 1 的,用來限制总带宽
delay_class 2 2 # 第二个是 Class 2 的,用来限制单一的带宽
delay_access 1 allow kkbox
delay_access 1 deny all
delay_access 2 allow lan_use
delay_access 2 deny all
delay_parameters 1 64000/64000 # 不限制指定域名的单一带宽,但对总带宽速作限制
delay_parameters 2 64000/64000 10000/50000 # 限制 LAN 的所有使用者单一带宽,并对总的带宽作以限制

pureftpd

各参数注释
User:用户名
status:状态 1为激活状态 0为非激活状态
Password :密码
Uid:用户系统ID号
GID:用户组ID号
ULBandwidth :上传最大带宽 单位 KB/S
DLBandwidth:下载最大带宽  单位 KB/S
comment  :注释
ipaccess :允许访问IP地址
QuotaSize :磁盘配额总大小 单位MB
QuotaFiles  :允许存放的文件数目个数 0为不限制

然后用这个帐号测试,/home/askwan1自动建立
新增加帐号可以通过phpmyadmin来添加。

相关连接和参考资料文档:
   PureFTPd: http://www.pureftpd.org
    MySQL: http://www.mysql.com
    phpMyAdmin: http://www.phpmyadmin.net

用BIND架设DDNS Server 提供DDNS服务

作者: abelyang <abelyang{at}twnic{dot}net{dot}tw>
version: 1.0
最后修正时间
: 2007/07/28 00:10
转载时请保持此一宣告 

1.前言
目前动态 DNS 两大主流,一个是 BIND (ISC),另一个就是套接 DB DNS PowerDNS ( mydns)
 
,两种方式各有好坏,主要是因为 BIND 会有一些复杂性,但效果非常好, PowerDNS 则是很简单
,
但相对的它不能承受大量查询,主要原因在于数据库上先天的限制. 本文主要为介绍 BIND 之动态
 
DNS
做法,而这个做法之最重要重点则在于nsupdate 这个指令及
IXFR (incremental zone transfer 
request),
是不同于传统的 AXFR (full zone transfer),IXFR 在做 Zone Transfer (DNS 的同步机制
)
 
,会以差异化的部份进行同步, AXFR 则是以整个 Zone 进行同步.DDNS 主要由 RFC 2136 构成
,
建议若您要对 DDNS 有一定深入的了解,可以阅读这篇 RFC 以了解更多重要的信息

(1034 1035 1995
2136 的基础)

本文适用于对 DNS 巳有一定了解的朋友,若是不甚清楚建议您可先参考 TWNIC 所做的讲义:
http://dns-learning.twnic.net.tw/DNS94/ 

如果你想对 nsupdate + key 的方法有更深入的了解可以参考
http://www.study-area.org/tips/tipsfr1.htm
或参考 isc bind 的文件有最详细的解说
http://www.isc.org/sw/bind/arm93/Bv9ARM.pdf
本文不讨论 view 的情形,若是 view 情形您必需从 view match-client 去更新或是使用不同
key,所以建议您多参考 isb bind 的文件,虽然辛苦些,但数据绝对是最官方最正确的


2.
必要的信息及知识
本文的范例以 Shell Script 做成,重点在于原理,采用什么工具或做法完全视您个人的能力.以下
就一些重点进行说明.

2.1 BIND 动态更新
基本上在 BIND8,BIND9 都是支持 nsupdate ,但这里面要注意的是 BIND8 8.3.X 后才支援 
IXFR,
BIND9 则都支持,所以若您的 Server 8.3.0 前的版本,那就不建议了,更何况这个以

前的版本多多少少都有许多安全性的问题.

2.1.1 nsupdate: 
bind
要开 allow-update 选项,让你的程序可以来执行更新指令,allow-update 选项可以是 IP
 
key,
而本文仅就 IP进行介绍,若用 Key 对有些朋友来说可能会变得稍复杂些了

# named.conf
#
其它略

zone "dyndns.twnic.tw" {
 type master;
 file "dyndns.twnic.tw";
 allow-update {127.0.0.1;};    #
开放 127.0.0.1 进行动态更新 
 allow-transfer { slave_ip;127.0.0.1;};  # slave
主机,可能一部或多部,若无请写
none
};


以上是开放让 127.0.0.1 进行动态更新,动态更新有其指令,详细您可看看 nsupdate man page 
(man nsupdate),
以下仅以最常用的进行说明:

#nsupdate
[root@eai1 dyndns]# nsupdate
> server 127.0.0.1
> zone dyndns.twnic.tw
> update delete user1.dyndns.twnic.tw A 211.72.210.249
> update add user1.dyndns.twnic.tw A 211.72.210.251
ttl 'A': not a valid number  #
这个例子是错误示范,加一笔记录要有 TTL

> update add user1.dyndns.twnic.tw 60 A 211.72.210.251
> show
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:      0
;; flags: ; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; UPDATE SECTION:
user1.dyndns.twnic.tw.  0       NONE    A       211.72.210.249
user1.dyndns.twnic.tw.  60      IN      A       211.72.210.251

> send
> quit


server 
指向某一台 NameServer 进行 update 操作
zone   
修改某个 zone file
update delete 
进行 update delete 动作,这个指令格式是

        update delete FQDN TYPE RDATA,
如果有多笔相同的 A
       
记录或不同的 MX 记录要删除某一笔需将 RDATA 补上
,
       
若没有 RDATA 则表示这个 FQDN 的这个 TYPE 都要删除

        (TYPE
即是 A,MX,PTR,SOA,NS..,RDATA 就是 TYPE
       
接的东西, A RDATA IP MX RDATA
       
先权 FQDN)
update add    
进行记录的增加,操作如同 delete, 但是一定要有

        TTL
,且必需明确写出 RDATA
show   
这只是操作后要显示进行了那些 update 指令

send   
这个代表要把整个 update 指令送给 server,操作 update
       
时数据不是马上送出的,所以 update 可以很多行,最后

        nsupdate
看到 send ,才会将整个所有 update 送出,
       
update 使用 port 53/udp 若送出的数据量
(DNS packet)
       
大于 512 bytes,则会 truncate 而改使用 53/tcp,这是您

       
需要注意的地方,而只要您有 send, SOA 记录的 serial
       
会自动加1,以期让 slave 来进行同步,所以过多的 send

       
能造成过多的 traffic

2.1.2 zone file 及日志文件 
如果您进行了 nsupdate 的操作,则原来 directory 所指的目录将会产生一些日志文件,这个日志文件即为
 
zone_name.jnl (directory
习惯上都设在 /var/named , 您自己必要注意 named 程序要有写入的权限
,
chroot
状况等)

# 显示 dyndns.twnic.tw zone file 内容
[root@eai1 named]# cat /var/named/dyndns.twnic.tw
$TTL 86400      ; 1 day
@         IN SOA  twnic.net.tw. snw.twnic.net.tw. (
                                2006073267 ; serial
                                7200       ; refresh (2 hours)
                                1800       ; retry (30 minutes)
                                2419000    ; expire (3 weeks 6 days 23 hours 56 minutes 40 seconds)
                                300        ; minimum (5 minutes)
                                )
                        NS      ns2.dyndns.twnic.tw.
                        NS      eai1.twnic.tw.
ns2                     A       203.73.24.204
;
如果别人在查询时,我们有这个记录则响应这个记录的 IP,若没有这个记录代表
;
这个网站没有上线,所以此时我们可以建立一笔 wildcard 记录,指向自己的说
;
明网站,以供 user 识别这个网站没有上线,而这笔 wildcard 的记录 TTL 时间
;
不能太长,以免别的 DNS Cache 了这资料
*                 0    A       211.72.210.251

# 目录里的东西
[root@eai1 named]# ls -la /var/named/
总计 128
drwxr-xr-x    2 named    named        4096  7
27 09:25 .
drwxr-xr-x   20 root     root         4096  3
13 16:50 ..
-rw-r--r--    1 named    named         451  7
27 09:25 dyndns.twnic.tw
-rw-r--r--    1 named    named      104177  7
27 10:01 dyndns.twnic.tw.jnl
-rw-r--r--    1 named    named         195  7
  4  2001 localhost.zone
-rw-r--r--    1 named    named        2851 10
17  2003 named.ca

我们可以看到这个 .jnl 的产生,这个 .jnl 档是不能随便删除,因为它等于是 dyndns.twnic.tw 的补
充数据,而这些补充资料在 DNS reload/restart , named 还会在把它读进来,所以动态更新后的数据,
并不会随着 dns 重启后而消失,如果你想让现在整个 zone 文件出现所有的记录,那可以 rndc stop 来停止
 
dns,
此时 named 会把 .jnl数据写入原来的 zone file,不过这种方式一般来说较不建议,因为我们的
zone
 file
只要存一个样版 (template),其它的东西都是临时性的. 而若你想知道现在整个 zone 的内容,在可

以做 zone transfer 的主机上(如上例为 slave_ip 127.0.0.1), dig 指令来执行 axfr:

[root@eai1 dyndns]# dig @127.0.0.1 dyndns.twnic.tw axfr

; <<>> DiG 9.3.0 <<>> @127.0.0.1 dyndns.twnic.tw axfr
;; global options:  printcmd
dyndns.twnic.tw.        86400   IN      SOA     twnic.net.tw. snw.twnic.net.tw. 2006073528 7200 1800 2419000 300
dyndns.twnic.tw.        86400   IN      NS      ns2.dyndns.twnic.tw.
dyndns.twnic.tw.        86400   IN      NS      eai1.twnic.tw.
*.dyndns.twnic.tw.      0      IN      A       211.72.210.251
ns2.dyndns.twnic.tw.    86400   IN      A       203.73.24.204
user1.dyndns.twnic.tw.  60      IN      A       211.72.210.248
user1.dyndns.twnic.tw.  60      IN      MX      10 user1.dyndns.twnic.tw.
#
以下略

...

2.设定 NameServer 仅进行差异化的同步 
Master/Slave
要进行 zone file 的同步, ddns server 若只有一部是可以不用考虑这些问题的,但是若有两

部以上的 DNS Server, 就需要考虑到同步的进行方式, zone file 的总数据量小,采用什么同步方式是无所
谓的,但若数据量多,或是经常处在变动状况,那差异化的同步就会显得很重要,因为它可以让所有的 DNS 在短时
间内全部同步完成,BIND 支持 IXFR ,其预设即是采用 IXFR, 若没有 IXFR (update) ,则采用 AXFR,所以不
需要对 IXFR 进行额外设定,但为使大家了解其参数,本处还是举例来进行说明,好让大家能够更了解


# master DNS's named.conf
key "rndc-key" {
        algorithm hmac-md5;
        secret "HpXtFRFdLaRPFjpZokIwusyezyyRNjxhcafCfmktWNyGkDFzHAXlpTZQtVLc";
};

controls {
        inet 127.0.0.1 port 953
                allow { 127.0.0.1; } keys { "rndc-key"; };
};

options {
        directory "/var/named";
        pid-file "/var/run/named/named.pid";
        allow-transfer { none; };
        provide-ixfr yes;   #
提供 slave 主机以 IXFR 同步
,default yes
        request-ixfr yes;   # slave
IXFR master 进行同步
,default yes
 recursion no;       #
不允许递归查询

};
zone "0.0.127.in-addr.arpa" {
        type master;
        file "named.local";
};
zone "." {
        type hint;
        file "named.ca";
};
zone "dyndns.twnic.tw" {
        type master;
        file "dyndns.twnic.tw";
        max-journal-size 500k;                      #
设定日志文件大小
        allow-transfer { 203.73.24.204;127.0.0.1;}; #
可做 ixfr/axfr 的来源 IP,必需写上 slave,
                                                    # 127.0.0.1
是方使我们自己查看 zone file 现况

        also-notify {203.73.24.205;211.72.210.251}; #
额外的同步主机,这可能是 hot site 备份主机
        allow-update { 127.0.0.1;};                 #
允许动态更新的来源
};


上述的东西相信只要对 BIND DNS 有一定了解的朋友应该都是没有问题的,较不常出现的项目我都加上了的批注以利
大家了解, slave 的设法都同于 master, 只有在 zone 的部份稍有不同:

# slave DNS's named.conf
#
其它设定皆同上,只有 zone ...稍有不同,同于一般的 slave zone,不需要再开
allow-update (default none)
zone "dyndns.twnic.tw" {
        type slave;
        masters {211.72.210.249;};
        file "dyndns.twnic.tw";
        allow-transfer { none;};
};


所以从上述我们可以知道 DDNS for Master/Slave 您只要对 master 进行 update,每次的 update 送出 (send) 
会使该zone 的序号加一,只要 zone 有更新 , dns 会送出 notify 讯息给所有的 NS 主机(NS 记录上所列的名称

服务器),及可能的 also-notify 对象,使 slave 主机知道要进行同步,同步时优先采用 IXFR, master 不支持 
IXFR
则改使用 AXFR,以达到即使更新,及时同步的效果


此外,有些做 DDNS 的公司可能对 IXFR 不了解,而是把所有的 zone type 都设成了 MASTER,然后对这些 NameServer
进行 nsupdate , 这种做法也是可以的,不过中间若漏了一步或那一台少做了一件事,那两边的资料就会不一致,导致

可能同一个名称会有不同的解析结果 (例如 gnway.net 做法)

--------------------------------------------------------------------------------
 abel
回复于:2006-07-28 14:52:21

3. DDNS 的前端及后台控制范例 
说是范例主要是让大家参考原理,并没有必要一定都用我的方式,只要前面讲的东西您可以了解,程控的部份

仅是末节,以下仅列出我所用的方式供大家参考

3.1 MYSQL table
主要由三个表构成,分别为 RR (Resource Record), RR_LOG (旧资料,建议您依状况适当保存),USER (user 认证)

CREATE TABLE RR (
  SN int(20) NOT NULL auto_increment,
  USERNAME varchar(64) NOT NULL default '',
  FQDN varchar(64) NOT NULL default '',
  TTL int(5) NOT NULL default '60',
  TYPE varchar(10) NOT NULL default '',
  RDATA varchar(64) NOT NULL default '',
  CREATE_TIME timestamp(14) NOT NULL,
  PRIMARY KEY  (SN),
  KEY USERNAME (USERNAME),
  KEY FQDN (FQDN),
  KEY TTL (TTL),
  KEY TYPE (TYPE),
  KEY CREATE_TIME (CREATE_TIME)
) TYPE=MyISAM;

--
-- Table structure for table 'RR_LOG'
--

CREATE TABLE RR_LOG (
  SN int(20) NOT NULL default '0',
  USERNAME varchar(64) NOT NULL default '',
  FQDN varchar(64) NOT NULL default '',
  TTL int(5) NOT NULL default '60',
  TYPE varchar(10) NOT NULL default '',
  RDATA varchar(64) NOT NULL default '',
  CREATE_TIME varchar(14) default NULL,
  PRIMARY KEY  (SN),
  KEY USERNAME (USERNAME),
  KEY FQDN (FQDN),
  KEY CREATE_TIME (CREATE_TIME)
) TYPE=MyISAM;

--
-- Table structure for table 'USER'
--

CREATE TABLE USER (
  SN int(20) NOT NULL auto_increment,
  USERNAME varchar(64) NOT NULL default '',
  PASSWD varchar(64) NOT NULL default '',
  EMAIL varchar(64) NOT NULL default '',
  MEMO varchar(255) NOT NULL default '',
  PRIMARY KEY  (SN),
  UNIQUE KEY USERNAME (USERNAME)
) TYPE=MyISAM;


3.2 dyndns.cfg
设定档 
这个设定文件主要为了给 CGI 程序及产生 nsupdate 的程序 (dyndns-cron.sh) 所使用,透过 eval 方式来执行
,
以取得共同的变量

# mysql host/db/user/password
DBHOST=localhost
DBNAME=dyndns
DBUSER=UserName
DBPASS=Your_Passwd
MYSQL="mysql $DBNAME -h $DBHOST -u $DBUSER -p$DBPASS"

# dyndns domain
DOMAIN=dyndns.twnic.tw

# Master IP
DYNDNS_MASTER=127.0.0.1

# nsupdate command file
CMD_FILE=/tmp/nsupdate.cmd

# update freqency
UPD_FREQ=15

# RR valid time (seconds),default 20 mins
RR_ALIVE=1200


3.3 dyndns.cgi CGI
程序 
这个 CGI 主要用于接收 USER 端来的信息,验证通过后即为把 USERNAME.DOMAIN 数据,A/MX 及对应 IP 存入

 Table RR
,此外这个 CGI shell script 做成,可以于多数人的环境执行 (chmod 755 及目录的 CGI
行权限 ExecCGI 莫忘)

#!/bin/sh
echo -ne "Content-Type: text/html\n\n"

if [ -n "$QUERY_STRING" ];then
#
取得 QUERY_STRING,以下这个作法是危险的,因为没有检查数据的正确性就
eval
#
我的用意只在于说明作法

        eval `echo "$QUERY_STRING" | sed "s/&/;/g"`
#
读取设定文件,这个路径您需要自行调整
        eval `cat /home/abelyang/dyndns/dyndns.cfg `
        sql0="select 1 from USER where USERNAME='$LOGIN' and PASSWD='$PASSWD'"
        res=`echo $sql0 | $MYSQL `
#
如果 USER 密码正确, ${#res} 应为2,不对则为 0
        if [ ${#res} -lt 1 ];then
                echo "Login Failure"
        else
#
取得 IP, 需判断有 Proxy 存在,但是不考虑 Proxy 后是 NAT 情形

                IP=${HTTP_X_FORWARDED_FOR:-$REMOTE_ADDR}
                FQDN="$LOGIN.$DOMAIN"
#
删除上一次的登入
                sql1="delete from RR where USERNAME='$LOGIN'"
#
预设的动态更新项目为 A/MX
                sql2="insert into RR(USERNAME,FQDN,TYPE,RDATA) values('$LOGIN','$FQDN','A','$IP')"
                sql3="insert into RR(USERNAME,FQDN,TYPE,RDATA) values('$LOGIN','$FQDN','MX','10 $FQDN')"
                echo $sql1 | $MYSQL
                echo $sql2 | $MYSQL
                echo $sql3 | $MYSQL
                echo $LOGIN login success @$IP
        fi
else
#
以下只是网页的部份,我没有做 DDNS 申请,这个部份我想只要懂网页的朋友应该都会才是

        cat <<EOF
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=big5" />
<link rel="stylesheet" href="./style1.css">
</head>
<body>
<BR><BR><center>
<form>
<h3>Abel Dyndns Demo </h3>
Login:<input type=text name=LOGIN ><BR>
Passwd:<input type=password name=PASSWD><BR>
<input type=submit values="Start DynDNS">
</form>
<BR></center>
</body>
</html>

EOF


fi

3.4 dyndns-cron.sh 定时产生 nsupdate 
这只程序主要进行读取 Table RR , 并产生 nsupdate 所需要的指令格式后执行 nsupdate, 程序预设

15秒执行一次(参数如上 dyndns.cfg 中的 UPD_FREQ 更新频率),您可以拿掉 while [ 1 ] 的循环,
改用 crontab 方式来跑,不过这样就较不容易控制一分钟以内的更新频率了,最后,这个程序会把
SOA 
的序号改成更新时间,这个时间是 UTC 的秒数(意即 1970/1/1 至今秒数, date +%s 可得), SOA

序号就可以知道最后的 update 时间,这个原理和 .com verisign 或是 dyndns 中的 dyndns.org/
noip.com
是相同的.

另外,根据 RR_VALID 参数, USER 的登录时间 小于 现在时间-RR_VALID (秒数),则该 Record 视同
离线,所以我们需要进行 delete 动作


#!/bin/sh

while [ 1 ]
do
#
您必需调整路径,放在 while loop 里是要让修改了设定档即可生效,若不要可放在 while 之外

        eval `cat /home/abelyang/dyndns/dyndns.cfg`
        cat <<EOF > $CMD_FILE
server $DYNDNS_MASTER
zone $DOMAIN
EOF

# 取得最后一次的更新时间, 多减一秒是为了预防程序的 delay
        last=`date -d "-$UPD_FREQ seconds -3 seconds" "+%Y%m%d%H%M%S"`
        now=`date "+%Y%m%d%H%M%S"`

# 取得这段时间内有上来更新的 USER,这个部份不检查 IP 不变动情形
#
主要因为 nsupdate 巳执行很快,而且 named 它自己会检查重复更新的东西
        echo "select FQDN,TTL,TYPE,RDATA from RR where CREATE_TIME between $last and $now" | $MYSQL| grep -v 'RDATA' | while read FQDN TTL TYPE RDATA RDATA2
        do

# 组出更新指令,主要为一个删除,一个增加
                echo "update delete $FQDN $TYPE $RDATA $RDATA2" >>$CMD_FILE
                echo "update add $FQDN $TTL $TYPE $RDATA $RDATA2" >>$CMD_FILE
        done

# 超过 RR_ALIVE (20分钟) 未有 login 数据则清除 DNS 记录
        last=`date -d "-$RR_ALIVE seconds" "+%Y%m%d%H%M%S"`
#
备份旧的数据,并清除过时数据 (看你自己要不要备份了)
#       echo "insert into RR_LOG select * from RR where CREATE_TIME<$last"|$MYSQL
#       echo "delete from RR where CREATE_TIME < $last" |$MYSQL

# 取得过期 (RR_ALIVE) 而未登录的列表进行删除动作,因为我们用了 wildcard (*),所以若别人连时
#
将会被指到 wildcard 所指的 IP ,而您可这个 Web Server 上做一些文章,例如 Offline 说明等
        echo "select USERNAME,FQDN,TYPE,RDATA from RR  where CREATE_TIME - $last < 0 order by USERNAME" | $MYSQL | grep -v 'USERNAME' | while read USERNAME FQDN TYPE RDATA RDATA2
        do
                echo "; delete $FQDN $TYPE $RDATA $RDATA2" >>$CMD_FILE
                echo "update delete $FQDN $TYPE $RDATA $RDATA2" >>$CMD_FILE
        done

# 自定更新 SOA, 主要是为了让序号字段为现在时间 (UTC)
        echo "update delete $DOMAIN SOA" >>$CMD_FILE
        echo "update add $DOMAIN 600 SOA ns1.dyndns.twnic.tw abelyang.eai1.twnic.tw $(date +%s) 900 60 604800 60" >>$CMD_FILE
        echo "send" >>$CMD_FILE
#
执行 nsupdate 指令

        nsupdate $CMD_FILE
#       echo "process ok"
        sleep $UPD_FREQ
done

3.5 登入及更新方法
所有的东西都准备好了后,我们就可以测试:

wget "http://eai1.twnic.tw/dyndns.cgi?LOGIN=abelyang&PASSWD=abelyang-dyndns" -O /tmp/dyndns-login-status 2>/dev/null


在最多等待15(我的默认值)的情况下,就可以更新到 DNS 中了

3.6 其它数据 
其它数据如 named.conf , dyndns.twnic.tw zone file 您都可以在前面的说明里找到,我于下面
link
放了一份所有的数据供大家参考,较不用费事 copy & paste ,但不保证下面 link 永远有效 (其中的
 
.tgz
即有所有档案的
tarball)
http://eai1.twnic.tw/example/


4. DDNS
再探讨 
如前言所言, DDNS 可以使用数据库来用 (意即我的范例中可以少掉 dyndns-cron.sh 那只),不过数据库

因其先天的状况,更据我的测试(PowerDNS), 5 万资 Record 的情况下,只能到达每秒 1000 次的查询,
而且此时尚不考虑同时有 update/delete/insert 等情形,主要因为受限于先天 DB select 速度所致

,
当然您可以透过微调或细部处理让这个数字变成1500 2000, 但都永不如 BIND 随便都可以透过每秒 
6000
次查询,当然用 DB 直接来做一定是可以且更简单的,不过安全性及抗压性 PowerDNS 是随时都会有

当掉的风险,至于用 BIND 倒是没有看过,主要是因为这种 Server 肯定是不递归(recursion no). 所以
著名的 DDNS 厂商都是用 BIND 而不用 DB 方式,因其抗压性不足而致风险过高.

此外,若我们看 dyndns.org/noip.com 的做法,可以知道他们也是用 BIND 来做,你可以查询其 SOA 的序
号即可以知道他每60秒更新一次,若是使用 DB 来做是没有必要顾虑序号问题的 (DB DB 同步方法, 
SOA
序号无关),此外您更可以查看 .com Verisign,他们的做法也是像 BIND 一样,而其以每15秒更新

频率在进行,所以若您使用 .com 的域名,变更DNS 大概只要15秒就可以同步到所有的 .com NameServer, 
而不是过去的2 (因为过去是 AXFR,现在是 IXFR),虽然Verigisn 仍不降低 NS 记录的 TTL (二天
),
但至少不会发生像过去最多会4 .com DNS 数据 Cache 才会过期的情况 (二天的更新频率+二天的

快取时间)


#
检查 .com NameServer (d.gtld-servers.net) SOA 信息来验证
[root@eai1 example]# for i in `seq 1 1000`;do dig +short @d.gtld-servers.net com soa;sleep 1;done
a.gtld-servers.net. nstld.verisign-grs.com. 1153990708 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990708 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990708 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990723 1800 900 604800 900 #
这里变更了, serial 即时间
a.gtld-servers.net. nstld.verisign-grs.com. 1153990723 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990723 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990723 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990723 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990723 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990723 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990723 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990723 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990723 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990723 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990723 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990738 1800 900 604800 900 #
变更序号
a.gtld-servers.net. nstld.verisign-grs.com. 1153990738 1800 900 604800 900
a.gtld-servers.net. nstld.verisign-grs.com. 1153990738 1800 900 604800 900

5. 结语 
我所写的 script 都是以简单的角度来出发,以供大家参考,至于用户及域名管理这个有待想要用的人自行

开发, shell script 有利于多数人阅读及了解原理,细节的东西唯有您自己做了后才更能体会.
对多数的公司来说使用 DDNS 是没有意义的,除了 Registy (.com/cn/tw/jp/hk...),或是以 DDNS 做为营

运的公司, DDNS其实是不难的,但是网络上较缺乏这方面的介绍文章,所以在此为大家介绍一下这些东西
的细节, 以利想要研究的朋友能初窥门径. 有任何意见都非常欢迎大家多多交流


:
此外,有些做 DDNS 的公司可能对 IXFR 不了解,而是把所有的 zone type 都设成了 MASTER,然后对这些
 
NameServer
进行 nsupdate , 这种做法也是可以的,不过中间若漏了一步或那一台少做了一件事,那两边

的数据就会不一致,导致可能同一个名称会有不同的解析结果 (例如 gnway.net 做法,我猜测),而像 dyndns.org
/noip.com
和花生壳(vicp.net,oray.net) 等的做法是一样的(也就是和本文所提的做法一样). 而有些

公司则使用 PowerDNS/Mydnl..等套接 DB DNS ,虽然可用,而且方式更简单,但是这些 DDNS 服务肯定
无法负荷大量的查询,因为每次的 dns 查询会在其内产生 N 次的 Select 指令,数据库处理 select
速度肯定是比不上 dns flooding 的速度

使用浏览器Cache和http状态码304实现的客户端缓存

Cache就是浏览器的缓存技术,大家肯定不陌生,浏览器在每次加载一个文件的时候,都要去自己的缓存文件夹里面去查找是否存在可用缓存,如果存在,则不再去服务器下载而直接使用本地内容,这是一个很好的节省服务器性能和流量的方式,在网站不做任何设置的情况下,浏览器会根据用户的设置来确定是否使用缓存,可见浏览器的“Internet选项”的“浏览历史纪录”的“设置”部分。
  通常来讲,Cache设置有两种方式:第一种是在HTML内容的head之中设置:

  <meta  http-equiv="Expires"  CONTENT="0">  //这一句设置文件的过期时间为0秒

  <meta  http-equiv="Cache-Control"  CONTENT="no-cache">  //这一句设置文件禁止被缓存

  第二种是通过HTTP Head来设置,例如在.NET(C#)之中:

  Response.Cache.SetExpires(time);//设置文件的过期时间为当前的时间。

  我个人比较喜欢通过HTTP HEAD这种方式来设置,因为我觉得这不属于HTML本身的内容,当然,如果是静态文件,就只能通过html head来设置了,在我的网站"地名信息系统"之中,我设置了每个HTML页面在7天后失效,因为该HTML是自动生成的。

  在上面的Cache介绍之中,提到了浏览器在存在缓存的时候不去服务器取相关的内容,可是仅仅这样设置,好像有时候这些浏览器还是去服务器请求,有些时候的请求很合理,例如用户点击“刷新”按钮的时候,有些时候我也不知道为什么,因此,我在系统之中进一步使用了HTTP状态码。

  HTTP状态码有很多用户不愿意去了解,其实这是很重要的内容,至于怎么个重要法,以及详细的使用说明请大家去参考相关的文档,我这里举几个例子:

  1.有的用户在网站页面不存在的时候显示了自己定义的页面,可是忘记使用404状态码,这样浏览器就不知道它下载的内容究竟是正常内容还是页面不存在的提示,可能用户能从页面内容上分辨出来,可是搜索引擎的机器人不会,因此就可能会被搜索引擎列入“无法检索”的黑名单。

  2.在页面转向的时候不使用301或者302的状态码,造成搜索引擎不知道叶面已经被转向。

  我专门要说的304是一个用处和Cache相同的东西,这个状态码的含义是“服务器端没有更新”,也就是说客户端的文件版本是最新的,他的用法是这样的:

  1.当用户首次请求该文件的时候,通过HTTP HEAD的Last-Modified字段将该文件的最后修改日期发送到客户端,让客户端知道该文件的版本,例如:

  Last-Modified: Tue, 08 Apr 2008 14:48:05 GMT

  2.在浏览器再次请求该文件的时候,会自动将该时间作为请求的HTTP HEAD的If-Modified-Since字段内容(有时候根据浏览器的不同,可能会用逗号隔开附加上文件的字节数大小),例如:

  If-Modified-Since: Tue, 08 Apr 2008 14:48:05 GMT

  3.服务端根据If-Modified-Since字段的内容(如果存在该字段)来判断客户端的文件是否已经过期,如果已经过期,则重新返回新的文件,如果没有,则只需要返回304状态码,就可结束输出,这样代表浏览器端的文件版本是最新的,不需要返回文件内容。

  要知道,服务器返回一个304的时间要比返回整个文件的时间要小得多,性能损耗和网络占用也小得多。

  Cache和304技术有一定的重复,我选择Cache和304技术一起使用是因为单用Cache好像浏览器有时候还是去取最新内容(尤其是用户点击刷新按钮的时候),而单独使用304则在浏览器向服务器询问的过程还是会占用一定的性能和时间。这两种技术结合起来使用是刚刚好的。

HTTP请求方法及响应码详解

HTTP是Web协议集中的重要协议,它是从客户机/服务器模型发展起来的。客户机/服务器是运行一对相互通信的程序,客户与服务器连接时,首先,向服务 器提出请求,服务器根据客户的请求,完成处理并给出响应。浏览器就是与Web服务器产生连接的客户端程序,它的端口为TCP的80端口,。浏览器与Web 服务器之间所遵循的协议就是HTTP。

HTTP的早期版本为HTTP/0.9,它适用于各种数据信息的简洁快速协议,但是其远不能满足日益发展各种应用的需要。 但HTTP/0.9作为HTTP协议具有典型的无状态性:每个事务都是独立进行处理的,当一个事务开始就在客户与服务器之间建立一个连接,当事务结束时就 释放这个连接。HTTP/0.9包含Simple-Request&Simple-Responsed的报文结构。但是客户无法使用内容协商,所 以服务器也无法返回实体的媒体类型。

1982年,Tim Berners-Lee提出了HTTP/1.0,在此后的不断丰富和发展中,HTTP/1.0成为最重要的面向事务的应用层协议。该协议对每一次请求/响 应,建立并拆除一次连接。其特点是简单、易于管理,所以它符合了大家的需要,得到了广泛的应用。其缺点是仍会发生下列问题:对用户请求响应慢、网络拥塞严 重、安全性等。

1997年形成的HTTP/1.1,也就是现在普遍使用的协议,在持续连接操作机制中实现流水方式,即客户端需要对同一服务器 发出多个请求时,其实现在多数的网页都是有多部分组成(比如多张图片),可用流水线方式加快速度,流水机制就是指连续发出多个请求并等到这些请求发送完 毕,再等待响应。这样就大大节省了单独请求对响应的等待时间,使我们得到更快速的浏览。

另外,HTTP/1.1服务器端处理请求时按照收到的顺序进行,这就保证了传输的正确性。当然,服务器端在发生连接中断时,会自动的重传请求,保证数据的完整性。

HTTP/1.1还提供了身份认证、状态管理和Cache缓存等机制。这里,我想特别提一下关于HTTP/1.1中的Cache缓存机制对 HTTP/1.0的不足之处的改进,它严格全面,既可以减少时间延迟、又节省了带宽。HTTP/1.1采用了内容协商机制,选择最合适的用户的内容表现形 式。

现在,很多地方都有用到的虚拟主机技术在HTTP/1.1中也可以实现。所谓的虚拟主机技术,就是同一主机地址实际对应多台主机。通俗的 讲,当你同时在一个网站申请两个主页时,用协议分析仪可以发现其实这两个主页对应的是同一个IP地址。这样用多台完全相同的机器形成WWW服务器就可以提 高处理的吞吐量。

传统的解决方案是改造域名服务器使其可以根据一定的算法将同一域名解释成不同的IP地址。分别对应虚拟主机的每台机器,其缺点是要求每台机器占用完全独立的IP地址,这与IP地址的缺乏是相矛盾的。
HTTP/1.1提供的解决方案在HTTP协议自身中加入了指定不同主机的功能,从而多台主机可以共享一个IP地址,既提高了性能又便于管理。

因为HTTP/1.1是Internet现行的标准协议,这里详细介绍其相关语法。

首先,HTTP/1.1格式可写为:
其中请求方法是请求一定的Web页面的程序或用于特定的URL。可选用下列几种:
GET: 请求指定的页面信息,并返回实体主体。
HEAD: 只请求页面的首部。
POST: 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体。
PUT: 从客户端向服务器传送的数据取代指定的文档的内容。
DELETE: 请求服务器删除指定的页面。
OPTIONS: 允许客户端查看服务器的性能。
TRACE: 请求服务器在响应中的实体主体部分返回所得到的内容。
PATCH: 实体中包含一个表,表中说明与该URI所表示的原内容的区别。
MOVE: 请求服务器将指定的页面移至另一个网络地址。
COPY: 请求服务器将指定的页面拷贝至另一个网络地址。
LINK: 请求服务器建立链接关系。
UNLINK: 断开链接关系。
WRAPPED: 允许客户端发送经过封装的请求。
Extension-mothed:在不改动协议的前提下,可增加另外的方法。
比如:
GET /index.html HTTP/1.1
Accept: text/plain /*纯ASCII码文本文件*/
Accept: text/html /*HTML文本文件*/
User-Agent:Mozilla/4.5(WinNT)
说明浏览器使用Get方法请求文档/index.html。浏览器则只允许接收纯ASCII码文本文件和HTML文本文件,其使用的引擎是Mozilla/4.5(Netscape)。

当服务器响应时,其状态行的信息为HTTP的版本号,状态码,及解释状态码的简单说明。现将5类状态码详细列出:
① 客户方错误
100  继续
101  交换协议
② 成功
200  OK
201  已创建
202  接收
203  非认证信息
204  无内容
205  重置内容
206  部分内容
③ 重定向
300  多路选择
301  永久转移
302  暂时转移
303  参见其它
304  未修改(Not Modified)
305  使用代理
④ 客户方错误
400  错误请求(Bad Request)
401  未认证
402  需要付费
403  禁止(Forbidden)
404  未找到(Not Found)
405  方法不允许
406  不接受
407  需要代理认证
408  请求超时
409  冲突
410  失败
411  需要长度
412  条件失败
413  请求实体太大
414  请求URI太长
415  不支持媒体类型
⑤ 服务器错误
500  服务器内部错误
501  未实现(Not Implemented)
502  网关失败
504  网关超时
505 HTTP版本不支持
比如:(在《TELNET……》一文中用telnet登陆80端口,相同的方法用在HTTP/1.1中,会发现没有显示,下面补充说明之)
telnet www.fudan.edu.cn 80
HEAD / HTTP/1.1
host:www.fudan.edu.cn /*本行为输入内容*/
HTTP/1.1 501 Method Not Implemented
Date: Web, 01 Nov 2000 07:12:29 GMT /*当前的日期/时间*/
Server: Apache/1.3.12 (Unix) /*Web服务器信息*/
Allow: GET, HEAD, OPTION, TRACE /*支持的方法类型*/
Connection: close
Connect-Type: Text/html; charset=iso-8859-1/*连接的媒体类型*/

<!DOCTYPE HTML PUBLIG "-//IETF//DTD HTML 2.0//EN">
<HTML><HEAD>
<TITLE>501 Method
Not Implemented</TITLE>
</HEAD><BODY>
<H1>Method Not Implemented</H1>
head to /inde
x.html not supported.<P>
Invalid method in request head / htp/1.1<P>
<HR>
<ADDRESS>
Apache/1.3.12 Server at www.fudan.edu.cn Port 80</ADDRESS>
</BODY></HTML>
关于实体头部的内容还可以有:
Last Modified :请求文档的最近修改时间。
Expires :请求文档的过期时间。
Connect-length:文档数据的长度。
WWW-authenricate:通知客户端需要的认证信息。
Connect-encoding :说明有无使用压缩技术。
Transfer-encoding :说明采用的编码变换类型。

随着Internet的发展,下一代的HTTP协议HTTP-ng已经在酝酿之中,它将会提供更好的安全性、更快的速度,其改进要点为:模块化强、网络效率高、安全性更好、结构更简单。

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1759249
 

http 是一个相对简单的协议,它定义了客户(通常通过浏览器)和www服务器之间的会话过程。现在我们来看看这个会话过程的简要说明:客户打开与服务器的套接,使用的端口通常是80。然后它服务器发送请求行,请求标题,最后是请求空行。客户的请求通常是请求文档,它可以是文本文件,图片或者是程序等。服务器接受这个请求,然后查找请求的数据,最后根据查找的结果做出响应。如果上面的过程是一个cgi程序的话,那么服务器将会执行这个程序,并将程序执行的结果输给客户端。所以不明白cgi程序的朋友,可以这样理解cgi程序,它可以用很多的语言来写,只要它能完成一个任务:分析客户请求行中的数据,然后代替服务器做出响应!我们今天要讨论的就是从客户端的角度来理解这个问题,首先来看一个标准的客户请求格式:
请求方法 文挡地址 http/版本
请求标题:数据1
…………….
请求标题:数据N
空白行
在上面的格式中,第一行是必须的,它指明请求的文挡,又称请求头。下面的是请求标题可以多个。最后的空白行表示终止。这里还有一个问题,如果请求方法是POST的话那么空白行后面还可以发送附加数据。这里有一个非常重要的问题就是请求方法。无论对于我们cgi新手还是喜欢web安全的朋友,都是必须的知识
这是一个典型的请求头:
GET /bbs/login.asp HTTP/1.0
其中GET就是一个请求方法。/bbs/login.asp是文挡的地址即URI,它是URL的一部分。HTTP/1.0 是http 协议的版本号。这种方式的请求是建立在已经和服务器的套接建立的基础上的。完整的URL 可以是这样的方式:http://www.target.com/bbs/login.asp 。在http 1.0的协议里定义了三种请求方式:GET,POST,HEAD。http 1.1又补充了一些,如PUT,DELETE,OPTIONS和TRACE。现在也越来越多的服务器支持这些方法。下面我们来介绍一下常用的方法。
GET 这个是浏览器用语向服务器请求最常用的方法。我们在浏览器上发送的URL就是一个GET请求,当然我们也可以用程序,比如netcat,webget等来做。我们有的时候在看到一些黑客高手们在文章中提到的一些请求的例子,可能新手朋友们很难理解,比如:
http://www.target.com/bbsxp165/bbsxp/searc...password)>1)  
这就是一个相当复杂的GET请求,/searchok.asp?是请求这个asp文件,后面就是要传输给这个程序的数据,这个数据是根据网页的交互固定的。服务器接受这个请求后这些数据将被放入环境变量QUERY_STRING中。数据通常是一些数据名/数据值对。没对数据名/数据值之间用&来分开。例子的提交里forumid=()空格里的是一个sql语句,这是由于这里存在sql injection漏洞,当然不在我们讨论的范围。还有GET请求的数据不能超过一个特定的长度,比如2000字节。
POST 这个方法也是用来传送数据的,但是与GET不同的是,使用POST的时候,数据不是附在URI后面传递的,而是要做为独立的行来传递,此时还必须要发送一个Content_length标题,以标明数据长度,随后一个空白行,然后就是实际传送的数据。网页的表单通常是用POST来传送的。这里我们来举两个安全人士常用的提交方式,通常就是黑客们所谓的发现某个网页的或者某个cgi程序的漏洞然后构造一个特殊请求的时候用的:
1,脚本实现
…………
$socket = IO::Socket::INET->new(PeerAddr => $host, PeerPort => $port, Proto => "tcp", Type => SOCK_STREAM) or die “can’t connect to the host\n”;
print $socket "POST /$b HTTP/1.1";
print $socket "Host: $host";
print $socket "Content-Type: text/xml";
print $socket "Content-length: $length";
print $socket "$data";
$socket->recv($rbuf,500);
close($socket);
……….
以上是perl程序POST提交的主体部分,比如一个溢出程序,关键的地方就在于$b(URI)和$data 的构造上!
2, 使用nc来提交
建立一个hack.txt的文件输入下面的内容:
POST /cgi-bin/websendmail HTTP/1.0
Content-length: xxx (should be replaced with the actual length of the
string passed to the server, in this case xxx=90)
receiver=;mail+your_address\@somewhere.org  
然后用nc来请求
nc www.victim.com 80 <hack.txt
这样就完成了一个post的提交,当然还有很多别的方法可以实现这个提交,这里只是举两个我认为方便的办法。
HEAD 方法和GET的语法是一样的,如果用HEAD方法请求的话,则服务器返回的只是响应标题,而不会返回被请求的文挡,HEAD方法通用于一些搜索引擎中,当然我们的cgi扫描软件很多都是使用这个方式请求的。
以下方法属于http 1.1的标准,我们目前使用的还少,简单的介绍一下定义。
PUT 可以将客户提交的文挡保存在服务器的URI上
DELETE 用于请求服务器删除指定的URI
OPTIONS 可以请求对于指定URI可用的通用选项信息
TRACE请求服务器将附加的文挡无变更的返回,主要用于调试。
提到了请求,自然要讲一下,服务器的响应了,它的标准格式如下
HTTP/版本号 状态码 消息
响应标题:数据1
………….
响应标题:数据N
空白行
客户提交的文挡
看个例子吧:
nc www.victim.com 80
GET /index.html  
HTTP/1.1 400 Bad Request  
Date: Tue, 14 May 2002 07:03:02 GMT  
Server: Apache  
Connection: close  
Transfer-Encoding: chunked  
Content-Type: text/html; charset=iso-8859-1  

127
……………
响应预定义的状态码有很多,通常是服务器默认的设置,是三位数,以1,2,3,4,5开头的
1xx 主要用于调试和实验的目的  
2xx 主要表示请求成功  
3xx 表示请求的uri有多个选择或者已经移动位置了  
4xx 400表示请求行中有语法错误,404表示文挡不存在
5xx 表示服务器内部错误
有的时候可能大家总是忽略了这些东西的做用,事实上有的时候他们的作用还真的不小,比如我们手头没有任何工具,如何知道服务器上是否有ida,printf等映射呢,我们可以这么请求:http://www.victim.com/*.ida 如果出现500 服务器内部错误,我想问题就已经很清楚了。
既然都说这么多的废话了,那么再给大家来点关于URL编码,数据传输的小知识,和cookies的一些介绍吧。
URL编码和数据传输
下面是一个例子:
http://www1.baidu.com/baidu?word=netcat+ex...gb2312&cl=3&f=1
通常我们的浏览器在发送数据的时候要先经过编码,这是一种规范。GET 和POST 都是一样的,当然对于表单你可以用enctype字段来规定其他的编码方式。上面的例子语法,格式是用HEAD请求的。我们看到其中有一些特殊的符号如”%”,这是由于当数据中有非字母或数据的字符时,URL编码会将该字符转化为其ASCII码对应的数字,这样便以一个两位数字的16进制编码来代表字符。在 URL 编码中由百分号指示。 因此,%25 表示百分号本身(25是十六进制的,就是以 16 为基,代表百分号的 ASCII 码值),所有127(7fhex) 以上,和 33 (21hex) 以下的所有字符都会被转义,这包含空格符,空格的转义符为 %20. 加号被解释为空格符。
cookies 内容简介
cookies的作用,这里我就不说了,光是对它语法和格式本身就进介绍,我们先来看一个cookies的例子
aspsky
userhidden=2&password=469e80d32c0559f8&userid=1&userclass=%B9%DC%C0%ED%D4%B1&username=admin& usercookies=1
localhost/bbs/
0
3061727232
29562033
2222055456
29561914
*
参考这个例子我们来看看cookies 的 properties 主要包括:
key -->aspsky
value-->userhidden=2&password=469e80d32c0559f8&userid=1&userclass=%B9%DC%C0%ED%D4%B1&usernam e=admin&usercookies=1
damain-->localhost/bbs/
secure-->0 相当于no
expire-->3061727232 29562033 有效时间需要解码才能读出来
modified-->3061727232 29562033 修改时间,解码方式和expire一样
created in ->server
有的时候还有ip address
以上这些介绍只是方便大家对cookies的理解,具体的请参考一些专业的资料。有的时候经常遇到一些朋友问起怎么伪造cookies,我想写到这里已经非常清楚了。
说了这么多,总结一下吧
如果你是一个新手的话,如果你对http协议不是很清楚的话,建议你好好读读一些资料,毕竟这个是我们一天到晚泡网的基础哦,如果你是一个和我一样,喜欢研究网络编程安全的朋友,那么希望我们能一起交流,一起进步。本文只是简单的介绍了一下http方面的一些基础知识,并没有深入的讲述,在一定程 度上说只是给新手朋友们一个概念,但是对于大家理解在网络一些高手的精彩文章,还是相当有用的。

 

http://space.itpub.net/59745/viewspace-611292