2008年11月23日星期日

嘿,今天你精神病了么?

由总后卫生部组织、北京军区总医院牵头制定的《网络成瘾临床诊断标准》近日通过专家论证,这意味着我国医学界诊断“网瘾”将实行标准 化.《标准》中有这样 一项量化的指标:平均每天连续使用网络达到或超过6小时,而且这种症状达到或者超过3个月.
----11月23日《人民日报》

恩,我参照了一下自己的症状,原来我是重度精神病患者唉……这下好了,以后杀人不犯法咯~咱是精神病,咱怕谁?


今天你精神病了么?

2008年11月17日星期一

[FWD]奥巴马胜选演说·文言版

〈奥巴马胜选演说·文言版〉

东东枪

Hello,Chicago!

芝城父老,别来无恙,

If there is anyone out there who still doubts that America is a place where all things are possible, who still wonders if the dream of our founders is alive in our time, who still questions the power of our democracy, tonight is your answer.

余尝闻世人有疑,不知当今美利坚凡事皆可成就耶?开国先贤之志方岿然于世耶?民主之伟力不减于昔年耶?凡存诸疑者,今夕当可释然。

It’s the answer told by lines that stretched around schools and churches in numbers this nation has never seen, by people who waited three hours and four hours, many for the first time in their lives, because they believed that this time must be different, that their voices could be that difference.

今夕之释然,皆蒙美利坚民众之协力——学塾祠庙之外,市井乡野之间,万千父老心焦似焚,苦待竟日,愿献一票之力。其中,平生未尝涉国事者,数亦不少,而今有此义举,皆因一念不衰——今夫天下,非同既往,愿发吁天之声,必成动地之势。

It’s the answer spoken by young and old, rich and poor, Democrat and Republican, black, white, Hispanic, Asian, Native American, gay, straight, disabled and not disabled. Americans who sent a message to the world that we have never been just a collection of individuals or a collection of red states and blue states. We are, and always will be, the United States of America.

今夕之释然,皆仰吾国同胞之齐心——何谈贫富老幼之差、党社宗族之异,惶论发肤肌体之别、志趣爱恶之分。吾国既以“合众”为名,吾辈则更无疏离之意,红蓝二党并肩而立,数十邦州挽手相合,无分你我,共称一家,昂然于世,齐声一呼,天下乃有此释然。

It’s the answer that led those who’ve been told for so long by so many to be cynical and fearful and doubtful about what we can achieve to put their hands on the arc of history and bend it once more toward the hope of a better day.

今夕之释然,皆因愤懑者之镇静,忧惧者之勇气,犹疑者之笃定——平素世间种种,消磨其志向,溃灭其梦想,而值此风云之际,除旧更新,当仁不让,倾力而动乾坤者,更何人哉!

It’s been a long time coming, but tonight, because of what we did on this date in this election at this defining moment change has come to America.

俟之诚久,其志弥坚。幸天地明察,乃有今日,乃有此刻,乃有此一选举,乃有我亿万美利坚大好国民——吾邦之大变革,方得自兹而始也!

A little bit earlier this evening, I received an extraordinarily gracious call from Sen. McCain.

Sen. McCain fought long and hard in this campaign. And he’s fought even longer and harder for the country that he loves. He has endured sacrifices for America that most of us cannot begin to imagine. We are better off for the service rendered by this brave and selfless leader. I congratulate him; I congratulate Gov. Palin for all that they’ve achieved. And I look forward to working with them to renew this nation’s promise in the months ahead.

倾接参议员麦君凯恩电,虽未得晤,幸有一谈,其言谆谆,其意诚诚,鄙人感佩之至。选战期内, 麦君劳碌几重,奔波几许,皆为国家计。诸般求索,时日良多,皆非余所能及。于国于民之惊人牺牲,亦非庸庸如吾辈者所可想见。以麦君之胆魄襟怀,能为吾邦所 用,实国家之幸,万民之幸也。前途漫漫,其事未竟,余所盼瞩由衷者,唯共麦凯恩君、佩林君,及诸贤士比肩,会吾等之绵力,成吾邦之大业。

I want to thank my partner in this journey, a man who campaigned from his heart, and spoke for the men and women he grew up with on the streets of Scranton and rode with on the train home to Delaware, the vice president-elect of the United States, Joe Biden.

乔君拜登,亦吾所感铭至深者也。竞选之业,艰险不足与外人道,幸有乔君之辅佐,其诚天可鉴之。乔君其人,素言恳辞切,意笃情真,盖尝经斯兰克顿街乡邻之提命,饱聆特拉华州父老之晤教也。他日余既登总统之位,乔君必当副之。

And I would not be standing here tonight without the unyielding support of my best friend for the last 16 years the rock of our family, the love of my life, the nation’s next first lady Michelle Obama.

拙荆米氏,追随鄙人凡一十六年,既为爱侣,更为挚友,既为吾阖家之基石,又乃余终生之至爱。鄙人尝自忖度,倘无贤妻若此,今朝阔论高谈于此处者,不知何人矣!

Sasha and Malia I love you both more than you can imagine. And you have earned the new puppy that’s coming with us to the new White House.

小女萨沙、玛丽,余素深喜之。昔日为父尝与汝等言,此番选战若得一胜,愿购小犬一头相赠,待阖家乔迁总统府邸之日,偕汝等同进吾宅。今当胜负已出,既有一诺在前,必自践行不欺也。

And while she’s no longer with us, I know my grandmother’s watching, along with the family that made me who I am. I miss them tonight. I know that my debt to them is beyond measure.

祖母大人虽已仙逝,料必有灵在天,俯察人寰,想应颔首开颜矣。吾奥巴马氏列祖列宗,亦当如是。今日今时,此情此景,鄙人追思之心,乌鸟之情,曷其有极!唯生死陌路,仙凡有别,虽怀反哺之心,而无答报之门也!

To my sister Maya, my sister Alma, all my other brothers and sisters, thank you so much for all the support that you’ve given me. I am grateful to them.

至若玛雅、艾玛二姐妹,以及吾家诸同胞,所惠我者,亦属良多,久沐恩德,此当拜谢。

And to my campaign manager, David Plouffe, the unsung hero of this campaign, who built the best — the best political campaign, I think, in the history of the United States of America.To my chief strategist David Axelrod who’s been a partner with me every step of the way.To the best campaign team ever assembled in the history of politics you made this happen, and I am forever grateful for what you’ve sacrificed to get it done.

大卫?普劳夫君,大卫?阿克塞罗德君,一为鄙人竞选事务之经理,一为鄙人国事韬略之智囊。余尝自喟叹,左右谋士,余所仰赖者,皆亘古未见之贤才。普阿二君,则更此中之翘楚。区区不才,有何德能,可得膀臂若此?当此功成之际,感荷之心,亦自拳拳。

But above all, I will never forget who this victory truly belongs to. It belongs to you. It belongs to you.

至于鄙人铭之肺腑,须臾不敢忘怀者,则诸位也。盖今日鄙人之胜绩,实诸位之胜绩,鄙人之荣光,实诸位之荣光!

I was never the likeliest candidate for this office. We didn’t start with much money or many endorsements. Our campaign was not hatched in the halls of Washington. It began in the backyards of Des Moines and the living rooms of Concord and the front porches of Charleston. It was built by working men and women who dug into what little savings they had to give $5 and $10 and $20 to the cause.

余素朴陋,虽有参选之心,并无必胜之志。谋事之初,银资乏匮,从者寥寥;起事之地,皆蔽寓荒斋,不在高阁;成事之基,无非寻常百姓,涓滴之献。

It grew strength from the young people who rejected the myth of their generation’s apathy who left their homes and their families for jobs that offered little pay and less sleep.

It drew strength from the not-so-young people who braved the bitter cold and scorching heat to knock on doors of perfect strangers, and from the millions of Americans who volunteered and organized and proved that more than two centuries later a government of the people, by the people, and for the people has not perished from the Earth.

今日之胜,有赖一众热血青年,抛其家,别其室,不辞其苦,不计其酬,矻矻于此——“国中青年 爱国之心已泯”之谬论,今可休矣!今日之胜,有赖壮志未已之诸前辈,无惧寒暑,行走奔波,劝说民众。今日之胜,乃数百万美利坚民众之胜,察其意,皆属踊跃 为国,观其行,处处谨严有序,足堪告慰二百年前开国之先贤——民有、民治、民享之政体,未尝动摇也!

This is your victory.

嗟夫!此实诸位之功也!

And I know you didn’t do this just to win an election. And I know you didn’t do it for me.You did it because you understand the enormity of the task that lies ahead. For even as we celebrate tonight, we know the challenges that tomorrow will bring are the greatest of our lifetime — two wars, a planet in peril, the worst financial crisis in a century.

余知诸君之意非在此一选举,亦非在鄙人一身。盖瞻前路之艰辛,益知此任非同小可也。虽今夕欢贺于此,而明朝酒醒,大患仍自当前,不容有怠——两地烽烟熊熊而起,四海之内纷纷而乱,金融业界惶惶而不得宁。

Even as we stand here tonight, we know there are brave Americans waking up in the deserts of Iraq and the mountains of Afghanistan to risk their lives for us.There are mothers and fathers who will lie awake after the children fall asleep and wonder how they’ll make the mortgage or pay their doctors’ bills or save enough for their child’s college education. There’s new energy to harness, new jobs to be created, new schools to build, and threats to meet, alliances to repair.

是夜,饮宴笙歌之声不绝于耳,而异邦大漠群山中,吾国大好青年,兀自苦戍边塞,惝恍竟夜,性 命尚未得安。吾国千万庶民,为人父母者,兀自惴惴难眠,所忧者,乃房宅所贷、病患之费、抚育之资也。至若吾国能源之耗,百业之兴,痒序之教,攻伐之术,怀 远之道,亦皆吾等忡忡挂怀者也。

The road ahead will be long. Our climb will be steep. We may not get there in one year or even in one term. But, America, I have never been more hopeful than I am tonight that we will get there. I promise you, we as a people will get there.

渺渺乎其远,如不可达,危危乎其高,若不可攀。朝夕岁月,焉得成就?余不揣愚钝,愿以四载韶华,付诸此业,胜算何如虽不可知,然昂扬必胜之奇志,成就伟业之壮怀,平生未之有也。君子一诺,其重何如,此地今夕,愿斗胆发一狂言——吾辈既在,其事必成!

There will be setbacks and false starts. There are many who won’t agree with every decision or policy I make as president. And we know the government can’t solve every problem. But I will always be honest with you about the challenges we face. I will listen to you, especially when we disagree. And, above all, I will ask you to join in the work of remaking this nation, the only way it’s been done in America for 221 years — block by block, brick by brick, calloused hand by calloused hand.

逶迤坎坷,份内之事。异见争端,料必有之。国中之政府,谅非无所不能者。余所秉承不移者,唯 忠信矣。倘有危难于前,必无欺瞒于世。诸君言论臧否,纵悖逆相左之议,余必当洗耳以聆。于此之外,更当恳请诸君,不吝心血,致力报效,以振吾美利坚重兴之 业。余亦别无他想,唯盼吾侪协力,延继吾国既肇二百二十一年之大统,汇涓滴之力,而成万世之业。

What began 21 months ago in the depths of winter cannot end on this autumn night. This victory alone is not the change we seek. It is only the chance for us to make that change. And that cannot happen if we go back to the way things were. It can’t happen without you, without a new spirit of service, a new spirit of sacrifice.

昔年冬日,余有志于斯,投身此业,屈指算来,倏然近二载矣。当此秋夜,追思反省,仍无溃退逃 亡之意。选战之胜,无非一役之功,余梦寐所思矢志所求者,非在乎此。溯源究本,此役之胜,不过革世变时一大好良机耳。倘止步于斯,垂手而待,或无诸君倾力 相援,则壮志丰功,无非泡影,诸般梦想,终必虚妄。

So let us summon a new spirit of patriotism, of responsibility, where each of us resolves to pitch in and work harder and look after not only ourselves but each other. Let us remember that, if this financial crisis taught us anything, it’s that we cannot have a thriving Wall Street while Main Street suffers. In this country, we rise or fall as one nation, as one people.

爱国之心,报国之念,吾人固有之,然逢今日之世,此心此念亦当一变——吾辈各执己业,益当各竭其力,各尽其命,非但为一己之利,而更期普世之荣。今岁,金融业界动荡多舛,细审观之,当可以之为鉴——实业之损,亦是金融之伤。可知,既在邦域之内,吾辈荣辱休戚,皆相与共矣!

Let’s resist the temptation to fall back on the same partisanship and pettiness and immaturity that has poisoned our politics for so long. Let’s remember that it was a man from this state who first carried the banner of the Republican Party to the White House, a party founded on the values of self-reliance and individual liberty and national unity.Those are values that we all share.

党争纷纭,阴谋卑鄙,愚鲁无知,皆腐蚀清白、惑乱政局之弊也,其缘由已久,余今愿与诸君协力,共灭除之。昔年曾有此郡先贤,执共和党之帜,而掌总统府之权。自强独立,自由统一等信念,皆斯人之所倡,亦吾辈之所宗。

And while the Democratic Party has won a great victory tonight, we do so with a measure of humility and determination to heal the divides that have held back our progress. As Lincoln said to a nation far more divided than ours, we are not enemies but friends. Though passion may have strained, it must not break our bonds of affection.

今岁选战,吾民主党人幸有一胜,然谦逊和合之心未尝少减。余素信服者,乃山河破碎之际,林肯总统之言——“既是至亲,终不为敌。虽弩张剑拔,而血脉未尝断,情义不少减。”

And to those Americans whose support I have yet to earn, I may not have won your vote tonight, but I hear your voices. I need your help. And I will be your president, too.

固然,仍有四方志士,不为鄙人所动,另有高明之选。虽终悭此一票之缘,然诸君高论,余亦声声在耳,字字在心。倘能得诸君之援手,鄙人幸甚。他日待余总而统之,亦必不另眼以待也。

And to all those watching tonight from beyond our shores, from parliaments and palaces, to those who are huddled around radios in the forgotten corners of the world, our stories are singular, but our destiny is shared, and a new dawn of American leadership is at hand.

吾邦民众,散居天下,各安其命,而其志一也。吾邦鼎盛之势,今已乍现锋芒。

