分类存档: 技术杂谈

setSoTimeout does not work with nio SocketChannel

通过SocketChannel.socket().setSoTimeout(timeout)设置读超时,对于SocketChannel.read(buffer)操作来说是不会有任何效果的,如果SocketChannel设置了blocking mode的话会一致阻塞直到有可读取的内容或EOF。

有人就此给Sun提了个bug(http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4614802), 但Sun不认为这是个bug:

Not a bug.  The read methods in SocketChannel (and DatagramChannel) do not
support timeouts.  If you need the timeout functionality then use the read
methods of the associated Socket (or DatagramSocket) object.

既然Sun不认为这是个bug只能自己使用的时候注意了(有人说起码是个Javadoc的bug, absolutely!)

网易新邮件提醒Chrome扩展开发

最近突然对chrome扩展开发来了兴趣,刚好最近了解了下网易邮箱助手获取新邮件到达的方法就想着自己动手写一个新邮件到达提醒的chrome扩展(其实挺蛋疼的,网易邮箱网页版本身就提供了提醒功能)。协议可以通过抓包来了解,也比较简单易懂。

周六睡醒就花了几个小时一边看chrome扩展的开发文档一边码代码,花了几个小时就把第一版弄出来了,chrome的扩展开发还是挺方便的。

一般来说,extensions都会有一个背景页面(background_page)用于主流程处理,逻辑代码通常都在这里。除了流程处理外一般还会有extension的设置需要处理,那么一般也会提供一个选项页面(options_page)。extension各个页面之间可以通过chrome.extension的api来进行通讯,比如可以通过chrome.extension.getBackgroudPage()获得背景页面的DOM树。API都相对比较简单,用的时候翻翻手册就很容易明白了(PS:360翻译的质量真不敢恭维)。

项目代码托管在google code上,有兴趣的可以自行查看

TODO:
1. 目前版本账号密码都是明文保存的,可以改成md5处理。
2. 点击进入邮箱查看邮件。
3. 5秒后自动关闭弹窗。
4. 自动更新。
5. 多账号支持。
6. 异常情况处理。

[RT] Using stunnel to telnet into GMail IMAP

PS: 原文被墙, 转来方便墙内翻阅。 最近在搞IMAP相关的东西, 一直头疼不知道怎么命令行下测gmail的一些行为, 这篇文章真是帮大忙了。

By edwin – Posted on 12 February 2009

Here is a case study of how stunnel can be used to test an SSL based protocol. We will create an stunnel configuration that reroutes the IMAP port (TCP 143) to the Secure IMAP port (TCP 993) on GMail’s IMAP server (imap.gmail.com). We will than test the setup by using telnet.

I will be using Ubuntu 8.10 (Intrepid Ibex).

First, let’s install stunnel.

sudo apt-get install stunnel

Edit /etc/default/stunnel4, change ENABLED=0 to ENABLED=1

Edit /etc/stunnel/stunnel.conf as shown in the example below:

; Sample stunnel configuration file by Michal Trojnara 2002-2006
; Some options used here may not be adequate for your particular configuration
; Please make sure you understand them (especially the effect of chroot jail)

; Certificate/key is needed in server mode and optional in client mode
;cert = /etc/stunnel/mail.pem
;key = /etc/stunnel/mail.pem

; Protocol version (all, SSLv2, SSLv3, TLSv1)
sslVersion = SSLv3

; Some security enhancements for UNIX systems – comment them out on Win32
chroot = /var/lib/stunnel4/
setuid = stunnel4
setgid = stunnel4
; PID is created inside chroot jail
pid = /stunnel4.pid

; Some performance tunings
socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1
;compression = rle

; Workaround for Eudora bug
;options = DONT_INSERT_EMPTY_FRAGMENTS

