搜狐邮件中心将推出本地客户端——极速邮
提前剧透一下:
- 该 Windows 客户端是用IronPython开发的,因此要求 .NET Framework 2.0 环境
- 该客户端采用了特殊协议,暂时只支持搜狐闪电邮系列邮箱。包括 @sohu.com、@vip.sohu.com、@sogou.com、@chinaren.com
- 大概 5 月发布
- 有可能同期以开源形式推出 Linux、MacOSX 的版本
提前剧透一下:
就是怎么在 PHP/mcrypt 和 PyCrypto 之间 DES(或其他加密算法,比如3DES/RSA/..) 通信的问题,我这里还额外考察了下 .NET 平台的算法
网上询问相关问题的还挺多,尤其是 PHP 和 .NET 之间的 DES 转换。主要是 PHP/mcrypt 隐藏了 padding 的细节,且只保留了 ZERO_PADDING 模式,所以不明白cyrpto原理的不太容易找到症结所在。我的传输方案统一用 pkcs#7 padding.
首先是给服务器端增加的 PKCS#7 PADDING 函数,来自PHP官方函数手册上某人的注释
function padding_pkcs7($crypto, $mode, $dat)
{
$block = mcrypt_get_block_size($crypto, $mode);
$len = strlen($dat);
$padding = $block - ($len % $block);
$dat .= str_repeat(chr($padding),$padding);
return $dat;}
function strip_pkcs7($crypto, $mode, $text)
{
$block = mcrypt_get_block_size($crypto, $mode);
$packing = ord($text{strlen($text) - 1});
if($packing and ($packing < $block)){
for($P = strlen($text) - 1; $P >= strlen($text) - $packing; $P--){
if(ord($text{$P}) != $packing){
$packing = 0;
}
}
}
return substr($text,0,strlen($text) - $packing);
}
本地端 .NET 平台的 descrypto 封装
# -*- coding: utf-8 -*-
# ipcrypto.py
from System import Array, Byte
from System.Security.Cryptography import DESCryptoServiceProvider, CryptoStream, CryptoStreamMode, PaddingModefrom System.IO import MemoryStream, StreamWriter, StreamReader
from hashlib import md5
def s2ab(s): return Array[Byte](tuple(Byte(ord(c)) for c in s))
def ab2s(ab, len=0):
if len == 0: len = ab.Length return ''.join([chr(ab[i]) for i in range(len)])
class descrypto():
def __init__(self, password):
pwmd5 = md5(password).digest()
self.key = s2ab(pwmd5[:8])
self.iv = s2ab(pwmd5[8:]) self.des = DESCryptoServiceProvider() # 缺省 des.Mode = CipherMode.CBC
def enc(self, input):
ms = MemoryStream()
encStream = CryptoStream(ms, self.des.CreateEncryptor(self.key, self.iv), CryptoStreamMode.Write)
sw = StreamWriter(encStream)
sw.Write(input)
sw.Flush()
encStream.FlushFinalBlock() return ab2s(ms.GetBuffer(), ms.Length)
def dec(self, input):
ms = MemoryStream(s2ab(input))
length = len(input)
decStream = CryptoStream(ms, self.des.CreateDecryptor(self.key, self.iv), CryptoStreamMode.Read)
byteArray = Array.CreateInstance(Byte, length)
length = decStream.Read(byteArray, 0, length)
return ab2s(byteArray, length)
本地端 PyCrypto 的封装
# -*- coding: utf-8 -*-
# pycrypto.py
from hashlib import md5from Crypto.Cipher import DES
class descrypto():
def __init__(self, password):
pwmd5 = md5(password).digest()
self.key = pwmd5[:8] self.iv = pwmd5[8:]
def enc(self, input):
des = DES.new(self.key, DES.MODE_CBC, self.iv)
lastblock = len(input) % 8
if lastblock > 0:
padding = 8 - lastblock input += padding * chr(padding)
return des.encrypt(input)
def dec(self, input):
des = DES.new(self.key, DES.MODE_CBC, self.iv)
ret = des.decrypt(input)
padding = ord(ret[-1])
for i in range(padding):
if ord(ret[-1 - i]) != padding:
padding = 0
break
if padding > 0:
ret = ret[:-padding]
return ret
既然要翻墙,肯定要有一台墙外主机。为了配合加密,以及 HTTP/HTTPS 协议代理,需要编译有 mcrypt 和 curl 的 PHP;在如今我估计这应该都属于web主机标配环境.
如果是文本数据,就加密后返回;如果非文本数据,就不加密了。返回给本地代理以第一个字符是"0" or "1"来指示接下来的数据是否经过加密。
配合其运行的代码见
$PASSWORD = "yourpasswordhere";
$pw_md5 = md5($PASSWORD, true);
$key = substr($pw_md5, 0, 8);$iv = substr($pw_md5, 8, 8);
$input = file_get_contents("php://input");
$td = mcrypt_module_open('des', '', 'cbc', '');
mcrypt_generic_init($td, $key, $iv);
if (strlen($input) > 0 && $input % 8 == 0) { $input = strip_pkcs7("des", "cbc", mdecrypt_generic($td, $input));
$req = explode("\r\n\r\n", $input, 3);
$rawreqline = explode(" ", $req[0]); $url = parse_url($rawreqline[1]);
$_headers = explode("\r\n", trim($req[1]));
//$_headers[count($_headers)] = "X-Forwarded-For: ".$_SERVER['REMOTE_ADDR'];
if ($url["scheme"] == "http" || $url["scheme"] == "https") {
$ch = curl_init($rawreqline[1]);
/* avoid HTTP/1.1 Transfer-Encoding: chunked */
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($ch, CURLOPT_HTTPHEADER, $_headers);
curl_setopt($ch, CURLOPT_HEADER, 1);
if ($url["scheme"] == "https") {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if ($rawreqline[0] == "POST" && count($req) == 3) {
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req[2]);
} $data = curl_exec($ch);
curl_close($ch);
} $text_mode = "0";
$res = explode("\r\n\r\n", $data, 2);
$header = explode("\r\n", $res[0], 2); // STATUS HEADER
$headers = explode("\r\n", $header[1]);
foreach ($headers as $hline) {
$h = explode(":", $hline, 2);
$k = strtolower(trim($h[0]));
if ($k == "content-type" && strpos(strtolower(trim($h[1])), "text/") === 0) {
$text_mode = "1";
break;
}
}
if ($text_mode == "1") {
mcrypt_generic_deinit($td);
mcrypt_generic_init($td, $key, $iv);
$data = mcrypt_generic($td, padding_pkcs7("des", "cbc", $data));
}
$data = $text_mode . $data;
}
?>=$data?>
我的这个 proxy 代码参考自 SUZUKI Hisao 的 Tiny HTTP Proxy。主要修改的有两点:
# -*- coding: utf-8 -*-
# 建议 Python 2.6 环境,以支持 https proxy
# Win32 下需安装
# http://www.python.org/ftp/python/2.6.5/python-2.6.5.msi
# http://www.voidspace.org.uk/downloads/pycrypto-2.0.1.win32-py2.6.exe
# 至于 IronPython 目前还没有 server-side ssl 支持,据说 IP 2.6.1 将会有...
try:
from ipcrypto import descrypto
#IronPython 里的 socket 不支持 bind 到 '0.0.0.0'
import platform
bind_address = (platform.node(), 8000)
except:
from pycrypto import descrypto bind_address = ('0.0.0.0', 8000)
import urllib2import BaseHTTPServer, SocketServer
#REMOTEURL/PASSWORD 和国外主机配合
REMOTEURL = 'http://www.dup2.org/blarblarblar.php'PASSWORD = 'yourpasswordhere'
# KEY/CERT 的生成参考 http://docs.python.org/library/ssl.html
KEYFILE = 'cert.pem'CERTFILE = 'cert.pem'
#自定义允许的IP列表, 给每个IP起个名字帮助记忆allow_clients = {'127.0.0.1': 'myself'}
desobj = descrypto(PASSWORD)
skip_headers = ["keep-alive", "proxy-connection", "connection", "accept-encoding"]
class pseudofile():
''' SSL Pseudo File Object'''
def __init__(self, sslobj):
self.sslobj = sslobj self.closed = 0
def read(self, size):
chunks = []
read = 0
while read < size:
data = self.sslobj.read(size-read)
read += len(data)
chunks.append(data) return ''.join(chunks)
def readline(self):
line = []
while 1:
char = self.sslobj.read(1)
line.append(char) if char == "\n": return ''.join(line)
def write(self, data):
bytes = len(data)
while bytes > 0:
sent = self.sslobj.write(data)
if sent == bytes:
break # avoid copy
data = data[sent:] bytes = bytes - sent
# 下面两个方法是 BaseHTTPServer 里会调用到的
def flush(self):
pass close = flush
def checkip(f):
def new_f(_self):
(ip, port) = _self.client_address
if ip in allow_clients:
f(_self)
else:
_self.send_error(403) return new_f
class ProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
@checkip
def do_GET(self):
content_length = 0
if hasattr(self, "sslhost"): self.raw_requestline = "%s https://%s%s %s\r\n" % (self.command, self.sslhost, self.path
, self.request_version)
h = [self.raw_requestline]
for kv in self.headers.items():
if kv[0] == 'content-length':
content_length = int(kv[1])
if kv[0] in skip_headers: continue
h.append("%s: %s" % kv)
h.append("connection: close") req = "\r\n".join(h) + "\r\n\r\n"
if content_length: req += self.rfile.read(content_length)
encreq = desobj.enc(req)
req = urllib2.Request(REMOTEURL, encreq) f = urllib2.urlopen(req)
text_mode = f.read(1)
response = f.read()
if text_mode == "1":
response = desobj.dec(response)
self.wfile.write(response)
print 'REQUEST:', self.raw_requestline.strip()
#有时候一些看起来是 text/* 的请求也是 binary mode,通常是 304 Not Modified print 'RESPONSE: %s, %d Bytes' % ('crypted mode' if text_mode == "1" else 'raw mode', len(response))
self.close_connection = 1
@checkip
def do_CONNECT(self):
# print self.raw_requestline
# "CONNECT twitter.com:443 HTTP/1.1"
self.sslhost = self.raw_requestline.split()[1]
self.wfile.write(self.protocol_version + " 200 Connection established\r\n")
self.wfile.write("Proxy-agent: QYB\r\n\r\n")
# TODO 浏览器端会看到一个警告,但是没有办法;避免警告是不对的,必须让使用者认识到现在是中间人模式
try:
import ssl
self.rfile = pseudofile(ssl.wrap_socket(self.connection, KEYFILE, CERTFILE, True))
self.wfile = self.rfile
self.handle_one_request()
except:
print 'ssl error:', self.raw_requestline self.close_connection = 1
do_PUT = do_GET
do_POST = do_GET
do_HEAD = do_GET do_DELETE = do_GET
class ThreadingHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): pass
httpd = ThreadingHTTPServer(bind_address, ProxyHandler)
httpd.serve_forever()
关键词:结石宝宝家长
RT @ranyunfei: 中国民众必须知道一个残酷的事实:如果你不想让你的孩子成为奴隶主的添头,在中国生小孩是需要勇气的。如若没有替孩子的自由和权利而战的准备就最好别生。
RT @aiww 判刘晓波,那是读书人的事;判谭作人,那是四川的事;今天审判赵连海,这是每一个有后代的人的事,除非你觉得中国人就该断子绝孙。
今天达达和我说,在她想象的剧情里,小灰灰还有一个姐姐,叫小红红。。。小红红和美羊羊一样,都有漂亮的蝴蝶结,穿粉红色的衣服,长相也差不多。。。囧
喜羊羊和灰太狼里的设定很奇怪,反方是很有代入感的温馨三口之家,正方是一个怪爷爷带着一大群父母不知道在哪里的孩子。
不过百亩森林也是这样,除了小豆,其他的小朋友身世都不清楚;米老鼠唐老鸭亦是如此。相比较美国动画片而言,是不是中国这个年龄段的动画片一直以来人物关系都有明显的长幼尊卑呢?欧洲动画片好像也展现家庭关系的多?
本来自从换到国外主机后,也就有了一个 ssh 帐号,用来翻墙是通行无阻。但是在某些情况下无法访问外面的 22 端口(比如搜狐的WIFI),那就只能想法通过80端口来翻了.
大概有三种方案:我这个重复造的轮子比 phpproxy 增加的地方有两处,一个是在墙内外传输之间弄了一个DES加密,避开内容检查;另外一个是很凑合的实现了HTTPS代理(就是会有浏览器警告,很像中间人证书欺骗)。本地的 Proxy 在 Python 和 IronPython 下都能运行,如果哪天想用手机翻墙的话再试试 PyS60 能不能跑起来
感觉对付防火墙还是得靠 P2P 的模式,部分人贡献出口通道,静态内容在墙内大量缓存。打算要是有时间的话研究研究 .NET Framework 里的 P2P 支持,做出来的产品能在 Windows 上运行才是王道。
看起来 Say No To GMOs 是一个反对理由的很好来源。可惜,我没时间一一查看,只是学习了一下 http://en.wikipedia.org/wiki/Genetically_modified_organism
从维基百科看,GMOs 有三个反对点:3. 转基因作物的食用安全
我自己感觉问题 1 还好说,专利不是太大的问题。市场竞争一定会出现专利方面可以制衡的另一方,再不济也有政府保障——虽然现在这个政府不太知道怎么去保障民众权利。俺只关注大公司,比如孟山都,是否会利用滥用专利权或垄断地位损害消费者利益。
问题2是真正要研究的,有可能导致灾难后果.
至于食品安全,维基有点语焉不详,只是说部分国家要求明确分开食物和饲料,以及外包装。看样子是要把选择权交给消费者。【有可能孟山都的雇员每天会上来删除对它不利的信息】
但我个人觉得我能吃地沟油,能吃三聚氰胺,被打劣质疫苗;每天被电脑手机辐射;上个互联网还常常被RESET而而火气很大...转基因食物对我个人健康,以及遗传DNA的影响(就算有影响也被基本国策湮灭了),大概是可以忽略不计的
其实 Labs 这个东西,本来我最初是想起名为"作坊"的,一方面是俺们行业总是被称呼为作坊,另一方面我确实认为这是项手艺活,该名字非常贴切。但是这个主意肯定会被产品同学痛批,所以还是用"工场"来代替"作坊"了
如果不是来自理工专业的研究生,只能凭空想象 Labs 是什么地方吧。我把它称之为工程师梦开始的地方,希望不会遭到误解。
在我的计划里,梦工场的出产未必是用户所迫切要求,未必是产品/运营所欣赏;而完全是工程师主导的,充满了不确定,以及那么一点点小bug的项目;梦工场的另外一个考虑就是优化框架,让各项应用可以很方便的以 plugins 形式嵌入——但现在还没这么作,需要长时间的磨合。
之所以有上述这样的打算,是因为我认为未来可能有样激动人心的事情就是把WebMail作为一个平台,鼓励第三方加入开发。2月份我在twitter上说,"关于gmail,实验室就是它的appstore...另外,google apps潜在的appstore商业机会才叫一个大"。仿佛为了回应我这段话,上个星期,GoogleCode Blog连续发表两条消息,一条针对企业,"Integrate, Publish, Sell - The Google Apps Marketplace",一条针对个人应用,"Coming soon: Gmail contextual gadgets available for trusted testers"
回到现实,梦工场目前也是产品经理们的实验地,看看产品经理驱动和工程师驱动最后谁会被用户接受,也是让人期待的事情。
不喜欢有人妖魔化 rpm,尤其是如果从来没有用过 fedora/opensuse 也出来乱喷
1. 这只是一个应用的打包规范,本质上和 deb 没有什么差别。4. 有一种叫 DeltaRPM 的格式,可以极大缩小每次 Update 的带宽开销。这非常酷
另外和 apt 对应的是 yum/zypper 等高级命令行工具2. 我使用 yum 和 zypper 的时候,没有感到什么 rpm 依赖关系的麻烦
我最近两年使用的都是 rpm 的系统,确实不了解最近 deb 的进展。但就我搜索来看,LZMA,Delta Patch 这两个特性 deb 系应该还差一点.
最新评论