@Phragmites #40 感情的事情,刚开始大家都觉得忘了,现在想起来,是因为看到了当时留下来的东西,记忆就自己回来了。
只是都是甜蜜的记忆,回忆起来却是苦的。
@Phragmites #40 感情的事情,刚开始大家都觉得忘了,现在想起来,是因为看到了当时留下来的东西,记忆就自己回来了。
只是都是甜蜜的记忆,回忆起来却是苦的。
@小二 #37
很古老了,当年知道的是老品葱。有一次不知道什么原因,找到了新品葱。
偶尔看看你们写了什么,那时候也没几个人说话,你那个时候叫小二8964,头像是新垣结衣
后来一次上品葱,一只鹿儿说自己借你的尸体发了个帖子。我是没看她发的内容,只是明白了你离开了
我有老年痴呆,很多东西记不清了。。。
我印象里,我注册品葱一个星期后就是管理员了。。。
也许刷声望太快了,被某些人排挤。。
各种人怀疑我的性别,怀疑我的年龄,怀疑我的职业。。。
明明只需要关心我说了什么就好了,老是研究我干嘛
@梅菲斯特 #5 在水楼里说要看《飞鸟不鸣》是你么?还是羊城?
@小二 #33
你退葱以后,我才注册,有印象才神奇。
我怀疑品葱某些人怀疑我是BE4之类的。。。
小说的话,我觉得《爱书的下克上》很好看!
@梅菲斯特 #3
好吧。我觉得那些都很好看。。。男生不喜欢看。。。
爱书的下克上
CLANNAD
寒蝉鸣泣之时
Angel Beats
太多了,我有很多想推荐你看怎么办。。。
@小二 #31 能不能把上面的大叔都换成lisa。。。
我习惯别人叫我lisa
我在品葱,也是为了推广小说,那些被共产党封杀的小说。。。
东京不太热【回应版】
youtu.be/MuUlJGgSw1w@小二 #29
额。。。
你是第一个叫我大叔的。。。
我不是程序员。我在品葱,更多是发些情感小说和音乐。。。
我看的小说动漫电影电视剧,听的音乐,N多东西被共产党封杀了,所以跑到墙外备份这些东西。
所以发现了品葱,在品葱反共,是因为共产党封杀我看的小说之类的。
题目非常难!!!
我先给答案
using CP;
int nbTrials=4;
range trials=1..nbTrials;
int n=11;
range coins=1..n;
tuple comb // the light ones
{
int x;
int y;
}
{comb} combs={<i,j> | ordered i,j in coins}; // i and j are the bad coins
dvar boolean x[trials][coins][1..2]; // 4 trials n coins right and left side
dvar int y[combs][trials] in -1..1; // result of the weight -1 : less 0 : equal 1 : more
subject to
{
// same number of coins both side
forall(i in trials) sum(j in coins) x[i][j][1]==sum(j in coins) x[i][j][2];
// a coin is either left or right or not there
forall(i in trials) forall(j in coins) x[i][j][1]+x[i][j][2]<=1;
// not empty
forall(i in trials) sum(j in coins) x[i][j][1]!=0;
// results of the balance
forall(c in combs)
forall(i in trials)
y[c][i]
==
sgn(x[i][c.x][1]+x[i][c.y][1]-x[i][c.x][2]-x[i][c.y][2]);
// To be able to find the fraud
forall(ordered c1,c2 in combs) or(i in trials) (y[c1][i]!=y[c2][i]);
}
execute
{
function display(v)
{
return String.fromCharCode(64+v);
}
for(i in trials)
{
for(var j in coins) if (x[i][j][1]==1) write(display(j));
write("-");
for(var j in coins) if (x[i][j][2]==1) write(display(j));
writeln();
}
}
ABC-DEF BEH-CDJ DGH-EIJ AGI-FHJ and ABC-DEF DGH-EIJ AGI-BHJ CDEHI-ABFGJ
@小二 #12
你绕不开js,那是因为你开发web的缘故。可以试试我上面说的行业。都不需要js的
@electron8964 #13
有个小说《爱书的下克上》,女主角一心想当图书馆里,我是一样的
我觉得小二发帖的时候,情绪不好
只是希望我的歪楼,可以转变大家的心情,在屏幕前自己笑一笑,忘记不开心的事情
现在大家应该忘了自己不开心的情绪了,收工~
@Phragmites #27
不是的,我是想起了初恋。。。
等你有一天长大了,结婚了。如果和我一样的情况,保留着初恋的一些东西,几十年后再看到,也会想起过去。只是你们不会有我这么大触动罢了
@electron8964 #6 我只是一个图书管理员而已,不是技术大牛
@小二 #8 我知道啊,但是RoR的设计理念非常先进,我觉得其他语言的web框架都是对RoR的模仿而已
而且我已经说了,自娱自乐推荐RoR,找工作的确是没希望了
我是看着Ruby China起来,看着它没落。。。都去玩Erlang了
Erlang和OCaml我觉得也不错, Erlang在通信行业还是可以找到饭碗的,工资也不低,
OCaml可以去Jane Street看看,类似F#,金融行业,工资不低啊
但这些都不是web开发主力语言。
不同行业,语言不同,隔行如隔山。比如,我看人工智能的时候,会python更多,主流框架tensorflow pytorch keras
比如量子力学 python
统计学 python R语言 SPSS
建筑 matlab maple lingo
金融 F# OCaml
以上内容,我都不会,但是都用过这些编程语言。。。
再来写冷门的~
IBM有一个叫ILOG的编程语言。。。我感觉很牛逼。。。
算了。。。不说了,说太多,把自己是谁都泄露了
对于小白的建议
先去学习Ruby on Rails,等你入门了,基本就可以自己写个BBS了
然后学习下RESTFul 规范,让你学会约束自己的代码【培养CRUD思维方式】
最后,网上搜索下前后端分离,前端可以看看vue,我觉得挺好用的!
写web,万变不离其宗,说到底也就是CRUD+js画界面
前端,html+js+css三板斧而已,MDN上学习,一个下午就搞定了
后端,我建议RoR入门,或者python入门。真的玩进阶版,php已经淘汰了,估计是golang会是将来的趋势,国内目前java为主
如果是找工作,建议java或者golang,如果是自娱自乐,推荐RoR和Python
瞄了一眼2049的代码。。
代码是golang写的,MVC架构。数据库是youdb,抛弃了ORM,将obj直接序列化存储到key -value的数据库。
好处是不需要mybatis这样的orm框架了,坏处是很多复杂业务,代码写起来就很变态,而且性能很糟。
youdb和leveldb一样,属于文件型数据库,同类是sqlite,但是sqlite是传统数据库,youdb是key-value数据库。
++++++++++++++
话说这代码是真的轻量级啊,MVC分的也算清楚,内部实现也不复杂。
说了这么多废话,就是觉得不喜欢这里的人,回家自己搭一个bbs自娱自乐也挺好的~
我也用discuz搭过一个bbs,可惜被共产党封杀了
繼續查看ext_sni_add_clienthello函數
發現CBB_add_bytes(&name, (const uint8_t *)ssl->hostname.get(),這行代碼,會對SNI賦值hostname
也就是說,爲什麽我們通過wireshark抓包,可以明文看到自己訪問什麽網站,就是這行代碼造成的!
GFW通過全網檢查ClientHello的SNI字段,來判斷你在訪問什麽網站!而且因爲是明文,所以GFW的工作很簡單,只要檢查你的TLS握手SNI字段,如果是敏感詞,就RST掉你的tcp連接。你就上不去了。品蔥就是這樣,所以上不去,需要開啓ESNI功能,就沒事了~
代碼修改:
1.增加一個ext,取名叫ESNI,參考SNI的實現,完成相關代碼
2.移植火狐的esni加密代碼到boringssl,完成第一步的esni報文拼接
3.編譯自己的boringssl庫,替換掉chrome自帶的boringssl
4.chrome有自己dll校驗機制,你手動替換有點問題,好像跑不起來,得網上找下教程,學習如何替換chrome的boringssl
別找我要代碼或者已經編譯好的dll !我要是寫好了,早提PR給chrome了~
關於爲什麽不自己寫代碼完成剛才我説的這麽多事情:
我翻墻的條件很糟糕,下載chrome的源碼,經常斷掉!失敗就得重新下載。至今沒有下載到chrome源碼。
另外編譯環境配置非常複雜,我沒有那個功夫去完成這麽費時間的工作。老了啊,想當年自己花了3個星期,搭了Gentoo Linux,現在搭個chrome的編譯環境都覺得浪費時間
言歸正傳!
我們講解Chrome了,chrome的TLS實現叫做BoringSSL,fork自著名的openSSL。
開始講如何boringssl的代碼。
翻開課本,boringssl\ssl\handshake_client.cc這個代碼。開始閲讀ssl_client_handshake
對!ESNI是在客戶端發起握手的時候發送的!所以從握手開始讀起。
找到do_start_connect這個函數!很多人覺得看著這個代碼,很嚇人。別怕,其實關鍵信息就i是ssl_write_client_hello這麽一行罷了。其他的都不重要~
ssl_write_client_hello這個函數,什麽都不做,純粹拼報文而已!二進制級別拼寫,類似給一個struct賦值。
我們就看ssl_add_clienthello_tlsext這個函數!因爲ESNI或者SNI都是放在ext字段。
翻開課本 boringssl\ssl\t1_lib.cc 找到 ssl_add_clienthello_tlsext
for (size_t i = 0; i < kNumExtensions; i++) {
const size_t len_before = CBB_len(&extensions);
if (!kExtensions[i].add_clienthello(hs, &extensions)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
return false;
}
意思是遍歷所有的ext,調用對應ext的add_clienthello函數,拼clienthello報文
// kExtensions contains all the supported extensions.
static const struct tls_extension kExtensions[] = {
{
TLSEXT_TYPE_server_name,
NULL,
ext_sni_add_clienthello,
ext_sni_parse_serverhello,
ext_sni_parse_clienthello,
ext_sni_add_serverhello,
},
{
TLSEXT_TYPE_extended_master_secret,
NULL,
ext_ems_add_clienthello,
ext_ems_parse_serverhello,
ext_ems_parse_clienthello,
ext_ems_add_serverhello,
},
{
TLSEXT_TYPE_renegotiate,
NULL,
ext_ri_add_clienthello,
ext_ri_parse_serverhello,
ext_ri_parse_clienthello,
ext_ri_add_serverhello,
},
{
TLSEXT_TYPE_supported_groups,
NULL,
ext_supported_groups_add_clienthello,
ext_supported_groups_parse_serverhello,
ext_supported_groups_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_ec_point_formats,
NULL,
ext_ec_point_add_clienthello,
ext_ec_point_parse_serverhello,
ext_ec_point_parse_clienthello,
ext_ec_point_add_serverhello,
},
{
TLSEXT_TYPE_session_ticket,
NULL,
ext_ticket_add_clienthello,
ext_ticket_parse_serverhello,
// Ticket extension client parsing is handled in ssl_session.c
ignore_parse_clienthello,
ext_ticket_add_serverhello,
},
{
TLSEXT_TYPE_application_layer_protocol_negotiation,
NULL,
ext_alpn_add_clienthello,
ext_alpn_parse_serverhello,
// ALPN is negotiated late in |ssl_negotiate_alpn|.
ignore_parse_clienthello,
ext_alpn_add_serverhello,
},
{
TLSEXT_TYPE_status_request,
NULL,
ext_ocsp_add_clienthello,
ext_ocsp_parse_serverhello,
ext_ocsp_parse_clienthello,
ext_ocsp_add_serverhello,
},
{
TLSEXT_TYPE_signature_algorithms,
NULL,
ext_sigalgs_add_clienthello,
forbid_parse_serverhello,
ext_sigalgs_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_next_proto_neg,
NULL,
ext_npn_add_clienthello,
ext_npn_parse_serverhello,
ext_npn_parse_clienthello,
ext_npn_add_serverhello,
},
{
TLSEXT_TYPE_certificate_timestamp,
NULL,
ext_sct_add_clienthello,
ext_sct_parse_serverhello,
ext_sct_parse_clienthello,
ext_sct_add_serverhello,
},
{
TLSEXT_TYPE_channel_id,
ext_channel_id_init,
ext_channel_id_add_clienthello,
ext_channel_id_parse_serverhello,
ext_channel_id_parse_clienthello,
ext_channel_id_add_serverhello,
},
{
TLSEXT_TYPE_srtp,
ext_srtp_init,
ext_srtp_add_clienthello,
ext_srtp_parse_serverhello,
ext_srtp_parse_clienthello,
ext_srtp_add_serverhello,
},
{
TLSEXT_TYPE_key_share,
NULL,
ext_key_share_add_clienthello,
forbid_parse_serverhello,
ignore_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_psk_key_exchange_modes,
NULL,
ext_psk_key_exchange_modes_add_clienthello,
forbid_parse_serverhello,
ext_psk_key_exchange_modes_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_early_data,
NULL,
ext_early_data_add_clienthello,
ext_early_data_parse_serverhello,
ext_early_data_parse_clienthello,
ext_early_data_add_serverhello,
},
{
TLSEXT_TYPE_supported_versions,
NULL,
ext_supported_versions_add_clienthello,
forbid_parse_serverhello,
ignore_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_cookie,
NULL,
ext_cookie_add_clienthello,
forbid_parse_serverhello,
ignore_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_quic_transport_parameters,
NULL,
ext_quic_transport_params_add_clienthello,
ext_quic_transport_params_parse_serverhello,
ext_quic_transport_params_parse_clienthello,
ext_quic_transport_params_add_serverhello,
},
{
TLSEXT_TYPE_token_binding,
NULL,
ext_token_binding_add_clienthello,
ext_token_binding_parse_serverhello,
ext_token_binding_parse_clienthello,
ext_token_binding_add_serverhello,
},
{
TLSEXT_TYPE_cert_compression,
NULL,
cert_compression_add_clienthello,
cert_compression_parse_serverhello,
cert_compression_parse_clienthello,
cert_compression_add_serverhello,
},
{
TLSEXT_TYPE_delegated_credential,
NULL,
ext_delegated_credential_add_clienthello,
forbid_parse_serverhello,
ext_delegated_credential_parse_clienthello,
dont_add_serverhello,
},
};
因爲我們要做的是SNI,所以看到了第一個ext,找到了它對應的add_clienthello是ext_sni_add_clienthello
漏了剛才的代碼,補上
conn, resp, err := dialer.Dial(uri, wsSettings.GetRequestHeader())
return internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
第一行代碼表示調用了開源的github.com/gorilla/websocket庫文件,不在此解讀了,你只要知道,通過這個開源庫,去建立了wss連接就好了,v2ray沒有自己實現wss,只是調用了第三方庫。
第二行代碼,表示去建立了一個tcp連接,無它~
希望我的講解,可以幫助大家少走很多彎路,看代碼知道哪些是v2ray實現的,哪些是第三方庫
遇到問題,需要解決的時候,有一個思路,知道去看什麽代碼文件!
比如爲什麽不像SS一樣需要自己指定加密算法呢?因爲這個是TLS自己協商出來的,而不是我們指定的,我們可以通過刪減剛才的config.CipherSuites來指定加密算法
以及被中間人攻擊怎麽辦?我們可以通過config.Certificates 來設置自己的根證書解決
爲什麽看不到AEAD加密算法實現呢?因爲在github.com/gorilla/websocket庫文件實現的數據AEAD加密
等等問題,可以通過閲讀代碼自己回答。有需要,可以自己提issue。但是我都是自己改代碼,完成自我功能需求的客制化~
有什麽v2ray的問題,可以給我留言。或者在v2ray的GitHub上提issue。
@Phragmites #21 爲什麽會這樣覺得呢?
@小二 #20
你的這篇文章更側重向讀者解釋DNS的工作原理。以及如何DNS劫持,最後DoH如何解決了相關問題。還有哪些問題是遺留下來。很好的科普文章!
但是我覺得這類科普類的文章太多了,不缺我一篇。我想寫自己的原創文章,國内講科普的代碼,講源碼的太少了。我希望把自己的代碼總結給大家,讓大家知道出了問題,去什麽目錄下找什麽文件,從什麽函數開始讀起,如何定位相關問題,並解決。
如果我的原創文章足夠多,也足夠優秀,我覺得2049bbs的PageRank自然會提升~
@electron8964 #16
如果你看過我在品蔥的帖子,就知道我孩子都上大學了。。。
@electron8964 #13 他寫小説挺好的,可惜共產黨封殺了他寫的小説。我不希望這些小説消失,便轉載到了品蔥,希望後人可以閲讀。
我覺得,2000年左右,還可以讀到很多很多小説,後來就大肆封殺。我現在只能讀讀日本輕小説了。當年涼宮春日的小説挺火的
@electron8964 #5 你也看慕容雪村的書麽?我有他親筆簽名~ 我在品蔥發了慕容雪村的小説。你可以去看看
@小二 #2
DNS協議的功能是通過域名查找對應的IP地址。DNS協議,明文跑在UDP協議下,端口寫死是53。
因爲端口固定是53,所以特別好劫持。因爲是明文,所以GFW可以針對性劫持。
用戶瀏覽器發起DNS請求,比如www.google.com的ip是什麽?GFW全網抓包端口53的UDP流量。
捕獲了你的請求,然後僞造了一個假的DNS應答報文給你,IP當然是假的了。所以就上不了Google了。
過去對品蔥就是這種封殺,僞造DNS。這個就是DNS劫持。
自己在本地的hosts裏增加正確的record就可以了。
就是把DNS請求通過https來發送,因爲https是加密的,所以克服了剛才的所有缺點。 DoH 有兩種方式,一種是發post請求,一種是發get請求。http的應答報文的body則是DNS的應答。
通過調用DoHEncode把請求封裝成一個二進制的buffer請求。
如果是post方式獲取DNS,則把剛才生成的buffer放到http的body裏,通過http二進制傳輸發送請求。
如果是get方式獲取DNS,則把剛才的buffer通過base64編碼,放到http的查詢URI裏發送。
得到的http應答報文根據DoHDecode函數,解析DNS應答,並返回給瀏覽器真實的ip地址。有效防止了DNS泄露,DNS劫持,等n多問題
因爲大部分代碼都在拼http報文的head部分,就不去探究了。我們只用關心DOHEncode和DOHDecode兩個函數就好了。這兩個就是dns協議的拼裝和解析。其實也不用過度解讀。。。
@Phragmites #8 蒹葭蒼蒼?看頭像猜的。 你好啊
@梅菲斯特 #6 那是因爲我沒葱發文章,才發水樓。發一個35葱,都沒法回本。
SSLExp_EnableESNI
tls13_ClientSetupESNI
tls13_ServerDecryptEsniXtn
再談下ESNI這個代碼,我不知道和TRR有什麽關聯,必須TRR打開,ESNI功能才能使用。感覺明明是獨立的兩個功能。
GFW的問題,我這裏網絡不好,發文章比較卡。。
通過TRR::OnDataAvailable函授收到應答報文,只看代碼On200Response,先不考慮異常情況
DohDecode會解析應答報文,放到mDNS裏
最後調用TRR::ReturnData 返回查詢結果,mHostResolver->CompleteLookup
網絡不好,排版很累,沒法預覽。我不明白,這個地方爲什麽開啓了TRR才可以使用esni,不使用的話esni沒法啓用。爲什麽呢?