; Authentication stuff
;verify = 2
; Don’t forget to c_rehash CApath
; CApath is located inside chroot jail
;CApath = /certs
; It’s often easier to use CAfile
;CAfile = /etc/stunnel/certs.pem
; Don’t forget to c_rehash CRLpath
; CRLpath is located inside chroot jail
;CRLpath = /crls
; Alternatively you can use CRLfile
;CRLfile = /etc/stunnel/crls.pem

; Some debugging stuff useful for troubleshooting
debug = 7
output = /var/log/stunnel4/stunnel.log

; Use it for client mode
client = yes

; Service-level configuration

;[pop3s]
;accept = 995
;connect = 110

[imaps]
accept = 143
connect = imap.gmail.com:993

;[ssmtp]
;accept = 465
;connect = 25

;[https]
;accept = 443
;connect = 80
;TIMEOUTclose = 0

; vim:ft=dosini

Start up Stunnel

sudo /etc/init.d/stunnel4 start

Verify that the IMAP is listening on the local server.

netstat -an | grep -iw LISTEN
tcp 0 0 0.0.0.0:143 0.0.0.0:* LISTEN

The following requires that your GMail account have IMAP enabled. This is not enabled by default. Replace username@gmail.com with your real email address. Replace password with your real password.

telnet localhost 143
Trying 127.0.0.1…
Connected to localhost.
Escape character is ‘^]’.
* OK Gimap ready for requests from 71.65.199.7 c5if2789008nfi.67
)
01 LOGIN username@gmail.com password
01 OK username@gmail.com authenticated (Success)
02 LOGOUT
* BYE LOGOUT Requested
02 OK 73 good day (Success)
Connection closed by foreign host.

That’s it. If you’re feeling adventourous you can use Hydra to brute force an account you own.

./hydra -l yourfriend@gmail.com -P password.txt -V localhost imap

如何抓iPad的http[s]请求

        
ipad上目前没发现有抓包的工具, 就算有也应该不会好用到哪里去, 但要对ipad的http或https请求进行抓包该怎么办呢?方法还是有的, 前提是有两块能上网的网卡(至少有一块无线网卡,主要是当无线路由用,本身有无线路由的就忽略connectify这一步吧)以及一个Windows操作系统(都怪Fiddler2只有windows版本)。

        
首先, 通过Ad-hoc open(Access Point等其他方式也可以, 我懒得输密码就设成Ad-hoc open了)将无线网卡共享出去(可以通过Connectify傻瓜化地设置, 不过dhcp似乎经常出问题啊), 如图:

 

        
然后, 开启Fiddler2 (一个强大的http[s]抓包工具), 如果需要抓取https的话需要在Tools->Fiddler Options->HTTPS里勾选上Decrypt https traffic, 如图:
        
接着需要设置Fiddler2的代理服务,如果不希望抓取非ipad的http请求请不要勾选WinINET Connections的相关选项
        
最后就是设置ipad的wifi了, wifi通过刚刚共享的无线进行上网并将HTTP代理设置为手动, 服务器地址填上192.168.2.1,端口填上8888(就是Fiddler2->Tools->Fiddler Options中填写的Fiddler listens on port),这样ipad上的所有http[s]请求都能在Fiddler2上看到了。


        
PS: Fiddler2抓https请求是通过伪造证书来实现的,需要信任这个伪造的证书。

 

如何修改deb安装包里的依赖关系/冲突

    今天装code::blocks时, 总是提示 错误: 破坏已有软件包 ‘codeblocks-common’ 对 codeblocks (< 10.05-1) 的冲突关系“, 直接打开deb包修改里面的control文件也没用,关闭后又恢复原样了。在 ubuntuforms 搜到一段shell脚本倒是可以修改control文件后重新生成一个deb包, 脚本如下:

#!/bin/bash
if [[ -z "$1" ]]; then
  echo “Syntax: $0 debfile”
  exit 1
fi
DEBFILE=”$1″
TMPDIR=`mktemp -d /tmp/deb.XXXXXXXXXX` || exit 1
OUTPUT=`basename “$DEBFILE” .deb`.modfied.deb
if [[ -e "$OUTPUT" ]]; then
  echo “$OUTPUT exists.”
  rm -r “$TMPDIR”
  exit 1
