星期三, 七月 04, 2007

prev: iptables DNAT/SNAT 的概念问题

先看下面的脚本:
#!/bin/bash
# [/usr/local/sbin/]ipt08_nat_DS.sh

/sbin/modprobe ipt_MASQUERADE
/sbin/modprobe iptable_nat
/sbin/modprobe ip_conntrack
/sbin/modprobe ip_conntrack_ftp
/sbin/modprobe ip_nat_ftp

iptables -F
iptables -t nat -F

# iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE # [5]
echo 1 > /proc/sys/net/ipv4/ip_forward

inet=220.168.98.221
ihttpd=192.168.0.2

iptables -t nat -A PREROUTING -d $inet -p tcp --dport 80 -j DNAT --to-destination $ihttpd # [1]
iptables -t nat -A POSTROUTING -s $ihttpd -p tcp --sport 80 -j SNAT --to-source $inet # [2]
iptables -t nat -A POSTROUTING -d $ihttpd -p tcp --dport 80 -j SNAT --to-source $inet # [3]
iptables -t nat -A OUTPUT -d $inet -p tcp --dport 80 -j DNAT --to-destination $ihttpd # [4]


iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m state --state NEW -i lo -j ACCEPT
iptables -A INPUT -m state --state NEW -i ! ppp0 -j ACCEPT

iptables -A INPUT -p tcp --dport 3313 -j ACCEPT # FOR: skype
iptables -A INPUT -j REJECT

iptables -A OUTPUT -j ACCEPT

iptables -A FORWARD -j ACCEPT
[1] 定义了外部的主机访问内部 http 服务器的目标地址转换规则,而 [2] 定义了该服务器的源地址转换规则。我之前一直认为有 [1] 就必须有 [2],两者必须匹配。但实际情况是,只需要有 [1],就可以让外部主机正常访问内部飞的服务器了!这是因为,当服务器响应了 iptables 所在的网关主机后,报文到达防火墙,iptables 会根据 DNAT 规则自动做 r-DNAT 操作(照着DNAT的步骤反过来做一遍),所以不再需要 SNAT 了。事实上,只要考虑一下 MASQUERADE 的工作,就可以明白──你只需要定义一个相当于 SNAT 的 转换,而不需要反过来做一次。

[2] 的作用,就是对 server 回复的报文改装源地址,从而可以对 Internet 隐藏内部网络的真实组成情况。注意,在防火墙上用 tcpdump 嗅探不会发现源地址进行了改变,因为从 server 到防火墙的报文只会经过 PREROUTING 和 INPUT 链,而 SNAT 必须到 POSTROUTING 链才生效。

设置了 [1] 后,在内部网络和防火墙主机上使用 $inet 访问服务器,会被拒绝!为什么呢?

先来看本地网络的情况:本地 $local 发送的到 $ihttpd(http server)的请求报文,因为使用 $inet,所以会经过防火墙,于是做 DNAT 操作,但没有做 SNAT 把源地址转换成防火墙的地址。http server 接到这个请求,发现源地址就在本地网络,于是把回复包直接发送到请求包的源地址 $local。

客户机 $local 接到回复报文,但它会感到“困惑”,因为它并没有向 $ihttpd 发送报文,它只好把这个包 DROP 掉,再去等待从防火墙主机 $inet 返回的“真正”的回复包,只是这个回复永远不会到达。

[3] 就是为了解决这个问题。当然,如果内部网络的主机直接使用 $ihttpd 访问 http server,就不会有上面的问题。

对于防火墙主机本身,直接用 $inet 访问 http server,也会被拒绝,因为本地产生的报文不会经过 nat 的 PREROUTING 链,而直接从 nat 的 OUTPUT 链出去,所以 [1] 定义的 DNAT 没有作用,所以,对本地报文,OUTPUT 链相当域 PREROUTING 链。

python distutils py_modules

package_dir = {'caxes' : 'lib'},
py_modules = ['lib.tree']
应该写成 lib.tree 而不是 lib/tree.py

星期一, 七月 02, 2007

python several Tree += examples

在设计 += 即 __iadd__ operator overloading 的时候,为保持一致性,另一个操作数只能是 Tree instance 或 Tree dict,那么即要找到不是它们时的边界条件,例如:
>>> tree = reload(tree)
>>> root = tree.Tree(0)
>>> print root

+++ Tree +++
() : 0,
--- Tree ---