To those — to those who would tear the world down: We will defeat you. To those who seek peace and security: We support you. And to all those who have wondered if America’s beacon still burns as bright: Tonight we proved once more that the true strength of our nation comes not from the might of our arms or the scale of our wealth, but from the enduring power of our ideals: democracy, liberty, opportunity and unyielding hope.

至于心怀叵测,与世人为仇、与天下为敌者,吾邦猛志常在,彼等必取灭亡。心思纯良,久慕大同 者,吾辈当倾力以助,鼎力相援。犹疑未定,不知吾自由之邦兴衰如何者,吾辈愿以今日盛况以告之——美利坚之所以谓之“美”者,非刀兵之强,金银之众,实民 主、自由、机遇、梦想之美也!

That’s the true genius of America: that America can change. Our union can be perfected. What we’ve already achieved gives us hope for what we can and must achieve tomorrow.

天自有道,地自有德,恩赋吾邦无上异禀——无他,唯变而已矣。美利坚变革不怠,合众国日趋尽善。当以过往先贤之伟绩,助吾侪今日之雄心,开子孙万世之辉光。

This election had many firsts and many stories that will be told for generations. But one that’s on my mind tonight’s about a woman who cast her ballot in Atlanta. She’s a lot like the millions of others who stood in line to make their voice heard in this election except for one thing: Ann Nixon Cooper is 106 years old.

今岁选战,多开亘古之先,屡传千秋佳话。感我至深者,亚特兰大之老妪安?尼克松?库帕也——库氏之一票,于数百万美利坚民众之选票无异,其所以引人称奇者,其人今岁高龄一百有六矣。

She was born just a generation past slavery; a time when there were no cars on the road or planes in the sky; when someone like her couldn’t vote for two reasons — because she was a woman and because of the color of her skin.

当其父辈之时,天道不彰,黑人为奴。库氏其生也不逢时,汽车尚不行于道,飞机未曾起于空,库氏既属黑人,又系女流,票选一事,概无瓜葛。

And tonight, I think about all that she’s seen throughout her century in America — the heartache and the hope; the struggle and the progress; the times we were told that we can’t, and the people who pressed on with that American creed: Yes we can.

今日今时,回溯库氏百岁之涯,但见吾邦先贤屡败屡战,且退且进,悲欣交集,甘苦杂陈。幸而正道存焉,壮志存焉,曰:吾辈既在,无所不能。

At a time when women’s voices were silenced and their hopes dismissed, she lived to see them stand up and speak out and reach for the ballot. Yes we can.

万马齐喑,其事堪哀,吾邦女界怒而起,愤而争,苦战不歇,历数十载。幸哉库氏,以百岁之高龄,终得亲见女流自立于世,重获天赋之权——吾辈既在,无所不能!

When there was despair in the dust bowl and depression across the land, she saw a nation conquer fear itself with a New Deal, new jobs, a new sense of common purpose. Yes we can.

当百业萧条,国人绝望哀鸣之际,库氏亲见吾美利坚出旷世之新政,挽狂澜于既倒,扶大厦之将倾,退畏惧之势,扶奋勇之心,终至人各有位,民心乃安——吾辈既在,无所不能!

When the bombs fell on our harbor and tyranny threatened the world, she was there to witness a generation rise to greatness and a democracy was saved. Yes we can.

当吾国良港遭袭,天下桀纣当道,暴政肆虐之时,库氏亲见豪杰群起,民主不衰——吾辈既在,无所不能!

She was there for the buses in Montgomery, the hoses in Birmingham, a bridge in Selma, and a preacher from Atlanta who told a people that “We Shall Overcome.” Yes we can.

蒙哥马利公车之罢辍,伯明翰城黑人之群起,塞尔玛城血雨腥风之事,库氏般般亲历。更曾亲聆亚特兰大传教之士振臂登高之呼——“吾等必胜!”诚哉斯言!吾辈既在,无所不能!

A man touched down on the moon, a wall came down in Berlin, a world was connected by our own science and imagination. And this year, in this election, she touched her finger to a screen, and cast her vote, because after 106 years in America, through the best of times and the darkest of hours, she knows how America can change. Yes we can.

俟科学昌明于世,创想通贯一时,既登广寒之阙,又溃柏林之墙。洋洋乎!有百年如是,乃见今岁选战中,库氏之一票。浩浩兮!一百零六载交锋更迭,方有美利坚今日之变革——吾辈既在,无所不能!

America, we have come so far. We have seen so much. But there is so much more to do. So tonight, let us ask ourselves — if our children should live to see the next century; if my daughters should be so lucky to live as long as Ann Nixon Cooper, what change will they see? What progress will we have made?

转眼兴亡过手,而今迈步从头。追昔抚今,不禁扪心而问——俟再历百年岁月,倘吾等后辈儿孙,亦有得享高寿如库氏者,复可见何等之变数?吾辈今日之功,他年可得而见之乎?

This is our chance to answer that call. This is our moment. This is our time, to put our people back to work and open doors of opportunity for our kids; to restore prosperity and promote the cause of peace; to reclaim the American dream and reaffirm that fundamental truth, that, out of many, we are one; that while we breathe, we hope. And where we are met with cynicism and doubts and those who tell us that we can’t, we will respond with that timeless creed that sums up the spirit of a people: Yes, we can.

所谓天命时运,莫过于此——当为吾邦万民造安身立命之业,为吾辈儿孙启各显雄才之门,为寰宇 各国创太平静好之世,为吾等壮志赋千秋不灭之元神。吾邦立国之本,必将光耀于天下。万千同胞,当如一人,一息尚存,梦想不灭。纵有世人旁观在侧,而疑窦生 焉,吾辈亦当以千秋不易之训共答之曰——吾辈既在,无所不能!

Thank you. God bless you. And may God bless the United States of America.

拜谢诸君。愿天佑吾民,天佑吾邦。

2008年11月14日星期五

Jokes From RFCs

一向是严肃的Internet标准制定者RFC也会开玩笑,事实上在数千的RFC文档里不缺乏恶搞式的幽默,似乎从1989年开始,每年的愚人节都会出台一个玩笑性质的RFC文档,而且有些玩笑(RFC 1149, 2322)事实上被人实现或者投入使用:
RfC 527 (1973)
ARPAWOCKY (看起来是一首诗?)

RfC 748 (1978)
Telnet Randomly-lose option (Telnet协议的随机丢失选项(估计作者是在抱怨当时网络丢包吧))

RfC 1097 (1989)
Telnet subliminal-message option (Telnet潜在信息选项?)

RfC 1149 (1990)
Standard for the transmission of IP datagrams on avian carriers
这个搞笑性很高:使用信鸽(或者其他鸟类)传输IP数据包的标准
而且这个RFC有好事者实现了,还不止一次:
The first implementation was B2P (back to Pigeons) April 2001 in Bergen, Norway. 用信鸽传递纸条,然后再OCR扫描进去的实现。
A later implementation (FiFly, March 2004, Israel) reached 2,27Mbit/s data transfer rate (very high latency, though) .用信鸽传递闪存芯片的实现
And a later one (April 2005) even higher throughput with adhesive surface-mounted carriers (SNAP = SNAil-based data transfer Protocol, 37 Mbit/s).这个是很囧的用蜗牛拉光盘小车的实现(谁说蜗牛就一定慢,以后不许抱怨网速和蜗牛一样慢)
下面的就不一个一个翻译了……太多了,造几个有代表性的翻译:

RfC 1216 (1991)
Gigabit network economics and paradigm shifts

RfC 1217 (1991)
Memo from the Consortium for Slow Commotion Research

RfC 1313 (1992)
Today's Programming for KRFC AM 1313 Internet Talk Radio

RfC 1437 (1993)
The Extension of MIME Content-Types to a New Medium

RfC 1438 (1993)
Internet Engineering Task Force Statements Of Boredom (SOBs)

RfC 1605 (1994)
SONET to Sonnet Translation

RfC 1606 (1994)
A Historical Perspective On The Usage Of IP Version 9 传说中的IPv9就是这个啦~天朝的科学研究,有兴趣的Google

RfC 1607 (1994)
A view from the 21st century

RfC 1776 (1995)
The Address is the Message

RfC 1882 (1995)
The 12-Days of Technology Before Christmas

RfC 1924 (1996)
A Compact Representation of IPv6 Addresses 吧ipv6地址压缩成Base85编码

RfC 1925 (1996)
The Twelve Networking Truths

RfC 1926 (1996)
An Experimental Encapsulation of IP Datagrams on Top of ATM IP包通过声音传输,嗯,通讯基本靠吼就是这个了

RfC 1927 (1996)
Suggested Additional MIME Types for Associating Documents

RfC 2100 (1997)
The Naming of Hosts

RfC 2321 (1998)
RITA -- The Reliable Internetwork Troubleshooting Agent

RfC 2322 (1998)
Management of IP numbers by peg-dhcp
RfC 2322 actually is being used successfully at hacking camps like HIP (Hacking in Progress) or the CCCamp. In a different area it is widely (and successfully) used: to handle frequency usage at clubs for RC model planes (cars, ships, ...)

RfC 2323 (1998)
IETF Identification and Security Guidelines

RfC 2324 (1998)
Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0) 超文本咖啡壶控制协议

RfC 2325 (1998)
Definitions of Managed Objects for Drip-Type Heated Beverage Hardware Devices using SMIv2

RfC 2549 (1999)
IP over Avian Carriers with Quality of Service (支持QOS的IP over 信鸽协议- -||)

RfC 2550 (1999)
Y10K and Beyond 有两千年问题,自然也有一万年问题

RfC 2551 (1999)
The Roman Standards Process -- Revision III

RfC 2795 (2000)
The Infinite Monkey Protocol Suite (IMPS)
   This memo describes a protocol suite which supports an infinite
number of monkeys that sit at an infinite number of typewriters in
order to determine when they have either produced the entire works of
William Shakespeare or a good television show. The suite includes
communications and control protocols for monkeys and the
organizations that interact with them.
描述了一个支持无限只猴子坐在无限台打字机前以测试他们能否打出整部莎士比亚的作品或者是肥皂剧本的协议

RfC 3091 (2001)
Pi Digit Generation Protocol(圆周率生成协议,他们还能想出更加没用的浪费电能的方式么?)

RfC 3092 (2001)
Etymology of 'Foo' Foobar的来源(不是那个音乐播放器啦)

RfC 3093 (2001)
Firewall Enhancement Protocol (FEP)

RfC 3251 (2002)
Electricity over IP - Mostly Pointless Lamp Switching (MPLampS)
通过IP协议传送电能

RfC 3252 (2002)
Binary Lexical Octet Ad-hoc Transport (BLOAT)

RfC 3514 (2003)
The Security Flag in the IPv4 Header

RfC 3751 (2004)
Omniscience Protocol Requirements

RfC 4041 (2005)
Requirements for Morality Sections in Routing Area Drafts

RfC 4042 (2005)
UTF-9 and UTF-18
UTF-8不够用咯

RfC 4144 (2005)
How to Gain Prominence and Influence in Standards Organizations
(not sure about this one - real, valid content but not feeling RfC-ish)

RfC 4824 (2007)
The Transmission of IP Datagrams over the Semaphore Flag Signaling System (SFSS)
这次不用信鸽了,用旗语了么?那么我们是不是还能发明个用狼烟的?

RfC 5241 (2008)
Naming Rights in IETF Protocols

2008年11月6日星期四

C++表达式计算

又是给某人写的作业...

#include
#include
#include
#include
#include
#include
#include
using namespace std;
//栈内优先级
map isp;
//入栈优先级
map icp;
//分离表达式
list Tokenize(const string& str,const string& delimiters){
list tokens;
string::size_type delimPos = 0, tokenPos = 0, pos = 0;
if(str.length()<1) return tokens;
while(1){
delimPos = str.find_first_of(delimiters, pos);
tokenPos = str.find_first_not_of(delimiters, pos);
if(string::npos != delimPos){
if(string::npos != tokenPos){
if(tokenPos tokens.push_back(str.substr(pos,delimPos-pos));
}else{
tokens.push_back("");
}
}else{
tokens.push_back("");
}
pos = delimPos+1;
} else {
if(string::npos != tokenPos){
tokens.push_back(str.substr(pos));
} else {
tokens.push_back("");
}
break;
}
}
return tokens;
}
inline bool isNumber(char c){
return c=='.'||'0'<=c&&c<='9';
}
inline bool isSymbol(string op) {
return op=="+"||op=="-"||op=="*"||op=="/"||op=="%"||op=="^"||op=="("||op==")";
}
inline bool isSpace(char c){
return c==' '||c=='\t'||c=='\n'||c=='\r';
}
//格式化&分离表达式
list toExpressionArray(string exp){
string fexp;
const char* data = exp.data();
int idx = 0;
int len = exp.length();
for(;idx char c = data[idx];
if(isNumber(c)){
fexp.push_back(c);
}
else if(!isSpace(c)){
fexp.push_back(' ');
fexp.push_back(c);
fexp.push_back(' ');
}
}
return Tokenize(fexp," ");
}

//中序换前序
list m2l(list m) {
list l;
stack s;
string op;
string y;
s.push("#");
for(list::const_iterator i = m.begin();i!=m.end();i++){
op = *i;
if(op=="")
continue;
if(!isSymbol(op)){
l.push_back(op);
}else{
if(icp[op]>isp[s.top()]){
s.push(op);
}else if(op==")"){
y = s.top();
s.pop();
while(y!="("){
l.push_back(y);
y = s.top();
s.pop();
}
}else{
y = s.top();
s.pop();
while(icp[op] l.push_back(y);
y = s.top();
s.pop();
}
s.push(y);
s.push(op);
}//if2
}//if1
}//for
while(s.top()!="#"){
l.push_back(s.top());
s.pop();
}
return l;
}
//计算
double _calc(double o1, double o2, char op) {
switch(op){
case '+':{
return o1+o2;
}
case '-':{
return o1-o2;
}
case '*':{
return o1*o2;
}
case '/':{
return o1/o2;
}
case '^':{
return pow(o1,o2);
}
}
return 0;
}
//计算前序表达式
double calc(list in){
stack sk;
for(list::const_iterator i = in.begin();i!=in.end();i++){
string op = *i;
if(!isSymbol(op)){
sk.push(atof(op.data()));
}else{
double o2 = sk.top();
sk.pop();
double o1 = sk.top();
sk.pop();
char o = op.data()[0];
double r = _calc(o1,o2,o);
sk.push(r);
}
}
return sk.top();
}
//没用
void printList(list l){
int x=0;
for(list::const_iterator i = l.begin();i!=l.end();i++){
cout<<++x<<":"<<*i< }
}
int main()
{
isp["+"]=3;
isp["-"]=3;
isp["*"]=5;
isp["/"]=5;
isp["%"]=5;
isp["^"]=5;
isp["("]=1;
isp[")"]=6;
isp["#"]=0;

icp["+"]=2;
icp["-"]=2;
icp["*"]=4;
icp["/"]=4;
icp["%"]=4;
icp["^"]=4;
icp["("]=6;
icp[")"]=1;
icp["#"]=0;

string exp;
cout<<"Exp:";
cin>>exp;
list l1 = toExpressionArray(exp);
printList(l1);
list l2 = m2l(l1);
printList(l2);
double r = calc(l2);
cout<<"Result:"< return 0;
}