fi
dpkg-deb -x “$DEBFILE” “$TMPDIR”
dpkg-deb –control “$DEBFILE” “$TMPDIR”/DEBIAN
if [[ ! -e "$TMPDIR"/DEBIAN/control ]]; then
  echo DEBIAN/control not found.
  rm -r “$TMPDIR”
  exit 1
fi
CONTROL=”$TMPDIR”/DEBIAN/control
MOD=`stat -c “%y” “$CONTROL”`
vi “$CONTROL”
if [[ "$MOD" == `stat -c "%y" "$CONTROL"` ]]; then
  echo Not modfied.
else
  echo Building new deb…
  dpkg -b “$TMPDIR” “$OUTPUT”
fi
rm -r “$TMPDIR”

Ubuntu下安装TP Link TL-WN821N 无线网卡驱动

        前几天笔记本自带的无线网卡似乎坏了,系统没有识别这个设备,就买了TP Link的USB无线网卡TL-WN821N。插上后发现Ubuntu根本没有识别这个设备,还是无法上网。
        郁闷之下,切回windows(TP Link随机光盘带有windows的驱动),上网搜索后发现有madwifi这个开源项目,但是该项目目前还不支持usb接口的网卡(其他两个版本ath5k/ath9k的设备支持列表也没找到TL-WN821N)。后来发现有人提到用ndiswrapper可以安装windows下的驱动文件,并且用IOGEAR GWU623.zip里面的驱动文件可以正常安装使用。但我这台机安装ndiswrapper后再装上GWU623的驱动,却还是无法上网!删掉这个驱动后用TL-WN821N自带光盘里的驱动文件,能正常上网了,oh yeah

mysql字符默认不区分大小写?

        最近发现短链接服务在生成短链接时经常会发生碰撞,刚开始单纯地以为是生成算法有问题,毕竟就算是4位的短链接也有62^4个链接可能(26个字母的大小写+10个数字),如果生成算法没问题的话发生碰撞的概率应该是较小的。
        检查了一遍代码,生成算法也确实有一些问题,但都不是主要问题。今天随手在mysql里select了一下,发现查询的时候居然不区分大小写!那么其实4位的短链接就只有36^4个链接可能(160多万而已)!查阅了mysql相关文档,大致了解了mysql字符集及校对规则的选取方式:
       1. 如果指定了字符集和校对规则,那么采用指定的字符集和校对规则。
       2. 如果指定了字符集但没有指定校对规则,那么采用指定的字符集和该字符集的默认校对规则。
       3. 否则,采用服务器字符集和服务器校对规则。
       可以通过show collation命令查询支持的校对规则及字符集默认的校对规则。
       mysql数据库也差不多支撑不住目前的需求了,反正要迁移到分布式数据库了就暂时不理这个问题了。但越来越觉得是应该好好完整地将mysql手册看一遍了。

邮件信头中Return-Path、Reply-To和From的区别

        懒得自己写,直接引用一段别人的回复(原贴传送门),大致跟自己之前的实践及查看的文档相符。
        1)The Return-Path (sometimes called the Reverse-Path or Envelope-FROM — all of these terms can be used interchangeably) is the value used during the SMTP session. As you can see, this does not need to be the same value that is actually found in the mail headers. Only the recipient’s mail server is supposed to add a Return-Path header to the top of the email. This records the actual Return-Path sender during the SMTP session. If a Return-Path header is already exists in the email, then that header is to be removed, and replaced by the recipient’s mail server.