>>> root += [1, 2, 3]
debug: AttributeError
Traceback (most recent call last):
File "", line 1, in ?
File "tree.py", line 266, in __iadd__
raise TreeTypeExc(_("Other operand should be a Tree instance or Tree dict"))
tree.TreeTypeExc:
这个 Exception 是由于 AttributeError 被捕捉产生的,因为调用了一个 dict 的 items(),那么定义一个有 items() 的类(多态性):
>>> class temp:
... def __init__(self):
... self.data = 1
... def items(self):
... return self.data
...
>>> t = temp()
>>> t.items()
1
>>> root += t
Traceback (most recent call last):
File "", line 1, in ?
File "tree.py", line 260, in __iadd__
for pathseq, node in other.items():
TypeError: iteration over non-sequence
以及
>>> root += {'a' : 1, 'b' : 2, 'c' : 3}
Traceback (most recent call last):
File "", line 1, in ?
File "tree.py", line 261, in __iadd__
self._1_node_set(pathseq, node())
TypeError: 'int' object is not callable
那么定义一个 callable 的类:
>>> class temp:
... def __init__(self, v):
... self.data = v
... def __call__(self):
... return self.data
...
>>> root += {'a' : temp(1), 'b' : temp(2), 'c' : temp(3)}
>>> print root

+++ Tree +++
() : 0,
('a',) : 1,
('b',) : 2,
('c',) : 3,
--- Tree ---

>>> root += {'' : temp(1), 'b' : temp(2), 'c' : temp(3)}
>>> print root

+++ Tree +++
() : 1,
('a',) : 1,
('b',) : 2,
('c',) : 3,
--- Tree ---

>>> root = tree.Tree(0)
>>> root += {'' : temp(1), 'b' : temp(2), 'c' : temp(3)}
>>> root += {1 : temp(1), 'b' : temp(2), 'c' : temp(3)}
Traceback (most recent call last):
File "", line 1, in ?
File "tree.py", line 261, in __iadd__
self._1_node_set(pathseq, node())
File "tree.py", line 333, in _1_node_set
next = pathseq[0]
TypeError: unsubscriptable object

>>> root += {tree.Tree(1): temp(1), 'b' : temp(2), 'c' : temp(3)}
Traceback (most recent call last):
File "", line 1, in ?
TypeError: unhashable instance

>>> root = tree.Tree(0)
>>> root += {'' : temp(1), 'b' : temp(2), 'c' : temp(3)}
>>> print root

+++ Tree +++
() : 1,
('b',) : 2,
('c',) : 3,
--- Tree ---
所以基本上可以看到,只有两个异常是需要注意的,即:TypeError 和 AttributeError,最终成品如下:
def __iadd__(self, other):
try:
self.__update__(other)
except TreeTypeExc:
try:
for pathseq, node in other.items():
self._one_node_set(pathseq, node())
except TreeExc, trx:
# print "debug: TreeExc"
raise TreeTypeExc(trx.msg)
except (AttributeError, TypeError):
# print "debug: AttributeError or TypeError"
raise TreeTypeExc(_("Other operand should be a Tree instance or Tree dict"))
return self
因为 'b' 和 ('b',) 都是 sequence,所以两种效果一样(对 "" 和 () 同理),但 'abc' 和 ('abc',) 是不同的。

现在 Tree 的基本结构已经全部完成,可以从我的项目的 SVN 检出。

google blog 被封的非代理临时解决办法

今天发现 google 的 blogspot 的连后台 www2.blogger.com 都被封了!恼怒之余,在网上查解决办法,发现一个帖子:
http://www.williamlong.info/archives/833.html
说明了一个非代理的解决办法,即修改 hosts 文件,定位到另一个真实的服务器。由于 google 有很多服务器,这确实是一个比较好的办法。

更进一步,根据这个原理,实际上只要能够正确的设置 DNS,不使用国内的 DNS 就可以了。例如,现在的 DNS 查询结果:
C:\Documents and Settings\sysadm>nslookup chowroc.blogspot.com
Server: ns-pxb.online.sh.cn
Address: 202.96.209.6

Non-authoritative answer:
Name: blogspot.l.google.com
Address: 72.14.207.191
Aliases: chowroc.blogspot.com

C:\Documents and Settings\sysadm>nslookup chowroc.blogspot.com
Server: ns-pxb.online.sh.cn
Address: 202.96.209.6

Non-authoritative answer:
Name: blogspot.l.google.com
Address: 72.14.207.191
Aliases: chowroc.blogspot.com
而我在 hosts 文件中的设置是:
72.14.219.190 chowroc.blogspot.com
72.14.219.190 www2.blogger.com
这样前后台都能够正常访问了。

而如果这个新的地址 72.14.219.190 将来也被封了的话,那就只好找找国外的 DNS 服务器了,通常可以将那个 DNS 设置为首选 DNS 等。