2008年11月4日星期二

超长字段数据库删除重复记录

今天接到一个CASE,在遥远的德国有一个1.1GB的MYSQL数据库,里面只有一个表,表里记录了若干很大的text字段和Blob字段,要求把某个text字段的重复项删除。(表里有上百万的记录,需要排除重复的字段平均大小在1K左右,带索引)
表结构如下:

DROP TABLE IF EXISTS `cert_db`.`certs`;
CREATE TABLE `cert_db`.`certs` (
`id` int(10) unsigned NOT NULL auto_increment,
`ServerName` varchar(128) default NULL,
`SerialNumber` blob NOT NULL,
`Version` int(10) unsigned NOT NULL,
`NotBefore` datetime NOT NULL,
`NotAfter` datetime NOT NULL,
`CertData` blob NOT NULL,
`IPK_DUMP` blob,
`IPK_N` blob,
`IPK_E` blob,
`Signature` blob NOT NULL,
`SigAlgName` varchar(50) default NULL,
`SigAlgOID` varchar(50) default NULL,
`SigAlgParams` blob,
`SubjectDN` text,
`IssuerDN` text,
`CertDump` text,
`SPK_DUMP` blob,
`SPK_N` blob,
`SPK_E` blob,
`ReallyBAD` char(1) default NULL,
`X509Dump` blob NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_CERTDN` USING HASH (`SubjectDN`(383))
) ENGINE=InnoDB AUTO_INCREMENT=4515 DEFAULT CHARSET=gbk;

第一次尝试使用如下语句:

delete from certs
where subjectdn in (
select subjectdn from certs
group by
subjectdn
having
count(subjectdn) > 1
)
and id not in (
select min(id) from certs
group by
certs
having
count(certs)>1
)

结果在本地的4000条记录上测试时就华丽的让MYSQL 挂掉了。。
然后使用md5进行索引,分步骤删除,成功:

#hash all
DROP TABLE IF EXISTS hash_all;
create table hash_all(
select id,md5(subjectdn) as dnhash from certs
);
#find dupclited
DROP TABLE IF EXISTS hash_dup;
create table hash_dup(
select id,dnhash from hash_all
group by
dnhash
having
count(dnhash)>1
);
#list rows to delete
DROP TABLE IF EXISTS hash_del;
create table hash_del(
select h.id,h.dnhash from hash_all h,hash_dup d
where
h.id!=d.id
and
h.dnhash=d.dnhash
);
#delete
delete c from certs c,hash_del d where c.id=d.id;
#then clean up
DROP TABLE IF EXISTS hash_all;
DROP TABLE IF EXISTS hash_dup;
DROP TABLE IF EXISTS hash_del;

2008年10月8日星期三

性能敏感的环境下用日志要小心

昨天用Profiler跑CPU负载时发现某个函数占用的CPU特别的多……但是函数本身没有什么特别之处,只是调用十分频繁(但是不止这一个函数调用频繁),最后吧焦点锁定到了日志记录上(好像经常跟上面栽跟头啊)
本来是用String.format格式化了一个字符串,然后再传到logger里面的,但是这个logger已经被关掉了。所以问题出在String.format上,屏蔽掉这个东西以后果然CPU占用立刻减少。
但是日志怎么办呢,Log4j的logger提供了isXXXEnabled方法,外面加个if就搞定了

2008年10月4日星期六

还是JFC,还是实时曲线图,就是加入了一个封装。




/*
* Copyrights (C) 2008 Bearice (Bearice@Gmail.com)
* Release under GNU/GPL Version 2.
*/

package cn.bearice.ipcontroller.manager.graph;

/**
*
* @author Bearice
*/
public interface RealTimeGraphDataSouce {
public Number getDate();
}


/*
* RealTimeGraphPanel.java
*
* Created on 2008年10月1日, 下午2:04
*/
package cn.bearice.ipcontroller.manager.graph;

import cn.bearice.java.util.StopableThread;
import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.RegularTimePeriod;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;

/**
*
* @author Bearice
*/
public class RealTimeGraphPanel extends javax.swing.JPanel {

private static final long serialVersionUID = 8250077948583513551L;
private TimeSeriesCollection timeSeriesCollection = new TimeSeriesCollection();
private JFreeChart jFreeChart = ChartFactory.createTimeSeriesChart(null, null, null, timeSeriesCollection, true, true, false);
private ChartPanel chartPanel = new ChartPanel(jFreeChart);
private XYPlot xyplot = jFreeChart.getXYPlot();
private LinkedHashMap tss = new LinkedHashMap();
private Class timeClass = Millisecond.class;
private long updateRate = 1000;
private long fixedDisplayRange = 50000;
private int maxSimpleCount = 10000;
private long maxSimpleAge = Long.MAX_VALUE;

public int getMaxSimpleCount() {
return maxSimpleCount;
}

public synchronized void setMaxSimpleCount(int maxSimpleCount) {
int old = this.maxSimpleCount;
this.fixedDisplayRange = maxSimpleCount;
Collection t = new ArrayList(tss.values());
for (TimeSeries timeSeries : t) {
timeSeries.setMaximumItemCount(maxSimpleCount);
}
firePropertyChange("maxSimpleCount", old, maxSimpleCount);
}

public long getMaxSimpleAge() {
return maxSimpleAge;
}

public synchronized void setMaxSimpleAge(long maxSimpleAge) {
long old = this.maxSimpleAge;
this.maxSimpleAge = maxSimpleAge;
Collection t = new ArrayList(tss.values());
for (TimeSeries timeSeries : t) {
timeSeries.setMaximumItemAge(maxSimpleAge);
}
firePropertyChange("maxSimpleAge", old, maxSimpleAge);
}

public long getFixedDisplayRange() {
return fixedDisplayRange;
}

public synchronized void setFixedDisplayRange(long fixedDisplayRange) {
long old = this.fixedDisplayRange;
this.fixedDisplayRange = fixedDisplayRange;
if (jrdoFixed.isSelected()) {
jrdoFixedActionPerformed(null);
}
firePropertyChange("fixedDisplayRange", old, fixedDisplayRange);
}

public long getUpdateRate() {
return updateRate;
}

public synchronized void setUpdateRate(long updateRate) {
long old = this.updateRate;
this.updateRate = updateRate;
if (t1 != null) {
t1.interrupt();
}
firePropertyChange("updateRate", old, updateRate);
}

public void setTitle(String text) {
jFreeChart.setTitle(text);
}

public TextTitle getTitle() {
return jFreeChart.getTitle();
}

public void setTitle(TextTitle title) {
jFreeChart.setTitle(title);
}

public Class getTimeClass() {
return timeClass;
}

protected void setTimeClass(Class timeClass) {
this.timeClass = timeClass;
}

public String getXLable() {
return xyplot.getDomainAxis().getLabel();
}

public void setXLable(String xLable) {
xyplot.getDomainAxis().setLabel(xLable);
}

public String getYLable() {
return xyplot.getRangeAxis().getLabel();
}

public void setYLable(String yLable) {
xyplot.getRangeAxis().setLabel(yLable);
}

public RealTimeGraphPanel() {
}

public RealTimeGraphPanel(Class timeClass) {
setTimeClass(timeClass);
}

public RealTimeGraphPanel(String title, String xLable, String yLable) {
setTitle(title);
setXLable(xLable);
setYLable(yLable);
init();
}

/** Creates new form RealTimeGraphPanel */
public RealTimeGraphPanel(Class timeClass, String title, String xLable, String yLable) {
this(timeClass);
setTitle(title);
setXLable(xLable);
setYLable(yLable);
init();
}

public synchronized void init() {
ValueAxis valueaxis = xyplot.getDomainAxis();
valueaxis.setAutoRange(true);
valueaxis = xyplot.getRangeAxis();
valueaxis.setAutoRange(true);
initComponents();
}

public synchronized void addTimeSeries(RealTimeGraphDataSouce ds, String name) {
TimeSeries timeSeries = new TimeSeries(name, timeClass);
timeSeries.setMaximumItemCount(maxSimpleCount);
timeSeries.setMaximumItemAge(maxSimpleAge);
timeSeriesCollection.addSeries(timeSeries);
tss.put(ds, timeSeries);
}

public static void main(String[] args) {
JFrame frame = new JFrame("Test Chart");
final RealTimeGraphPanel rtgp = new RealTimeGraphPanel(Second.class, "Triangle Functions", "Time", "Value");
rtgp.addTimeSeries(new RealTimeGraphDataSouce() {

int i;

public Number getDate() {
return Math.sin(i++ * 0.01);
}
}, "Sin(t)");
rtgp.addTimeSeries(new RealTimeGraphDataSouce() {

int i;

public Number getDate() {
return Math.cos(i++ * 0.01);
}
}, "Cos(t)");
rtgp.addTimeSeries(new RealTimeGraphDataSouce() {

int i;

public Number getDate() {
double d = Math.cos(i * 0.01) + Math.sin(i * 0.01);
i++;
return d;
}
}, "Cos(t)+Sin(t)");
rtgp.addTimeSeries(new RealTimeGraphDataSouce() {

int i;

public Number getDate() {
double d = Math.cosh(i * 0.01);
i++;
return d;
}
}, "cosh(t)");
rtgp.setUpdateRate(20);
rtgp.setTimeClass(Millisecond.class);
frame.getContentPane().add(rtgp, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {

@Override
public void windowClosing(WindowEvent windowevent) {
System.exit(0);
}
});
}

/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
//
private void initComponents() {
bindingGroup = new org.jdesktop.beansbinding.BindingGroup();

buttonGroup1 = new javax.swing.ButtonGroup();
jPanel1 = (chartPanel);
jScrollPane1 = new javax.swing.JScrollPane();
jPanel2 = new javax.swing.JPanel();
jLabel3 = new javax.swing.JLabel();
jSpinner2 = new javax.swing.JSpinner();
jLabel4 = new javax.swing.JLabel();
jLabel1 = new javax.swing.JLabel();
jSpinner1 = new javax.swing.JSpinner();
jLabel2 = new javax.swing.JLabel();
jbtnStart = new javax.swing.JButton();
jbtnStop = new javax.swing.JButton();
jbtnClear = new javax.swing.JButton();
jrdoScaled = new javax.swing.JRadioButton();
jrdoFixed = new javax.swing.JRadioButton();
jLabel6 = new javax.swing.JLabel();
jLabel5 = new javax.swing.JLabel();
jSpinner4 = new javax.swing.JSpinner();
jSpinner3 = new javax.swing.JSpinner();

setName("Form"); // NOI18N

jPanel1.setName("jPanel1"); // NOI18N

javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 460, Short.MAX_VALUE)
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 193, Short.MAX_VALUE)
);

jScrollPane1.setBorder(javax.swing.BorderFactory.createTitledBorder("Options"));
jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
jScrollPane1.setName("jScrollPane1"); // NOI18N

jPanel2.setName("jPanel2"); // NOI18N

jLabel3.setText("Update rate:");
jLabel3.setName("jLabel3"); // NOI18N

jSpinner2.setModel(new javax.swing.SpinnerNumberModel(Long.valueOf(0L), Long.valueOf(0L), null, Long.valueOf(50L)));
jSpinner2.setName("jSpinner2"); // NOI18N

org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${updateRate}"), jSpinner2, org.jdesktop.beansbinding.BeanProperty.create("value"));
bindingGroup.addBinding(binding);

jLabel4.setText("ms");
jLabel4.setName("jLabel4"); // NOI18N

jLabel1.setText("Range:");
jLabel1.setName("jLabel1"); // NOI18N

binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, jrdoFixed, org.jdesktop.beansbinding.ELProperty.create("${selected}"), jLabel1, org.jdesktop.beansbinding.BeanProperty.create("enabled"));
bindingGroup.addBinding(binding);

jSpinner1.setModel(new javax.swing.SpinnerNumberModel(Long.valueOf(0L), Long.valueOf(0L), null, Long.valueOf(500L)));
jSpinner1.setName("jSpinner1"); // NOI18N

binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${fixedDisplayRange}"), jSpinner1, org.jdesktop.beansbinding.BeanProperty.create("value"));
bindingGroup.addBinding(binding);
binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, jrdoFixed, org.jdesktop.beansbinding.ELProperty.create("${selected}"), jSpinner1, org.jdesktop.beansbinding.BeanProperty.create("enabled"));
bindingGroup.addBinding(binding);

jLabel2.setText("ms");
jLabel2.setName("jLabel2"); // NOI18N

binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, jrdoFixed, org.jdesktop.beansbinding.ELProperty.create("${selected}"), jLabel2, org.jdesktop.beansbinding.BeanProperty.create("enabled"));
bindingGroup.addBinding(binding);

jbtnStart.setText("Start");
jbtnStart.setName("jbtnStart"); // NOI18N
jbtnStart.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnStartActionPerformed(evt);
}
});

jbtnStop.setText("Stop");
jbtnStop.setEnabled(false);
jbtnStop.setName("jbtnStop"); // NOI18N
jbtnStop.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnStopActionPerformed(evt);
}
});

jbtnClear.setText("Clear");
jbtnClear.setName("jbtnClear"); // NOI18N

binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, jbtnStop, org.jdesktop.beansbinding.ELProperty.create("${enabled}"), jbtnClear, org.jdesktop.beansbinding.BeanProperty.create("enabled"));
bindingGroup.addBinding(binding);

jbtnClear.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnClearActionPerformed(evt);
}
});

buttonGroup1.add(jrdoScaled);
jrdoScaled.setSelected(true);
jrdoScaled.setText("Scaled");
jrdoScaled.setName("jrdoScaled"); // NOI18N
jrdoScaled.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jrdoScaledActionPerformed(evt);
}
});

buttonGroup1.add(jrdoFixed);
jrdoFixed.setText("Fixed");
jrdoFixed.setName("jrdoFixed"); // NOI18N
jrdoFixed.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jrdoFixedActionPerformed(evt);
}
});

jLabel6.setText("MSA");
jLabel6.setToolTipText("Maxium Simple Time (ms)");
jLabel6.setName("jLabel6"); // NOI18N

jLabel5.setText("MSC");
jLabel5.setToolTipText("Maxium Simple Count");
jLabel5.setName("jLabel5"); // NOI18N

jSpinner4.setModel(new javax.swing.SpinnerNumberModel(Long.valueOf(0L), null, null, Long.valueOf(1L)));
jSpinner4.setName("jSpinner4"); // NOI18N

binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${maxSimpleAge}"), jSpinner4, org.jdesktop.beansbinding.BeanProperty.create("value"));
bindingGroup.addBinding(binding);

jSpinner3.setModel(new javax.swing.SpinnerNumberModel());
jSpinner3.setName("jSpinner3"); // NOI18N

binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${maxSimpleCount}"), jSpinner3, org.jdesktop.beansbinding.BeanProperty.create("value"));
bindingGroup.addBinding(binding);

javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
jPanel2.setLayout(jPanel2Layout);
jPanel2Layout.setHorizontalGroup(
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel2Layout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel2Layout.createSequentialGroup()
.addComponent(jLabel3)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSpinner2, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel4)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel2))
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel2Layout.createSequentialGroup()
.addComponent(jbtnStart)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jbtnStop)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jbtnClear)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jrdoScaled)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jrdoFixed)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addComponent(jLabel6, javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel5, javax.swing.GroupLayout.Alignment.LEADING))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(jSpinner4)
.addComponent(jSpinner3, javax.swing.GroupLayout.DEFAULT_SIZE, 73, Short.MAX_VALUE))
.addContainerGap(13, Short.MAX_VALUE))
);
jPanel2Layout.setVerticalGroup(
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel2Layout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel2Layout.createSequentialGroup()
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jbtnStart)
.addComponent(jbtnStop)
.addComponent(jbtnClear)
.addComponent(jrdoFixed)
.addComponent(jrdoScaled))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel3)
.addComponent(jSpinner2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel4)
.addComponent(jLabel1)
.addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel2)))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(jLabel5)
.addComponent(jSpinner3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel6)
.addComponent(jSpinner4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addContainerGap())
);

jScrollPane1.setViewportView(jPanel2);

javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 460, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
);

bindingGroup.bind();
}//


class RefThread extends StopableThread {

@Override
protected void loop() throws InterruptedException {
LinkedHashMap lhm = new LinkedHashMap(tss);
for (RealTimeGraphDataSouce ds : tss.keySet()) {
try {
Number val = ds.getDate();
TimeSeries ts = lhm.get(ds);
ts.addOrUpdate((RegularTimePeriod) timeClass.newInstance(), val);
} catch (Exception ex) {
Logger.getLogger(RealTimeGraphPanel.class.getName()).log(Level.SEVERE, null, ex);
}
}
Thread.sleep(updateRate);
}
}
RefThread t1;

public synchronized void start() {
t1 = new RefThread();
t1.start();
jbtnStart.setEnabled(false);
jbtnStop.setEnabled(true);
}

public synchronized void stop() {
t1.exit();
t1 = null;
jbtnStart.setEnabled(true);
jbtnStop.setEnabled(false);
}

public synchronized void clear() {
Collection t = new ArrayList(this.tss.values());
for (TimeSeries timeSeries : t) {
timeSeries.clear();
}
}
private void jbtnStartActionPerformed(java.awt.event.ActionEvent evt) {
start();
}

private void jbtnClearActionPerformed(java.awt.event.ActionEvent evt) {
clear();
}

private void jrdoFixedActionPerformed(java.awt.event.ActionEvent evt) {
xyplot.getDomainAxis().setFixedAutoRange(fixedDisplayRange);
}

private void jrdoScaledActionPerformed(java.awt.event.ActionEvent evt) {
xyplot.getDomainAxis().setFixedAutoRange(0);
}

private void jbtnStopActionPerformed(java.awt.event.ActionEvent evt) {
stop();
}
// Variables declaration - do not modify
private javax.swing.ButtonGroup buttonGroup1;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JLabel jLabel4;
private javax.swing.JLabel jLabel5;
private javax.swing.JLabel jLabel6;
private javax.swing.JPanel jPanel1;
private javax.swing.JPanel jPanel2;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JSpinner jSpinner1;
private javax.swing.JSpinner jSpinner2;
private javax.swing.JSpinner jSpinner3;
private javax.swing.JSpinner jSpinner4;
private javax.swing.JButton jbtnClear;
private javax.swing.JButton jbtnStart;
private javax.swing.JButton jbtnStop;
private javax.swing.JRadioButton jrdoFixed;
private javax.swing.JRadioButton jrdoScaled;
private org.jdesktop.beansbinding.BindingGroup bindingGroup;
// End of variables declaration
}

社论:某熊答记者问

几天前的事情了……不过一直登不上Blogger,所以今天才发。

darth ssword 00:30:40
您现在程序的规模大致是多少呢


IcyBear 00:31:40
当前模块大概300row
当前项目大概数K
整个系统应该上数W

darth ssword 00:32:12
天那
您是怎么做到的


IcyBear 00:33:09
一个字:磨
两个字:死抗



darth ssword 00:33:40
经典!
darth ssword 00:33:55
开发周期呢


IcyBear 00:35:24
别提了,八年了……


darth ssword 00:35:45
!!


IcyBear 00:36:02
整个东西就两个人,那个不用java- -
但是超过3/4是java实现- -


darth ssword 00:37:32
究竟是什么让您爆发出如此非凡的勇气和毅力?
darth ssword 00:37:58
什么系统丫


IcyBear 00:38:22
给学校写的网络控制系统,收费的

IcyBear 00:38:45
darth ssword 00:37:32
究竟是什么让您爆发出如此非凡的勇气和毅力?
因为据说有可能不一定会给¥¥


darth ssword 00:40:12
惊,我听说lieo骗到过3000+
darth ssword 00:40:25
您究竟。。。


IcyBear 00:41:15
ToT
国家三令五申,严禁拖欠程序员工资,但是学校竟然,他竟然……(晕过去,饿的)



darth ssword 00:42:02
同情+悲愤
darth ssword 00:43:05
控制如此巨大的项目,您一个人有什么经验和教训吗



IcyBear 00:44:00
文档对一个团队来说是重要的,对一个人来说仿佛就成了累赘



darth ssword 00:45:48
您是否认为您的程序中包含大量的设计模式


IcyBear 00:47:29
有关这个问题,我想这么回答:该用的用了,不该用的乱用了,需要用的地方想用但是不会用,不需要用的地方想不用但是不用就没发写了



darth ssword 00:48:57
您怎么看待uml


IcyBear 00:49:22
没用过……不知道,请下一位记者提问


darth ssword 00:50:02
您对版本控制有什么心得吗


IcyBear 00:50:42
有……有事没事经常commit一下绝对没坏处



darth ssword 00:51:40
在漫长的开发过程中,您是如何抗拒外来的诱惑的


IcyBear 00:52:26
诱惑?要是有诱惑我早TMD不干了(拍桌子)


darth ssword 00:54:34
那么,您除了编程之外的生活是如何度过的?例如学习啊,小妹啊,etc


IcyBear 00:56:51
我用几句名言回答您的提问:
1)六十分万岁,多一份浪费,少一分白费
2)我对3次元空间的MM不感兴趣,如果你们当中有宇宙人,未来人,超能力者,请来找我,以上。
谢谢


darth ssword 00:59:26
谢谢您的回答,您的回答让我很受启迪!


IcyBear 00:59:49
不用感谢,这次的记者招待会就到这里,大家晚安


darth ssword 01:00:27
(掌声)

2008年10月1日星期三

JFreeChart的实时曲线图的例子

好久没更新过技术相关的东西了,今天要开始写管理控制台,里面需要用到实时曲线图,决定使用JFreeChart做,这东西还真的挺好用,简单的几句代码就能搭建一个看起来不错的程序:-)
预览图:



/*
* RealTimeGraphPanel.java
*
* Created on 2008年10月1日, 下午2:04
*/
package cn.bearice.ipcontroller.manager.graph;

import cn.bearice.java.util.StopableThread;
import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;

/**
*
* @author Bearice
*/
public class RealTimeGraphPanel extends javax.swing.JPanel {

private static final long serialVersionUID = 8250077948583513551L;
TimeSeriesCollection tcl;
ChartPanel cp;
JFreeChart jfc;
ArrayList tss = new ArrayList();
TimeSeries tts = new TimeSeries("Avg.", Second.class);

/** Creates new form RealTimeGraphPanel */
public RealTimeGraphPanel() {
tcl = new TimeSeriesCollection();
tcl.addSeries(tts);
addTimeSeries();
jfc = ChartFactory.createTimeSeriesChart("Random Numbers", "Time", "Value", tcl, true, true, false);
XYPlot xyplot = jfc.getXYPlot();
ValueAxis valueaxis = xyplot.getDomainAxis();
valueaxis.setAutoRange(true);
// valueaxis.setInverted(true);
valueaxis = xyplot.getRangeAxis();
valueaxis.setAutoRange(true);
cp = new ChartPanel(jfc);
initComponents();
}

public void addTimeSeries() {
TimeSeries timeSeries = new TimeSeries("Random" + tss.size(), Second.class);
tcl.addSeries(timeSeries);
tss.add(timeSeries);
}

public static void main(String[] args) {
JFrame frame = new JFrame("Test Chart");
final RealTimeGraphPanel rtgp = new RealTimeGraphPanel();
frame.getContentPane().add(rtgp, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);

frame.addWindowListener(new WindowAdapter() {

@Override
public void windowClosing(WindowEvent windowevent) {
System.exit(0);
}
});
}

/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
//
private void initComponents() {

jbtnStartStop = new javax.swing.JButton();
jPanel1 = (cp);
jbtnFixed = new javax.swing.JButton();
jbtnScaled = new javax.swing.JButton();
jbtnClear = new javax.swing.JButton();
jbtnAdd = new javax.swing.JButton();

setName("Form"); // NOI18N

jbtnStartStop.setText("Start");
jbtnStartStop.setName("jbtnStartStop"); // NOI18N
jbtnStartStop.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnStartStopActionPerformed(evt);
}
});

jPanel1.setName("jPanel1"); // NOI18N

javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 261, Short.MAX_VALUE)
);

jbtnFixed.setText("Fixed");
jbtnFixed.setName("jbtnFixed"); // NOI18N
jbtnFixed.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnFixedActionPerformed(evt);
}
});

jbtnScaled.setText("Scaled");
jbtnScaled.setName("jbtnScaled"); // NOI18N
jbtnScaled.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnScaledActionPerformed(evt);
}
});

jbtnClear.setText("Clear");
jbtnClear.setName("jbtnClear"); // NOI18N
jbtnClear.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnClearActionPerformed(evt);
}
});

jbtnAdd.setText("Add");
jbtnAdd.setName("jbtnAdd"); // NOI18N
jbtnAdd.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnAddActionPerformed(evt);
}
});

javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jbtnStartStop)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jbtnFixed)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jbtnScaled)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jbtnClear)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jbtnAdd)
.addContainerGap(53, Short.MAX_VALUE))
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jbtnStartStop)
.addComponent(jbtnFixed)
.addComponent(jbtnScaled)
.addComponent(jbtnClear)
.addComponent(jbtnAdd))
.addContainerGap())
);
}//


class T1 extends StopableThread {

@Override
protected void loop() throws InterruptedException {
for (TimeSeries timeSeries : tss) {
double d = randomNum();
num = (num + d) / 2;
timeSeries.add(new Second(), d);
}
tts.add(new Second(), num);
Thread.sleep(1000);
}
double num;
Random rand = new Random();

private double randomNum() {
return rand.nextGaussian();
}
}
T1 t1;
private void jbtnStartStopActionPerformed(java.awt.event.ActionEvent evt) {
if (t1 == null) {
t1 = new T1();
t1.start();
jbtnStartStop.setText("stop");
} else {
t1.exit();
t1 = null;
jbtnStartStop.setText("start");
}
}

private void jbtnFixedActionPerformed(java.awt.event.ActionEvent evt) {
// cp.setAutoscrolls(true);
XYPlot xyplot = jfc.getXYPlot();
ValueAxis valueaxis = xyplot.getDomainAxis();
valueaxis.setFixedAutoRange(50000);
}

private void jbtnScaledActionPerformed(java.awt.event.ActionEvent evt) {
XYPlot xyplot = jfc.getXYPlot();
ValueAxis valueaxis = xyplot.getDomainAxis();
valueaxis.setFixedAutoRange(0);
}

private void jbtnClearActionPerformed(java.awt.event.ActionEvent evt) {
for (TimeSeries timeSeries : tss) {
timeSeries.clear();
}
}

private void jbtnAddActionPerformed(java.awt.event.ActionEvent evt) {
addTimeSeries();
}
// Variables declaration - do not modify
private javax.swing.JPanel jPanel1;
private javax.swing.JButton jbtnAdd;
private javax.swing.JButton jbtnClear;
private javax.swing.JButton jbtnFixed;
private javax.swing.JButton jbtnScaled;
private javax.swing.JButton jbtnStartStop;
// End of variables declaration
}

PS:里面用到了一个自己实现的StopableThread,代码如下:

/*
* Copyrights (C) 2008 Bearice (Bearice@Gmail.com)
* Release under GNU/GPL Version 2.
*/
package cn.bearice.java.util;

/**
* 可以停止的线程。
* @author Bearice
*/
public abstract class StopableThread extends Thread {

protected boolean exit = false;

/**
* 线程开始后,进入循环前调用。
*/
protected void before() {
//System.out.println("Starting thread: "+this);
}

/**
* 线程退出前,循环退出后调用。
*/
protected void after() {
// System.out.println("Ending thread: "+this);
}

/**
* 循环体代码。
* @throws java.lang.InterruptedException
*/
protected abstract void loop() throws InterruptedException;

/**
* 指示线程退出循环。
*/
public synchronized void exit() {
exit = true;
interrupt();
}

@Override
public final void run() {
before();
while (!exit) {
try {
loop();
} catch (InterruptedException ex) {
}
}
after();
}
}

2008年9月20日星期六

Windows 2008 STD激活可能

昨天得到的消息,Windows2008的激活已经可能了,原因是dell推出了OEM的2008
Windows 2008的激活机制其实和Vista是相同的,但是以前一直没有放出OEM的CD-KEY所以激活不能,但是DELL出了OEM系统以后就有OEM-KEY了,激活的步骤和Vista是一样的:导入证书,导入CDKEY,如果BIOS本身没有SLIC表就加上个mod
PS:发现2008其实就是vista的另一个mod版
PS2:现在用的系统是2008 Datacenter,没有流出OEM-key,所以本机测试不能,但是已经证实某同学已经成功激活。
PS3:OEM_KEY:223PV-8KCX6-F9KJX-3W2R7-BB2FH(FOR 2008 STD ONLY[x86 or x64])
PS4:看到有很多人是到这里找破解方法的,所以就把我用的那个脚本发一下,仅供品牌机用户或者刷过BIOS的用户使用。
这个脚本原本是激活Vista的,修改了一下,现在是2008和Vista通用的。
地址:http://www.rayfile.com/files/5c9068c2-91c2-11dd-9b64-0014221b798a/

Dr.COM宽带认证客户端修改版

最近换上了电信的数字化校园网络,需要用这个登陆器,但是这个登陆器实在是烂得可以,随便添加驱动,往系统目录里乱写东西就懒得追究他了,每次登陆都要弹出一个IE的框,十分麻烦,而且还总是提示重启。
终于忍受不了了,拿出ODBG,改之。
作用:屏蔽了登录后弹出IE窗口,屏蔽了自动重启的代码。
URL:http://www.rayfile.com/files/8b1e3bd9-86be-11dd-a1c1-0014221b798a/

2008年9月18日星期四

趁着Blogger赶快过来放个水

一直是Blogger后台登陆不能,今天忽然发现登录可能,赶快上来放个水,以防被和谐后来不及。

2008年9月15日星期一

中国电信,我恨你

更正:其实还是有些冤枉电信了,宿舍的网络设备是学校负责维护的,不是电信。
其实好坏也仅仅是相对的,这么骂也仅仅是为了出气而已。其实网络故障只是单纯的故障,任何物理故障都是可以修复的,而难修复的是垄断形成以后从业人员的心里。
在南方其实网通的服务是很不错的(可惜这里没有网通的线),而在北方电信的客服MM说话也是很甜的,经常需要南方北方来回跑的某熊对这一点深有体会。
希望反垄断法实施能产生一些真正的效果

最近被我们伟大的垄断企业:中国电信彻底的,完全的雷到了。
事件的起因是学校搞网络改革,把原先的校园网接入改为由电信负责的数字化校园宽带接入。广告说的好啊,每人一条网线独享×××的带宽。受够6人(这学期开 学以后是16+人)共享一条2MADSL(我们这栋楼连校园网都没)的某熊美滋滋的跑去办理了,每月60大洋,2M带宽。当时在营业厅问:是不是独立 IP,公网的,技术人员支支吾吾半天,一直重复是每人一条网线,重复问了好几遍是不是公网独立IP,才蹦出一句:大概是吧。就算这样总算也是办理了一个帐 号(排队的人还真多)。
等到了帐号开通以后发现事情完全不对头:首先,每个宿舍只有一条网线,还是原来剩下的(这栋楼以前布有网线,但是年代及其久远)不过这倒是无可厚非,用交 换机也是可以。然后设置自动捕获IP。发现所谓的公网IP完全是扯淡,得到的地址段是10.10.38.*,掩码255.255.254.0。好吧,是不 是公网IP也无所谓了(我强烈怀疑那个技术员知不知道什么是公网IP),只要网速快而且稳定就好。但是事实又一次证明了电信的扯淡性质:ping www.google.com 平均每10个数据包至少有2-3个是timeout,有2个的looptime>100ms,剩下的倒是不错,只有20ms,虽然不是很满意但还 行。但是使用一段时间以后发现稳定性极差,有时候根本无法连接到网络,检查了一下才发现,这栋楼的中央集线设备进入不是交换机而是集线器,也就是说:这栋 楼所有使用者发送的所有数据包,都会被完整的广播到其他机器上,而且由于线缆的质量极差,连接速度只有10Mbps(如果不是线的问题而是集线器的问题的 话,那电信就应该撞墙自杀算了,10M不带自适应的集线器怎么说也是2003年以前的古董了吧)用嗅探器抓包发现,网卡的空闲IO负荷已经接近理论最大值 了,也就是说我什么事都还没做,这条网线的带宽已经满了。
满了就满了,只要不影响我的2Mbps的额定网速我也没意见(虽然理论上已经不可能了),掉线,APR泛滥,广播风暴,什么事情都让我遇到了,最重要的 是,插上这条网线不久,我一直工作正常的TPLINK无线路由竟然宕机了(以前是ADSL的路由器,又挂载了一个8口的交换机,现在级联起来当交换机 用)。怪不得我们从电信那里出来的时候,那个人一直在说:要用集线器,要用集线器。感情用交换机还能把系统弄宕机了啊!
交换机宕机不要紧,大不了我们换集线器,可是这电脑蓝屏总该要紧了吧。这鸟网络使用必须安装一个登录软件,但是就是这个登录软件问题也是一大堆,不但用到 了winpcap(天知道用这个是不是用来窃取个人隐私的,不过就现在来看也不用这么专业的窃取了,反正所有数据包都是广播的),而且还要安装驱动(蓝屏 的根源啊,炉子应该最有感觉吧)。(幸亏前两天我换回了32位系统,不然估计连这个登陆器都运行不了),程序的逻辑上也是bug一堆,最主要的BUG是它 每次启动都要检测驱动,但是检测的又不完全,所以经常提示:驱动安装完成,是否重启,一不小心就要被他重启一回。
重启就重启吧,只是是软关机,对数据危险小一点(要是重启时有没保存的文档我就没办法了)但是那个驱动也继承了电信一贯的优良传统:蓝屏。现在某熊用的是 win2008系统,稳定性应该还是说的过去的,但是自打装上那个玩意儿就蓝屏不断。每天不蓝几次都不行。(其实有点夸张,因为我总共用了不到一天就丢掉 了,60块大了水漂,哦是70,还有初装费)
总之,这次是完完全全,彻彻底底的被电信忽悠了一次,我宣布,以后在我眼里,中国电信和中国足球同级。

2008年8月30日星期六

TTS的口音

前几天装了日文的TTS引擎,然后用不同的TTS引擎说同一段话,恩,很有地方风味呢
原文是这个,Spring的JavaDoc里随便找到

This interface is implemented by objects that hold a number of bean definitions, each uniquely identified by a String name.

声音文件在这里:
http://bbs.cfan.com.cn/attachment.php?aid=242995

行程确定

2号的火车……凌晨4点就要开始……算上中间倒车的时间,这次估计要超过36小时了= =
PS:值得一提的是,这次买到的车票是4车厢1号,话说前面3个车厢应该是邮车啥的……没准是一趟火车的第一个座位,lucky~

血的教训告诉我们:最好不要多线程共享一个数据库连接

今天遇到一个奇怪的问题,有一段程序,会被多个线程并行访问,因为没有涉及到数据库写操作,所以就偷懒共享了一个DAO,但是发现一旦并发线程超过2个,就会出现异常,而且还是很奇怪的异常,好像是说数据库没有响应。调试了半天没发现什么问题,后来把DAO对象设置成了ThreadLocal,问题消失了……看来用一个数据库连接最好不要被多个线程同时操作。
最近经常在多线程同步上出问题,上午的时候出现了一个锁死的问题:有一个调用在一个同步块里面锁死了(因为另一个线程异常退出了,没有给这个线程解锁)然后后面紧跟着所有线程都停在了同步块上。在Profiler里一片红红的,好不壮观。
话说今天这个锁死的解决多亏了jconsoler和jvisualvm,这两个工具是最新的几个JDK版本才提供的,他们提供了对Java应用程序的本地或远程的即使监视和分析的功能。jconsole可以把每个线程的运行状态和阻塞原因打印出来,配合jvisualvm的可视化线程图表就可以确定到底是那个(些)线程进行了锁死,并有针对性的进行修复。
再废话一句:这两个工具好像都是基于JMX的,所以说JMX真是个好物啊~~

牢骚:最近Blogger确实有问题

不知道是ISP的问题还是Blogger本身的问题……每次访问Blogger的控制台就被断开连接,症状和被墙了一样……郁闷。但是访问我的Blog域名却没什么问题……更加郁闷。
每次想写点什么东西的时候,被这个错误弄得没什么心情写了。牢骚完毕。

2008年8月24日星期日

JMX-RMI

最近发现JMX是个好东西,很适用于分布式系统的控制信息传递。

/*
 * Copyrights (C) 2008 Bearice (Bearice@Gmail.com)
 * Release under GNU/GPL Version 2.
 */
package jmxtest;

import java.rmi.registry.LocateRegistry;
import java.util.HashMap;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.remote.JMXAuthenticator;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.security.auth.Subject;

/**
 *
 * @author Bearice
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws Exception {
        LocateRegistry.createRegistry(8877);
        MBeanServer server = MBeanServerFactory.createMBeanServer();
        ObjectName helloName = new ObjectName("hello:name=hello");
        HelloWorld hello = new HelloWorld();
        HashMap prop = new HashMap();
        prop.put(JMXConnectorServer.AUTHENTICATOR, new JMXAuthenticator() {

            public Subject authenticate(Object credentials) {
                System.out.println(credentials);
                if (credentials instanceof String) {
                    if (credentials.equals("hello")) {
                        return new Subject();
                    }
                }
                throw new SecurityException("not authicated");
            }
        });
        JMXConnectorServer cserver =
                JMXConnectorServerFactory.newJMXConnectorServer(
                new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:8877/hello"), prop, server);
        cserver.start();
        server.registerMBean(hello, helloName);
        for (ObjectInstance object : server.queryMBeans(null, null)) {
            System.out.println(object.getObjectName());
        }
        System.out.println(hello);
        System.out.println("start.....");
    }
}

/*
 * Copyrights (C) 2008 Bearice (Bearice@Gmail.com)
 * Release under GNU/GPL Version 2.
 */
package jmxtest;

import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.JMX;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

/**
 *
 * @author Bearice
 */
public class Client {

    static HelloWorldMBean hello;

    public static void main(String[] args) throws Exception {
        final AtomicInteger count = new AtomicInteger();
        HashMap prop = new HashMap();
        prop.put(JMXConnector.CREDENTIALS, "hello");
        final JMXConnector conn = JMXConnectorFactory.connect(new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:8877/hello"), prop);
        conn.connect();
        Runnable r = new Runnable() {

            public void run() {
                for (int i = 0; i < 10; i++) {
                    try {
                        final HelloWorldMBean hello = JMX.newMBeanProxy(conn.getMBeanServerConnection(), new ObjectName("hello:name=hello"), HelloWorldMBean.class);
                        hello.setName("bearice");
                        hello.sayHello();
                        hello.getHelloString();
                        System.out.println(count.incrementAndGet());
                        Client.hello = hello;
                        System.out.println(hello.equals(hello.getThis()));
                    } catch (Exception ex) {
                        Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        };
        Thread[] ts = new Thread[5];
        for (int i = 0; i < ts.length; i++) {
            ts[i] = new Thread(r);
            ts[i].start();
        }
        for (Thread t : ts) {
            t.join();
        }
        System.out.println(hello.equals(hello.getThis()));
        System.out.println(hello.getId());
        System.out.println(hello);
    }
}

/*
 * Copyrights (C) 2008 Bearice (Bearice@Gmail.com)
 * Release under GNU/GPL Version 2.
 */
package jmxtest;

import java.io.Serializable;

/**
 *
 * @author Bearice
 */
public interface HelloWorldMBean extends Serializable {

    void setName(String name);

    String getName();

    void sayHello();

    String getHelloString();

    int getId();

    @Override
    String toString();

    @Override
    int hashCode();

    @Override
    boolean equals( Object obj);
    
    HelloWorldMBean getThis();
}
class HelloWorld implements HelloWorldMBean, Serializable {

    static AtomicInteger count = new AtomicInteger();
    private static final long serialVersionUID = 1627976932729278650L;
    int id = count.incrementAndGet();
    String name;

    public synchronized void setName(String name) {
        this.name = name;
    }

    public synchronized String getName() {
        return name;
    }

    public synchronized void sayHello() {
        System.out.println(getHelloString());
    }

    public synchronized String getHelloString() {
        return "Hello, " + name;
    }

    public synchronized int getId() {
        return id;
    }

    public HelloWorldMBean getThis() {
        return this;
    }
}

忙里偷闲

最近发现Blogger总是断开连接,难道又被墙了,现在是开着无界上来发东西,不然就会出Connection Reset。不知道是为什么。
早上听过一个笑话,说坐火车要检查身份证,有一个哥们没带。售票员就建议说:“你跟那个查身份证的说‘李[哔]志是个王[哔]蛋’他就让你过去了。”那人就这么做了,的确让他过去了。
顺便说一句,上面讲的笑话是一件真实发生的事情= =||

2008年8月19日星期二

[FWD]如何忽略防火长城

原文地址:http://www.yeeyan.com/articles/view/40085/11992
翻译: 万柳烈士旅 
简介
本文从TCP/IP协议安全性的角度对中国防火墙系统的工作原理、工作特性和潜在的漏洞、造成的问题进行了详尽的调查和分析,并从多个角度提出了针对特别是防火长城这类利用TCP/IP协议进行攻击的反制措施。

如何忽略防火长城

Richard Clayton, Steven J. Murdoch, and Robert N. M. Watson*
University of Cambridge, Computer Laboratory, William Gates Building, 15 JJ Thomson Avenue, Cambridge CB3 0FD, United Kingdom
{richard.clayton, steven.murdoch, robert.watson}@cl.cam.ac.uk
* 致谢:我们感谢一位中国公民的帮助。(我们不会透露他的名字,他对我们实验的本质完全不知情,他的网页也不包含任何非法内容)他为我们理论思考提供了极为 可信的实践材料。Richard Clayton正为Intel Research资助的spamHINTS项目工作。Steven J. Murdoch由OpenNet Initiative资助。

摘要: 所谓“防火长城”之部分工作原理即是检测传输控制协议(TCP)报文中需要封锁的关键词。如果出现关键词,TCP复位报文(即RST标志置位的报文)即向 连接两端发送,连接随之关闭。而原报文完好通过防火墙,如果双方完全忽略防火墙的复位,那么连接仍可顺畅进行而防火墙失效。一旦连接被封锁,防火墙还会进 而尝试封锁双方的继发连接。后种特性可能被利用来对第三方进行拒绝服务攻击。

1 引言

中华人民共和国运行的互联网过滤系统,普遍认为是世界上最复杂的系统之一。[1]其部分工作原理即是检测网络(HTTP)流量判断是否出项特定关键词。[2]这些关键词涉及一些中国政府封杀的组织、不可接受的政治意识形态、不愿讨论的历史事件。[3]
直观判断,关键词封锁发生在连接中国与外界网络的路由器组内部。[4]这些路由器利用基于入侵检测系统(IDS)技术的设备来判断报文内容是否匹配中国政府制订的过滤规则。[5]如果客户端与服务器的一个连接需要封锁,路由器则会在数据流中注入伪TCP复位报文,于是双方便会断开连接。[6]这种封锁一旦触发,便会持续数分钟,相同方向上的继发连接都会被伪复位直接打断。
在本文第2节我们将讨论国家阻止其公民访问特定网络内 容的方法,以及以往调查者认定的优点和缺点。在第3节我们提供了从中国防火墙系统封锁的连接两端获取的一组报文。第4节我们提出了这个防火墙的一个模型, 来解释我们获得的结果。然后第5节我们将展示,通过忽略防火墙发出的TCP复位我们成功传输了本来应该被封锁的内容,并讨论为什么这种手段防火墙难以应 付。第6节我们展示了防火墙的封锁行为如何可以被利用来对第三方进行拒绝服务攻击。最后在第7节,我们讨论了这种规避审查的方法的优缺点,并思考了中国以 外的网站如何免于封锁降低访问难度,还提出公共政策能怎样鼓励人们规避审查的问题。

2 内容封锁系统

有三种显著的内容封锁手段:报文丢弃、DNS污染、内容检测。研究北威州封锁右翼纳粹内容的Dornseif的论文[7],和研究英国电信混合封锁系统封锁恋童癖网站的Clayton[8]的论文,一起确定了以上手段。

2.1 报文丢弃方案

在一种报文丢弃方案中,往特定IP地址的所有流量被全部丢弃,于是网站便无法访问。这种方案费用低廉,易于实施──标准的防火墙和路由器便已提供这些必要特性。报文丢弃方案有两个主要问题。首先,IP地址列表必须保持最新,如果内容提供者不想让ISP轻易封锁他们的网站,保持更新的困难便暴露出来。[9]其次,系统会导致“过度封锁”──共用同一IP的其他网站被全部封锁。Edelman调查了过度封锁的潜在程度,发现69.8%的.com、.org和.net网站与50以上其他网站共用IP。[10](虽然一部分域名只是“停放”在一个普通网页上)其详细数字显示网站共用IP数的连续变化图谱,反映出在一台主机上尽量多挂网站这种盛行的商业做法。

2.2 DNS污染方案

在一种DNS污染方案中,当用户查询域名服务系统(DNS)将文字的域名转换为数字的IP地址时,可以返回错误的应答或者不返回应答导致用户不能正 常访问。这类方案没有过度封锁的问题,因为禁止访问特定网站不会影响到其他网站。不过,邮件传递也需要DNS查询,如果只是封锁网站而不封锁邮件服务的 话,此类方案实现起来容易出错。Dornseif展示的样本中所有的ISP都至少有一次在实现DNS污染时出错。[11]

2.3 内容检测方案

多数内容检测方案是让所有流量通过一个代理服务器。这个代理通过不提供禁止内容来过滤。这种系统可以做得非常精确,程度可以到屏蔽单个网页或者单个图像而让其他内容顺利通过。这类基于代理的系统没有普遍使用的原因是,可以应付主干网络或者整个国家网络流量的系统过于昂贵。2004年9月美国宾夕法尼亚州,要求封锁包含儿童色情网站的一条州法令以违宪被裁定无效[12]。当初由于经费原因,宾夕法尼亚的ISP采用的是报文丢弃和DNS污染的混合策略,导致的过度封锁和“前置审核限制”对地区法庭作此裁决起到了相当的作用。不过,基于代理的系统已被部署到若干国家比如沙特阿拉伯[13]、缅甸[14],以及挪威的一些网络提供商比如Telenor[15]。Clayton研究的英国电信的系统是一种混合设计,利用廉价缓存代理处理特定目标IP的报文。不幸这导致用户可以逆向工程得到封禁网站的列表,而这些网站提供儿童的非法图像,这违背了此系统的公共政策目标。
进行内容检测的另一种手段则是入侵检测系统(IDS)。IDS设备可以检测通过的网络流量并判断其内容是否可接受。如果需要封禁则会调度邻近的防火墙拦截报文,或者就中国的情况而言,发送TCP复位报文导致威胁性连接关闭。基于IDS的系统显然比其他方案更灵活,更难规避。Dornseif和Clayton都对如何规避各种封锁进行了深入探讨。[16]然而如果通信保持清晰不加密不变形到IDS无法辨别的程度,那么无论采取什么规避手段,IDS方法都能够将其检测出来。[17]

3 中国防火墙如何封锁连接

我们在实验中从英国剑桥(墙外)的若干机器连接了中国内的一个网站(墙内)。当前中国的防火墙系统的工作方式是完全对称的[18]──在两个方向上检测内容并进行过滤。[19]通 过从剑桥的终端发出所有的指令我们完全避免了违反中国法律的可能性。一开始我们以正常模式访问一个中国网页并记录双方的报文流。接下来我们又发起一次有意 触发封禁的请求,观察连接是如何被复位报文关闭的。我们继续“正常”的(不包含触发性词汇的)请求,却发现接下来的连接都意外地被封锁了。接下来我们将详 细描述观测结果。

3.1 复位封锁

刚开始我们只是访问一个普通网页,如预期得到完全正常的返回。如下面的转储报文所示,起始的TCP三次握手(SYN[20],SYN/ACK[21],ACK[22]) 之后客户端(此实例中使用了53382端口)向服务端http端口(tcp/80)发出了超文本传输协议(HTTP)的GET指令获取顶级页面(/),传 输正常。我们使用netcat(nc)发出这个请求,没有使用网页浏览器,从而避免了无关细节。报文用ethereal截取,用一般格式表示出来。
cam(53382) → china(http) [SYN]
china(http) → cam(53382) [SYN, ACK]
cam(53382) → china(http) [ACK]
cam(53382) → china(http) GET / HTTP/1.0
china(http) → cam(53382) HTTP/1.1 200 OK (text/html) ……
china(http) → cam(53382) ……其余页面内容
cam(53382) → china(http) [ACK]
……接下来这个页面就完整了。
我们发出另一个请求,包含了一小段可能触发封禁的文字,当然这也很快发生了:
cam(54190) → china(http) [SYN]
china(http) → cam(54190) [SYN, ACK] TTL=39
cam(54190) → china(http) [ACK]
cam(54190) → china(http) GET /?falun HTTP/1.0
china(http) → cam(54190) [RST] TTL=47, seq=1, ack=1
china(http) → cam(54190) [RST] TTL=47, seq=1461, ack=1
china(http) → cam(54190) [RST] TTL=47, seq=4381, ack=1
china(http) → cam(54190) HTTP/1.1 200 OK (text/html) ……
cam(54190) → china(http) [RST] TTL=64, seq=25, ack zeroed
china(http) → cam(54190) ……其余页面内容
cam(54190) → china(http) [RST] TTL=64, seq=25, ack zeroed
china(http) → cam(54190) [RST] TTL=47, seq=2921, ack=25
开头三个复位报文序列号对应了GET报文的序列号+1460和+4380(3 × 1460)。[23]我们认为防火墙发出三个不同的值是想确保发送者接受复位,即使发送者已经从目的地收到了“全长”(1460字节)ACK报文。复位报文的序列号需要“正确”设定,因为现在多数TCP/IP实现都会严格检查序列号是否落入预期窗口。[24](这个验证序列号的内在漏洞由Watson在2004年首先提出。[25]
此结果还显示,在连接被打断后仍然收到了从中国机发来的一部分页面。然后剑桥机响应了那两个意外报文,发送了自己的TCP复位。注意它将确认号置零而没有使用随机初始值的相关值。收到的所有复位报文的TTL[26]都 是47,而中国机来的报文的TTL都是39,说明它们来源不同。如果来源的初始值都是64,这也许说明复位产生的地方距离服务器有8跳(hop)。 traceroute显示那是通信从Sprint网络(AS1239)进入中国网通集团网络(AS9929)后的第二台路由器。
我们也从中国服务器的视角看这次连接封锁:
cam(54190) → china(http) [SYN] TTL=42
china(http) → cam(54190) [SYN, ACK]
cam(54190) → china(http) [ACK] TTL=42
cam(54190) → china(http) GET /?falun HTTP/1.0
china(http) → cam(54190) HTTP/1.1 200 OK (text/html) ……
china(http) → cam(54190) ……其余页面内容
cam(54190) → china(http) [RST] TTL=61, seq=25, ack=1
cam(54190) → china(http) [RST] TTL=61, seq=1485, ack=1
cam(54190) → china(http) [RST] TTL=61, seq=4405, ack=1
cam(54190) → china(http) [RST] TTL=61, seq=25, ack=1
cam(54190) → china(http) [RST] TTL=61, seq=25, ack=2921
cam(54190) → china(http) [RST] TTL=42, seq=25, ack zeroed
cam(54190) → china(http) [RST] TTL=42, seq=25, ack zeroed
我们可以看到,当检测到“坏”报文,防火墙也向中国机发送复位(“[RST]”)报文,但都在GET报文(以及其响应报文)后面。最后两个复位报文(零确认号)是剑桥机发送的。
其他到中国机的复位(因为有“falun”而生成的)TTL都是61,这意味着它们在3跳以外生成,初始值为64。这跟剑桥观测到的8跳偏移不一 样。不过这说明可能有不止一台设备在生成复位──或者初始值经过调整不是64。我们目前对于观测到的这种不对称性没有确定的解释。
开始三个复位的序列号也设置在一定范围(+25,+1485,+4405)以确保命中,事实上+25报文就已经重置了连接。[27]第四、五个复位报文检查确认号发现,它们可以视作连接重置前中国机成功发送的两个报文的响应。

3.2 直接重置连接

防火墙不仅检测内容,还有其他封锁规则。我们发现,只要进行一次“坏”连接,在短时间内相同两主机之间的所有网络通信在经过检查之前就都被封锁了。前面也是连接被封搜,不过现在开始继发连接也会被封锁了。比如,在上面一例以后立刻继续,我们看到:
cam(54191) → china(http) [SYN]
china(http) → cam(54191) [SYN, ACK] TTL=41
cam(54191) → china(http) [ACK]
china(http) → cam(54191) [RST] TTL=49, seq=1
复位报文从防火墙而 来(也往服务器而去)随之客户端便关闭了连接。如果客户端在复位到达前成功发送GET报文,便会接着收到多个防火墙发来的复位(即使GET报文是完全无毒 的)。接下来便是从服务端来的复位──服务器收到复位后便会立刻在GET到达前关闭连接。由于GET发来时不再存在打开的连接,服务端便按照协议返回一个 复位。值得注意的是,防火墙在SYN阶段(三次握手阶段一)没有试图重置连接,而是等到了SYN/ACK(阶段二)。虽然可以在客户端一发出SYN就给它 复位报文,但只有等到SYN/ACK才能构造出对服务端起作用的有效复位。[28]
在实验中我们发现,节点被阻断通信的时间是可变的。有时候是几分钟,有时候可能是一小时。平均时间大概在20分钟,不过由于观测到时间值有在特定值附近聚集的显著趋势,我们怀疑不同的防火墙组件有不同的时间延迟设定;这就需要深入理解是到底是谁在处理通信,才能较准确地预测封锁周期。

3.3 其他中国网络的情况

我们获取了中国自治系统(AS)的一个列表,并从中生成了在全球路由表中所有中国子网的列表。[29]然 后我们利用了一个修改过的tcptraceroute,判断出通信是通过哪个AS从国际网络进入中国,并从中得知了中国主干边际网络的实体。结果便是: AS4134,AS4837,AS7497,AS9800,AS9808,AS9929,AS17622,AS24301和AS24489。然后我们在各 个AS中挑选了样例服务器测试,发现所有网络都有都跟前面描述相似的复位行为(除了AS24489:跨欧亚信息网)。以此我们可以推出:我们的结果正展示 了典型的“防火长城”系统。情况在2006年5月下旬是这样的,但并不一定普遍适用。[30]

4 防火长城的设计

基于以上实验结果,以及中国使用的技术设备类型的描述──比如思科的“安全入侵检测系统[31]──我们提出以下模型来描述中国防火墙中路由器的工作方式(此模型很符合观测,但仍是推论性的,因为中国的网络提供商没有发布关于这些系统的任何技术规格):
当报文到达路由便被立刻放入适当的向前传输队列。此报文也被送到带外IDS设备进行内容检测。如果IDS(关键词匹配)认为此报文“不好”,那么便为连接两端各生成三个TCP复位报文(有三个不同的序列号)交由路由器传输。[32]
IDS在逻辑上是与路由器分离的,很难从路由传输队列中去除或者延迟“坏”报文。然而发出复位关闭连接是相对简单的。如果路由器相对繁忙,而IDS 工作正常,复位报文会在“坏”报文之前发送;这也是我们在实验中观测到的主要情况,虽然有时候复位报文会拖在后面。复位报文的设定值充分显示出,设计者担 心与路由器相比IDS的拥塞导致“坏”报文跑在复位报文前面。这种设计中如果不发送附加的复位,在繁忙情况下防火墙是无法保证封锁的可靠性的。
一旦IDS检测到需要封锁的行为,它也可以向主路由器添加一条简单的丢弃规则而不发出复位。[33]我们相当怀疑这种做法在主干高速路由器上扩展性差,而在IDS内的封锁简单而廉价。
我们还观测封锁的时长得知,提供防火墙功 能的设备不止一个。我们进行了进一步实验,发送256个包含威胁性字串的报文通过防火墙,虽然是从一个机器上发出的,但将它们的源地址设置分别为256个 连续的IP地址值,即中国防火墙会认为这是256个不同机器在发送需要封锁的内容。结果是,我们观测到有时候返回的复位报文是乱序的。然而现代互联网处理 报文基本上是用FIFO(先进先出)队列,[34]那 么对于这种失序的最简单解释便是,不同的报文给了不同的IDS,它们各有各的FIFO队列但在发送复位时负载不一样。可惜我们发现这个实验引起了很多的报 文丢失(不是所有的连接都返回了应有的复位报文),不能对报文失序程度有直观感受。这样我们也没法(通过队列建模)确定平行IDS设备的数量下界。我们计 划以后再做这个实验。

4.1 防火墙“状态”

没有证据证明带外IDS设备互相通信,并共享网络连接“状态”的记录。实验表明在一个边际网络触发防火墙不影响通过其他边际网络的通信。
而在“状态”本来应该保留的地方(IDS设备中)却没有关于TCP状 态的检查。设备孤立地检查报文,于是将?falun分散到相邻两个报文就足以避免检测。更有甚者,这些设备对于是否有连接存在也不关注,我们的许多测试中 甚至没有进行三次握手打开连接就直接发送GET报文。事实上除了初始检测之后的持续封锁,没有证据证明IDS设备做了其他什么特别的事情,IDS只是一次 检查一个报文而已。

5 有意忽略复位

防火墙完全依赖于终端节点以标准兼容方式实现TCP协议[35],在收到复位报文时中断连接。如上所述,虽然有时候防火墙有点超常,复位报文跑在GET报文前面结果被仔细验证一番以后丢掉了,不过在下一个报文到达防火墙的时候连接就会被防火墙摧毁所以,总得来说还是没有什么区别。
不过现在考虑如果终端节点不遵守标准然后TCP复位被彻底忽略的情况,我们会想到,即使触发了IDS,防火墙也对HTTP传输没有任何影响。于是我们进行了深入实验两边的终端节点都忽略TCP复位的情况。这有许多方法可以实现,我们选择设置合适的报文过滤防火墙规则。在Linux可以安装iptables并使用此命令:
iptables -A INPUT -p tcp --tcp-flags RST RST -j DROP
来丢弃传入的RST置位报文。如果是FreeBSD的ipfw那么命令是这样的:
ipfw add 1000 drop tcp from any to me tcpflags rst in
当双方都丢弃TCP复位时我们发现网页传输确实没有被封锁。在剑桥端检测传输的结果:
cam(55817) → china(http) [SYN]
china(http) → cam(55817) [SYN, ACK] TTL=41
cam(55817) → china(http) [ACK]
cam(55817) → china(http) GET /?falun HTTP/1.0
china(http) → cam(55817) [RST] TTL=49, seq=1
china(http) → cam(55817) [RST] TTL=49, seq=1
china(http) → cam(55817) [RST] TTL=49, seq=1
china(http) → cam(55817) HTTP/1.1 200 OK (text/html) ……
china(http) → cam(55817) ……其余页面内容
cam(55817) → china(http) [ACK] seq=25, ack=2921
china(http) → cam(55817) ……其余页面内容
china(http) → cam(55817) [RST] TTL=49, seq=1461
china(http) → cam(55817) [RST] TTL=49, seq=2921
china(http) → cam(55817) [RST] TTL=49, seq=4381
cam(55817) → china(http) [ACK] seq=25, ack=4381
china(http) → cam(55817) [RST] TTL=49, seq=2921
china(http) → cam(55817) ……其余页面内容
china(http) → cam(55817) ……其余页面内容
cam(55817) → china(http) [ACK] seq=25, ack=7301
china(http) → cam(55817) [RST] TTL=49, seq=5841
china(http) → cam(55817) [RST] TTL=49, seq=7301
china(http) → cam(55817) [RST] TTL=49, seq=4381
china(http) → cam(55817) ……其余页面内容
china(http) → cam(55817) [RST] TTL=49, seq=8761
……接下来这个页面就完整了。
网页以正常方式传输,除了中间夹杂一些防火墙的TCP复位报文。由于被完全忽略(一共28个复位),它们对客户端的TCP/IP栈没有任何影响。客户端仍然继续接收网页,正常发送ACK。中国端也能看到类似的正常传输夹杂复位的情形。
这样,只是简单地忽略防火长城发出的报文我们就让它完全失效了!这无疑会让它的实现者大为恼火。

5.1 迷惑封锁

一方面是在连接建立以后通过发出TCP复位来阻断继发连接,另一方面我们也观察到一些防火墙有 时还有附加策略。在一些节点(当然是随机的),我们看见了防火墙发来的伪SYN/ACK报文。显然其序列号是随机而且无效的。如果防火墙的SYN/ACK 报文比真报文先到那么连接失效──客户端从伪SYN/ACK中获取了随机的序列号并发给服务端错误的ACK,于是服务端便返回复位报文,导致客户端关闭连 接。实际上,如果客户端发送GET比较快,还会收到一批其他报文,导致防火墙和服务端的进一步复位:
cam(38104) → china(http) [SYN]
china(http) → cam(38104) [SYN, ACK] TTL=105
cam(38104) → china(http) [ACK]
cam(38104) → china(http) GET / HTTP/1.0
china(http) → cam(38104) [RST] TTL=45, seq=1
china(http) → cam(38104) [RST] TTL=45, seq=1
china(http) → cam(38104) [SYN, ACK] TTL=37
cam(38104) → china(http) [RST] TTL=64, seq=1
china(http) → cam(38104) [RST] TTL=49, seq=1
china(http) → cam(38104) [RST] TTL=45, seq=3770952438
china(http) → cam(38104) [RST] TTL=45, seq=1
china(http) → cam(38104) [RST] TTL=45, seq=1
china(http) → cam(38104) [RST] TTL=37, seq=1
china(http) → cam(38104) [RST] TTL=37, seq=1
对付这种防火墙的 新策略比处理伪复位报文麻烦许多。因为即使客户端忽略了服务端来的(完全真实的)复位,还是会继续错误理解服务端的序列号,导致不能与服务端同步以完成三 次握手打开连接。当然如果有时候防火墙的伪SYN/ACK跑在真报文后面,就会被客户端忽略不造成任何混淆,不过防火墙仍然会坚持不懈用复位报文来打断连 接但是由于复位报文都被忽略了所以也没有用,网页照样显示。
重要的是确定来的两个SYN/ACK报文谁是真的。在样例中我们觉得它们很好区分,防火墙版的TTL值大不相同,没有DF标志,没有TCP选项。这些伪SYN/ACK在现在为止还是像伪复位一样很好过滤的,防火长城再次失效。另外,由于只有封锁继发连接时才会使用这种策略,那么客户端可以把服务端的TTL记下来,而防火墙是搞不清该往伪报文里填什么值的。
不过,防火墙越 搞越复杂,说不定就能造出没法区分的SYN/ACK报文来了。那客户端直接把第一个收到的SYN/ACK当成防火墙发出的伪报文即可。不过要是防火墙又来 时不时来延时一下才发送伪SYN/ACK(让思维简单的机器通过,打倒思维复杂的机器!)那么这场复杂的“博弈”会升级成更深奥的战略对决。要注意打开网 页常常会有多个连接,那么防火墙即使只是搞掉其中一部分也会觉得有“胜利感”。
一个高效的客户端策略(先决条件是客户端和服务端都丢弃复位报文)是将所有传入SYN/ACK报文视为有效(防火墙以后也许会发好几个过来),然后检查全部的序列号和确认号直到从服务端收到一个ACK以确认正确的取值。不过这对于像iptables或者ipfw这种简单的报文过滤系统来说太复杂了,超出实现能力。
新一轮“博弈”也许是防火墙开 始针对所有客户端报文伪造ACK。可能客户端可以通过检测从服务端获得的真正RST来看穿防火墙的整个伪连接,于是防火墙连这些都要开始伪造了──这样下 去策略变得不知道有多复杂。不过终端节点确实有优势来最终判断报文是来自(有状态的)对方还是(无状态的)防火墙。要是防火墙也开始记录“状态”,那么整 个主要架构的变化(虽然一定又是一笔巨大的开支)便会带来许多其他可用策略,优势也会决定性地偏向防火墙这边。
可是必须注意到,防火墙的SYN/ACK 报文伪造问题不能通过改变服务端的TCP/IP栈来安全地解决。那样的话服务端需要发现客户端持续地响应的那个“错误”的ACK值并改变自身状态以响应这 个从伪SYN/ACK报文中出来的值。但这样就去掉了一个Bellovin记录的重要安全步骤,进而导致恶意主机伪造源IP地址访问的漏洞。[36]
另外,在可以“嗅探”并伪造报文的对手面前进行安全连接,这在密钥交换协议邻域已经得到充分研究。未决的问题是,如何利用中国防火墙目前的架构性缺陷,通过对现有TCP/IP栈的简单修改来战胜防火长城。

6 拒绝服务攻击

我们前面提到,单个包含?falun之类内容的TCP报文就足以触发节点间至多长达一个小时的封锁。如果伪造源地址,就可以发起(但也是受限的)拒绝服务(Denial of Service)攻击,阻断特定节点间的通信。不过不同的人有不同的目标,这对某些攻击者来说已经足够。比如,识别并阻止地区政府机构的主机访问“Windows自动更新”;或者阻止某个部委访问一个联合国网站;或者阻止中国海外使馆访问家乡网站。
我们计算发现,即使是一个人通过单个拨号连接也可以发起相当有效的DoS攻击。这样一个人每秒可以产生大约100个触发性报文,假设封锁时间大约是20分钟,那么120000对节点便可被永久封锁。当然,现在的DoS攻击几乎不会通过单个拨号方式实现,而是在快得多的网络上以巨大的数量进行。那么120000便可以乘到你满意。不过防火墙的IDS组件也许没有资源记录如此大量的封锁连接,所以实际的影响要考虑受到此类资源限制的情况。还要注意当IDS处理DoS攻击的时候它处理其他连接信息的资源就会变少,于是其效用也就暂时降低。

6.1 DoS攻击的限制

进一步实验显示此防火墙的封锁方式比我们迄今为止解释的还要复杂一些;因此DoS攻击的效果不一定有刚才那样说得那么好。
首先,封锁只应用于相似端口上的继发连接。[37]只 有端口值前9最高有效位与触发封锁的端口对应时,防火墙才会封锁这一连接,这样的端口每次有128个。Windows这类系统会连续分配临时端口,于是平 均有64个继发连接会被封锁。(有时比如触发封锁的端口是4095那么就不会有继发封锁)反之OpenBSD之类的系统会随机分配临时端口,于是继发连接 被封锁的可能性只有1/500。
我们对防火墙的这种行为没有确定的解释。不看端口直接封锁所有连接似乎还简单有效许多。[38]这么做也许是为了避免误封NAT后面的其他用户,或者是用来确定发送某报文的IDS。也许这么做只是有意要显得神秘而愈发有威慑力。然而从DoS攻击者的角度,除非有特殊条件可以预测临时端口,要让所有可能端口段都被封锁所需的报文发送量便增长了500倍。
图1:中国防火墙对“坏”字符串的封锁情况。
图1:中国防火墙对“坏”字符串的封锁情况。
2006年二月上旬我们进行了一次10天的试验,每小时一次从256个相邻IP地址进行连接。这里是前128的结果;其余部分模式也十分相似。黑点表明连接被封锁,白点表明没有封锁,灰点是结果不定(完全没有响应)。在110小时前后可见防火墙策略的显著变化(封锁更多的IP地址)。
其次,并非所有IP地址的流量都被检测过。我们每小时进行一次突发连接,发送一组256个IP地址连续的含有“?falun”的报文。起初每组报文 只有约三分之二被封锁掉,封掉的地址每次不同。不过几天之后几乎所有报文被封锁。我们无法通过逆向工程确定地址选择的算法,不过IP地址选择确有鲜明的模 式[39],暗示背后的机制可能相当简单。最直接的解释是资源匮乏──流量的三分之二也许就是整个系统可以处理的极限。显然某些时候如果一部分机器没有进行报文检测的工作,DoS攻击也就不可能通过它们发起。
最终需要注意的就是,这些实验只是在中国内外的少量节点上进行的,虽然我们得到了足够一致的结果,但像“防火长城”这种复杂的系统我们还是可能忽略了它的某些重要特性。因此虽然我们认为DoS攻击可以在许多情况下成功,我们也不能保证任一节点对上的任一次攻击都能成功。

7 战略考虑

要让流量顺利通过中国防火墙就 必须要求双方忽略复位。“世界其他地方”的机器如果想在中国也能正常访问,只需稍作调整。但在中国这边的人就不那么愿意装一些特殊软件了。主要问题是防火 墙可能做的,不仅是封锁,还有记录。随之而来的可能就是侦查,安装的特殊软件便会被查获,有人便会对你安装此种软件的动机产生某种看法。
中国防火墙的 报文检查功能也可以通过加密的方法规避。如果当局通过对内容的统计分析检测出加密通信,那么安装特殊软件遭到侦查的问题还是一样的。由于加密系统一般会话 结束后便废弃密钥,通信内容是色情还是政治演讲这是无法分辨的。如果用丢弃复位的方法穿墙,防火墙可以通过日志的方法记录封锁触发内容,当局便可以检查日 志并对这两种通信采取不同措施。这两种方法相比有人觉得丢弃复位法更有优势。
如果复位丢弃广泛以毫不相关的理由应用,中国政府可能不得不对复位丢弃软件采取更加容忍的态度。
关于软件防火墙的一些研究指出如果例行丢弃TCP复位会产生一些副作用。[40]复位报文主要是用来快速报告不受欢迎的传入连接。如果远端机工作良好,那么忽略报文而不响应复位只会产生稍微多一点的流量。
然而,对于不想丢掉所有的TCP复位的人来说,当然这里也有另一种策略。[41]目前TTL校验就是一种检测复位报文真伪的简单方法。特别地,我们注意到Watson提出的通过第三方伪复位造成连接关闭的复位攻击[42],通常的防御手段是仔细验证序列号。如果再加上复位报文的TTL校验,就可以更好地识别伪复位。本文作者之一编写了一段20行的FreeBDS补丁[43],可以丢弃TTL值严重偏离的复位报文。到现在用户体验都很好。其他操作系统和个人防火墙大概也不会觉得添加这个功能很麻烦。
当然,中国防火墙也可以改进,让前面提及的规避方法难以实现。特别是它可以较容易地构造正确TTL的复位报文发往触发报文的相同方向。不过要想获得相反方向的正确TTL就不大容易了,因为网络路由基本上是非对称的,防火墙不能透视通信的两个方向。更复杂的方法是将报文从路由队列中移除(或者延迟到内容检测完成才放行)。除非报文在抵达终点前被阻止,我们的基本策略──无视防火墙发出的噪音──将继续有效。
另一套完全不同的防火墙策略则是当封禁被触发后不向该站点转发任何报文。不过我们前面提到此法扩展性极差,因为完成这个“内联”过程需要路由器间的快速通路──而且,全面的封锁无疑增加了DoS攻击的威力。

7.1 打破“防火长城”的公共政策动力

特别是在美国,有相当的政治利益关心着中国之外的公司如何帮助中国政府压制信息、锁定持不同政见者和异见网志作者。特别地,在2006年2月的一次国会听证会上,相当数量的美国大型公司由于其政策和行为而受到了严厉谴责。[44]不过对于如何绕开中国的过滤技术现在也有更多的关注。比如由CIA部分资助的SafeWeb,在2000到2003年运营了一个匿名网络代理,同时它还开发出一种昵称TriangleBoy的反审查技术。[45]2006下半年发起的加拿大的Psiphon计划旨在让“不受审查国家的公民向在他们防火墙背后的朋友和家人提供自由的网络连接”。[46]
可以预测本文所描述的通过忽略复位报文规避中国防火墙的 方法也会引起相当的兴趣。当然也会有“军备竞赛”的危险,所以双方采取的策略可能复杂得多。让防火墙立刻失效方法也是相当直接的;不过实现这个方案需要中 国外的服务器和中国内的浏览器同时丢弃复位。服务器一方显然会有动力去实现复位丢弃,因为这样就让在中国的人可以访问。不过要是看看中国国内的情形和人们 改变浏览器(或者操作系统)配置的动力就会知道事情远比这复杂得多。虽然都是运行在中国的机器上,这些软件却都是在中国以外开发,特别是大多数软件都安装 在微软开发的Windows上。
我们这里提出一个关于公共政策的问题:是否应该鼓励或者强制微软修改程序以帮助规避中国防火墙?显然对于中国的审查有着广泛谴责,那么反审查措施当然会得到政治意见和公共意见的赞同。[47]在本节前面我们提到,对这种改进的技术性反对意见是很有限的,这种改进可以提升对第三方攻击(防火墙只是第三方干涉网络通信的特例)的防御安全性。然而微软(以及其他操作系统和浏览器开发商)很可能不愿意冒犯中国政府,那么在被强制之前他们会一直拖下去。
一般的常识便是软件容易修改,硬件不易修改。不过把硬件改动的时间与制订新法规的时间相比就会知道,在强制供应商提供规避防火墙功能的法律生效之前,中国政府就会采用新的封锁硬件。[48]可以推测,新的硬件会考虑到我们提到的问题并对复位丢弃免疫。因此我们认为通过立法(强制供应商)并不是实用的办法,除非立法是普遍意义上的、不关注技术细节的。让供应商少去讨好中国政府多关心其他所有人,这才是最现实的道路。

8 结论

本文我们展示了基于报文内容检测的“防火长城”。当过滤规则触发时,伪复位报文便发向TCP连接两方。然而真正的报文亦完好通过防火墙,于是通过忽略这些复位,通信便不受干扰。相同方向上的继发连接也被封锁(只有在端口相关的情况下),不过通过忽略复位通信仍可以顺畅继续。
以上结果对于中国当局具有相当的意义,他们也许会加强他们的系统、修补防火墙的漏洞。当然我们在前面也说,这并不容易。[49]然而对于希望自由访问网络的中国居民来说,以上结果意义就小得多,因为他们的活动仍然受到记录和监视。只有当丢弃复位报文成为普遍做法以后,人们的才能说他们是无意中翻墙的。这种想法也不能算离谱,因为验证TCP复位是否为伪造也是TCP/IP栈应该做的。
我们还展示了封锁的副作用:为DoS攻击提供了可能性。当然这种DoS攻击只能用在特定节点之间。利用一套封锁机制来封锁什么东西这本身并没什么新意,但如果防火墙不作足够的状态记录,我们也看不到避免这种攻击的简便方法。
我们展示的结果也关系到其他使用类似复位机制来保护自身利益的国家、机构、企业。他们应该谨慎地认识到这种封锁完全依赖于被封锁者的默许。一些相对中国来说的小国家会经受更大的DoS攻击风险,因为他们的终端节点要少得多,防火墙在攻击效果变得显著之前尚不会过载。

9 补记

2007年春另外一组研究者(Jedidiah R. Crandall和其他人的“ConceptDoppler”项目)[50]的一些实验也揭露了这个防火墙的工作细节,但复位机制是基本不变的。不过他们的测量表明复位现在开始发生在中国互联网的内部,不仅是边际路由器;且与我们一年前观察到的相比封锁在繁忙时段变得更加断断续续。他们的研究手段也让他们可以发表一份关于被过滤话题的统计列表。

注释

  • [1] OpenNet Initiative, “Internet Filtering in China in 2004–2005: A Country Study,” OpenNet Initiative, http://www.opennetinitiative.net/studies/china/ONI_China_Country_Study.pdf (accessed October 21, 2007).
  • [2] OpenNet Initiative, “Probing Chinese Search Engine Filtering,” OpenNet Initiative: Bulletin 005, http://www.opennetinitiative.net/bulletins/005/ (accessed October 15, 2007).
  • [3] Ronald J. Deibert and others, eds., Access Denied: The Practice and Policy of Global Internet Filtering (Cambridge: MIT Press, 2007).
  • [4] Nart Villeneuve, “Censorship is in the Router,” June 3, 2005, http://ice.citizenlab.org/?p=113 (accessed October 15, 2007).
  • [5] OpenNet Initiative, “Probing Chinese Search Engine Filtering.”
  • [6] RST标志置位的TCP报文。这种报文表明一方要求立即关闭当前连接不再传输。
  • [7] See Maximillian Dornseif, “Government Mandated Blocking of Foreign Web Content,” Security, E-Learning, E-Services: Proceedings of the 17 DFN-Arbeitstagung über Kommunikationsnetze, eds. Jan van Knop, Wilhelm Haverkamp, Eike Jessen, 617–646 (Dusseldorf, Germany: GI, 2004).
  • [8] Richard Clayton, “Failures in a Hybrid Content Blocking System,” in Privacy Enhancing Technologies: 5th International Workshop Cavtat, Croatia, May 30-June 1, 2005 (Berlin, Germany: Springer, 2006): 78–92.
  • [9] Richard Clayton, “Anonymity and Traceability in Cyberspace,” Technical Report (2005), http://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-653.pdf (for details of the complexity, see the extensive discussion in “Anonymity and Traceability in Cyberspace”) (accessed October 15, 2007).
  • [10] Benjamin Edelman, “Web Sites Sharing IP Addresses: Prevalence and Significance,” Berkman Center for Internet & Society, http://cyber.law.harvard.edu/people/edelman/ip-sharing (accessed October 15, 2007).
  • [11] Dornseif, “Government Mandated Blocking,” 626–27.
  • [12] Center for Democracy & Technology v. Pappert, 337 F. Supp. 2d 606 (E.D. Penn. 2004).
  • [13] King Abdulaziz City for Science and Technology: Local Content Filtering Procedure. Internet Services Unit (2004), http://www.isu.net.sa/saudi-internet/contenet-filtring/filtring-mechanism.htm (accessed October 15, 2007).
  • [14] OpenNet Initiative, “Internet Filtering in Burma in 2005: A Country Study,” OpenNet Initiative, http://www.opennetinitiative.net/burma/ONI_Burma_Country_Study.pdf (accessed October 15, 2007).
  • [15] Telenor, “Telenor and KRIPOS Introduce Internet Child Pornography Filter,” press release, September 21, 2004, http://presse.telenor.no/PR/200409/961319_5.html (accessed October 15, 2007).
  • [16] Dornseif, “Government Mandated Blocking,” 642-44; Clayton, “Failures in a Hybrid Content Blocking System,” 78–92.
  • [17] IDS可以将多种不规则的表现转化为标准形式,然后与封锁列表比对作出正确决策。
  • [18] 这种对称必须存在,因为防火墙需要同时封禁网络请求和回应。
  • [19] Villeneuve, “Censorship is in the Router.”
  • [20] SYN(同步)标志标明了打开TCP连接时的第一个报文。
  • [21] 这是对SYN报文的回应,其SYN和ACK(确认)置位,用“SYN/ACK”来表示在TCP连接打开过程中的第二个报文。
  • [22] 关于TCP的准确细节,和发起连接时交换SYN、SYN/ACK和ACK置位报文的方法原因,可以查阅很多好的网络通信教材,比如W. Richard Stevens, TCP/IP Illustrated, Volume 1, The Protocols (Reading, MA: Addison-Wesley, 1994)。
  • [23] 当我们启用TCP时间戳且报文包含12字节TCP附加选项的时候,这个值变成1448的倍数。
  • [24] TCP对所有数据报文用序列号进行标记,指示报文包含数据的顺序。当报文丢失、延迟或重复时,可以靠序列号来重建数据流。“窗口”是指在没有收到确认时最 多可以发送的数据量。现在的互联网中,检查序列号落入窗口(复位报文序列号符合预期)是避免第三方干扰连接的重要安全措施。
  • [25] Paul A. Watson, “Slipping in the Window: TCP Reset Attacks,” Open Source Vulnerability Database, http://osvdb.org/reference/SlippingInTheWindow_v1.0.doc (accessed October 15, 2007).
  • [26] 存活时间(TTL)初始值由报文发送者确定,通过一个路由就减一。这是为了确保报文不在路由间无穷循环,当TTL为零时报文就被丢弃了。于是通过校验TTL值可以推算报文走过的距离。
  • [27] 如果复位在GET报文之前到达,则此复位报文不会被接受。服务器是FreeBSD系统,在连接的这个阶段,TCP栈接受的复位的序列号必须精确匹配上次发送的确认的值,以防止拒绝服务攻击。在GET报文到达前其值为+1,于是这时所有的复位都是无效的。
  • [28] SYN/ACK报文含有连接双方选定的序列号。
  • [29] AS指特定ISP拥有的骨干网络。我们采用的是CERNET的“China ASN List”,http://bgpview.6test.edu.cn/bgp- view/cur_ana/ipv4cn/china_asnlist.shtml。互联网路由器保存有优化路径列表,而“全球路由表”表达了特定AS对 地址的所有权。
  • [30] See Jedidiah R. Crandall and others, “ConceptDoppler: A Weather Tracker for Internet Censorship” (14th ACM Conference on Computer and Communications Security, Alexandria, VA, October 29–November 2, 2007) http://www.cs.unm.edu/~crandall/concept_doppler_ccs07.pdf (accessed October 15, 2007).
  • [31] Earl Carter, Secure Intrusion Detection Systems (Indianapolis: Cisco Press, 2001).
  • [32] 即,检测报文内容的设备是在实际连接“旁边的”于是只能检测“坏”流量而不能对其有任何直接影响。
  • [33] 路由器一般都有根据特定标准进行报文过滤的功能。
  • [34] Yi Wang, Guohan Lu, and Xing Li, “A Study of Internet Packet Reordering,” Information Networking (Heidelberg, Germany: Springer-Berlin, 2004): 350–359.
  • [35] J. Postel, ed., “Transmission Control Protocol, DARPA Internet Program Protocol Specification” (memo, Network Working Group Request for Comments, September 1981) http://www.ietf.org/rfc/rfc793.txt (accessed October 21, 2007).
  • [36] S. Bellovin, memorandum, May 1996, in Network Working Group Request for Comments, “Defending Against Sequence Number Attacks,” http://www.ietf.org/rfc/rfc1948.txt (accessed October 15, 2007).
  • [37] ──译注,此节所述似已过时。在翻译完成的时候译者测试发现,继发封锁跟初始端口或者继发端口没有关系,所有端口的继发连接都被屏蔽。
  • [38] HTTP通信不仅在80端口(tcp/http)上被封锁,还有其他一些端口也受到影响。不过一个端口被封不影响其他邻近端口,比如80端口被封不影响433端口(tcp/https)。
  • [39] 见图1。
  • [40] See Clayton, “Anonymity and Traceability,” 81.
  • [41] 未来中国的防火墙还可能通过FIN报文来打断连接,然而忽略所有FIN报文则会导致不能正常连接,到那时TTL校验法会更好。
  • [42] Watson, “Slipping in the Window.”
  • [43] Robert N. M. Watson, “Patches Associated with My Academic Research,” http://www.cl.cam.ac.uk/~rnw24/patches (accessed October 15, 2007).
  • [44] Suzanne Goldenberg, “Congress Accuses Google of Collusion,” The Guardian, February 16, 2006, http://www.guardian.co.uk/china/story/0,,1710616,00.html (accessed October 15, 2007).
  • [45] SafeWeb, “TriangleBoy Whitepaper,” SafeWeb, 2003, http://web.archive.org/web/20030417171335/http://www.safeweb.com/tboy_whitepaper.html (accessed October 15, 2007).
  • [46] Psiphon, http://psiphon.civisec.org (accessed October 15, 2007).
  • [47] Stokely Baksh, “US Calls for Fall of Great Firewall,” United Press International, February 15, 2006; Kate Allen, “Today, Our Chance to Fight a New Hi-Tech Tyranny,” Observer, May 28, 2006; Cory Doctorow, “See No Evil?,” Guardian, July 6, 2007.opinion. 47
  • [48] “此法案是近年来孕育期最长的。导致此法案产生的斯科特报告是在五年半前的1996年2月发表的。保守党政府接受了此报告的提议并立即发出了资讯文书。工 人党1997年的宣言坚定承诺要采取行动。于是1998年出了那本白皮书。不过之后政府就不闻不问于是,过了三年此法案才推出。”Hansard Parliamentary Debates, Commons, 6th ser., vol. 374 (2001), col. 457.
  • [49] 本文中描述的实验都是在2006年春进行的,本文的初始版发表在2006年6月的隐私增强技术研讨会上。
  • [50] ──译注:见http://www.conceptdoppler.org/。