All bounces that occur during the SMTP session should go back to the Return-Path value. Some servers may accept all email, and then queue it locally, until it has a free thread to deliver it to the recipient’s mailbox. If the recipient doesn’t exist, it should bounce it back to the recorded Return-Path value.

        Note, not all mail servers obey this rule. Some mail servers will bounce it back to the FROM address.

        2)The FROM address is the value actually found in the FROM header. This is supposed to be who the message is FROM. This is what you see as the “FROM” in most mail clients. If an email does not have a Reply-To header, then all human (mail client) replies should go back to the FROM address.

        3)The Reply-To header is added by the sender (or the sender’s software). It is where all human replies should be addressed too. Basically, when the user clicks “reply”, the Reply-To value should be the value used as the recpient of the newly composed email. The Reply-To value should not be used by any server. It is meant for client side use.

        However, as you can tell, not all mail servers obey the RFC standards or recommendations.

数据库表的轮转(续)

       上次提到了数据库表的轮转,大致阐述了问题及初步的解决方法。然而实现的时候还是会遇到些问题。
        轮转的时候,需要将旧表A中未完成的任务都迁移到新建的表B中(当然也可以不迁移,而是直接将旧表中的任务处理完为止,但这种方式更为复杂曲折,不予考虑)。迁移的时候有可能会往A中继续插入新的任务,迁移可能会遗漏一些记录;或者从A中取出任务执行完后需要更新任务状态时却发现A中该记录已被迁移到B中了,造成任务状态的不确定性;又或者在A中存在某用户U的开通记录但处理时失败了需要继续重试,但迁移过程中继续插入了一条取消记录,那么就有可能取消成功了但是又由于之前有个失败的开通记录,重试后就导致又被开通了。可能存在的问题还有许多,比如有可能新建表或迁移的时候数据库处理失败了。有没办法将创建新表、迁移记录处理成一个原子操作呢?
        首先想到的是通过加锁。具体步骤如下:
                1. 创建新表B
                2. 对旧表A和新表B及轮转记录表C都加写锁(lock tables a write, b write, c write;)
                3. 关闭自动提交(set autocommit=0)
                4. 更新轮转记录C
                5. 将旧表A中需要迁移的记录都select出来,然后批量插入到B中
                6. 将sql语句中记录的tableName变量更改成新表B的表名
                7. 更新轮转记录C,标记本次迁移完成
                8. 释放表锁(unlock tables)及其他一些后续处理
        但由于对数据库锁天然的畏惧,最终并没有使用该方式,而是通过自己程序内部控制以模拟达到锁的效果。具体步骤如下:
                1. 创建新表B
                2. 设置onCopy=true(onCopy=true时,其他希望更新任务记录的数据库操作都会阻塞在copyLock.wait()上)
                3~7与上述方式一致
                8. 设置onCopy=false,并调用copyLock.notifyAll()以唤醒所有阻塞的数据库操作
        这次重构,由于对mysql的了解还不够深入,花费了许多时间,期间详细查看了mysql官方文档关于事务及锁的描述,获益匪浅

数据库表的轮转

        负责的一个项目,在开通时会赠送一批服务(粗略算了下大概有10来个),而开通这些服务有些可能比较耗时,就将这些服务都以后台任务的形式处理了。
        最开始开通量少的时候,这些任务都是在一张表里以不同任务名记录的,处理完就修改下任务状态。后来随着开通量的增加,集中在一张表里已经开始会影响性能了,那么很自然地就想到分表,将不同的任务划分到不同的表里,数据量就少了一个数量级。
        但是开通量增长得比较快,就算分表发现性能也并不能好多少。最开始想偷懒就打算隔一段时间清理一下已经处理完的任务(反正这些任务处理完后基本都不需要再保留的了),但是mysql如果不optimize表的话,删除的空间是不会被回收利用的,索引也是,所以就得经常停机optimize表。
        经常这么折腾,谁都受不了,而且服务也得停会影响线上用户的正常使用也不好。最近就将这些任务表都做成按天轮转,每天轮换一个表,这样每个表的数据就基本在10多万行里,而且空间不够的时候可以直接drop掉一些过期的表(写个脚本定期drop)也不会影响线上服务,维护工作量明显少了很多。