当然啦,ZF 还会继续努力,争取将国内的 DNS 和国外的 DNS 彻底隔绝。那么,让我们共同等待 1984 的来临吧。

另一种方法是访问如下的 URL:
http://chowroc.blogspot.com.nyud.net:8090/

另外,使用 nslookup 或 dig 查 blogger.com 可以得到好几个地址:
64.233.163.191, 72.14.207.191, 72.14.219.191
将 chowroc.blogspot.com 和 www.blogger.com 的 hosts 解析都指到其中一个就可以了。

我试过查国外的 DNS,但得到的结果和国内一样,大概这些 DNS 是也是按照地域来区分的。但找了一个在德国的朋友查了一下,也是和国内的一样,不解ing。

星期五, 六月 22, 2007

python reload()

当在交互界面调试 module 的时候,如果 import 了这个 module,然后又作出了更改,可以使用 reload() 重新加载这个模块来同步变化,否则只能退出再进入,那会导致其他的设定丢失造成不便:
>>> import tree
>>> ...
>>> tree = reload(tree)

python Tree root.[inexistent].branch ?

如果
root = Tree(value)
root.trunk.branch = value1
而 trunk 不存在,能否自动创建这个 Tree Node Container

是应该利用 root 的 __getattr__ 还是应该利用 __setattr__ ?因为这时候 trunk 根本就不存在,这时也就根本无从利用其 __setattr__,而对于 root,可以肯定的是在进行 root.trunk.branch = value1 的操作时,肯定是 __getattr__ 被调用!可以看下面的例子:
>>> class test:
... def __init__(self):
... self.x = 1
... def __getattr__(self, attr_name):
... try:
... return self.__dict__[attr_name]
... except KeyError:
... self.__dict__[attr_name] = 'inexistent'
... return self.__dict__[attr_name]
...
>>> t = test()
>>> t.x
1
>>> t.y
'inexistent'
>>> t.x.y = 2
Traceback (most recent call last):
File "", line 1, in ?
AttributeError: 'int' object has no attribute 'y'
>>> t.z.x = 2
Traceback (most recent call last):
File "", line 1, in ?
AttributeError: 'str' object has no attribute 'x'

>>> print t
Traceback (most recent call last):
File "", line 1, in ?
TypeError: 'str' object is not callable
这表明 __repr__ 已经受到了影响,那么原因何在呢?

先来看下面这个例子:
>>> class test:
... def __init__(self):
... self.x = 1
... def __getattr__(self, attr_name):
... print attr_name
... if attr_name == 'y':
... return 2
... else:
... raise AttributeError, attr_name
...
>>> t = test()
>>> t.x
1
>>> t.y
y
2
>>> print t.x
1
>>> print t
__str__
__repr__
<__main__.test>
首先可以看到,在前面的例子中 return self.__dict__[attr_name] 其实不是必须的,因为 python 自己会为我们做这些,并且做的更好,因为它会检查继承树。实际上,只有当一个 attribute 在其继承树中都找不到的时候,__getattr__ 才会被调用。

从 print t 的输出可以看出,self.__str__ 和 self.__repr__ 这两个方法实际上也是通过 __getattr__ 来寻找的,在前面的例子中,没有重载 __str__ 和 __repr__,而是对它们进行了赋值操作,将字符串 'inexistent' 赋值给了它们,当然会导致它们"not callable"。

那么,为了实现上面的 Tree 操作,并且不影响 print 操作,编码如下:
def __repr__(self):
return "" % hex(id(self))
# for k, v in self.__traverse__(): print '%s = %s;' % (k, v)
def __str__(self):
return self.__repr__()
def __getattr__(self, attr_name):
setattr(self, attr_name, Tree(None))
return self.__dict__[attr_name]
def __setattr__(self, attr_name, value):
if attr_name in self.__used_names:
raise TreeExc(_("Attribute name '%s' is reserved" % attr_name))
try:
# If self.attribute exists
existed = self.__dict__[attr_name]
if isinstance(value, Tree):
subtree = value
self.__dict__[attr_name] = subtree
# Replace the node directly
else:
# self.__dict__[attr_name]._Tree__node_value = value
# This will lead to raise TreeExc at #1, because the setattr operation of
# "self.__dict__[attr_name].attribute = value" has been affected by self.__setattr__()
subtree = existed
subtree.__dict__['_Tree__node_value'] = value
# Only replace the node value
except KeyError:
# if self.attribute does not exists, assign it a EMPTY node
if isinstance(value, Tree):
subtree = value
self.__dict__[attr_name] = subtree
else:
self.__dict__[attr_name] = Tree(value)
为了和内置的 print 显示同样的效果,使用了 return "" % hex(id(self)),这里 id(self) 就是得到内存地址。

但是这里还有一个疑问,在前面那个例子中,因为 __str__ 和 __repr__ 的 attr_name 已经被打印出来,并且它又不是 "y",为什么没有抛出 AttributeError 的异常?

星期四, 六月 21, 2007

python module name contains '.'?

sh$ mv bin.py bin.test.py
sh$ python
Python 2.3.4 (#1, Feb 6 2006, 10:38:46)
[GCC 3.4.5 20051201 (Red Hat 3.4.5-2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import bin.test
Traceback (most recent call last):
File "", line 1, in ?
ImportError: No module named test
>>> import bin
>>> dir(bin)
['__builtins__', '__doc__', '__file__', '__name__', 'dbin', 'dbin8', 'rdbin']
>>>
".test.py" 都被忽略掉了!因为 bin.test.py 会导致查找文件 bin/test.py,当然是不存在的!

python Tree 若干相关问题

1. Tree traverse:
@ path string to node/value map
@ path sequence to node/value map
@ list of nodes/values (reference)
@ deep list of nodes?
\==> 2

2. Tree 和 deep dict 两者之间的一致性及其体现(基本原理及设计思想)
计入设计文档

3. Tree search:
@ include/exclude
@ return string or list sequence map
@ copy or assign?
@ key indexed items

4. Tree node value copy/deepcopy?
\==> python mutable/immutable 设计思想?

5. add operation:
head.branch + root.br1 ?
head.branch + {['root', 'br1', 'br11'] : value1, ['root', 'br1', 'br12'] : value2, ...}
\==> Tree update

6. Tree cyclic link 问题

星期三, 六月 20, 2007

python itree = func(head.branch=value)

>>> class tree:
... def __init__(self, **kwargs):
... self.ka = kwargs
...
>>> t = tree()
>>> print t.ka
{}
>>> t = tree(a=1)
>>> print t.ka
{'a': 1}
>>> t = tree(a.b=1)
SyntaxError: keyword can't be an expression
虽然已经定义了 class Tree,并且可以非常方便的操作,如:
head = Tree(value)
head = Tree(value, data=value1, extra=value2)
head.branch = value
head.branch = Tree(value)
head.branch[key] = value
value = head.branch()
value = head.branch[key]()
head.Node1(['branch', 'br1'], value)
head.Node1(['branch', {'br1' : key}, 'br2'], value)
tmap = head.branch('traverse')
other = Tree(value); head.update(other)
但如果要做 func(head.branch=value) 还是不可能的。不过实际上也没有这样的必要,如果需要的是 head.branch 或它的值,可以分别用 head.branch 和 head.branch() 作为其参数,如果是要改变 head.branch 的值,在函数中更改即可。上面的形式只会造成混乱。

python **kwargs

>>> class tree:
... def __init__(self, value, **kwargs):
... exec "self.%s = kwargs" % value
...
>>> t = tree('x', k='v')
>>> print t
<__main__.tree instance at 0xb7ec464c>
>>> print t.x
{'k': 'v'}
>>> t = tree('x', **{'k' : 'v'})
>>> print t.x
{'k': 'v'}
>>> print **{'a' : 1, 'b' : 2}
File "", line 1
print **{'a' : 1, 'b' : 2}
^
SyntaxError: invalid syntax
参考:
2007/05/python-datetime-object-from.html

python setattr

>>> class tree:
... def __init__(self): pass
...
>>> t = tree
>>> t = tree()
>>> t.x = 1
>>> print t.__dict__
{'x': 1}
>>> t.'' = 1
File "", line 1
t.'' = 1
^
SyntaxError: invalid syntax
>>> t. = 1
File "", line 1
t. = 1
^
SyntaxError: invalid syntax
>>> setattr(t, '', 2)
>>> print t.__dict__
{'': 2, 'x': 1}
>>> setattr(t, '?', 2)
>>> print t.__dict__
{'': 2, 'x': 1, '?': 2}
>>> print t.?
File "", line 1
print t.?
^
SyntaxError: invalid syntax
>>> setattr(t, '.y', 3)
>>> print t.__dict__
{'': 2, 'x': 1, '?': 2, '.y': 3}
>>> print t..y
File "", line 1
print t..y
^
SyntaxError: invalid syntax
可见,使用 setattr 可以创建非常不规范的 attr_name,它实际上只是作为一个字符串存放在 instance.__dict__ 中了。在:
2007/06/python-dict-pseudo-private-attributes.html
中我也已经讨论过 __dict__ 的相关问题,实际上是一脉相承的。