diff --git a/.gitignore b/.gitignore
index 32858aa..e5a4d02 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+
+.idea/
*.class
# Mobile Tools for Java (J2ME)
@@ -9,4 +11,4 @@
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
-hs_err_pid*
+hs_err_pid*
\ No newline at end of file
diff --git a/NSS.iml b/NSS.iml
new file mode 100644
index 0000000..01178b1
--- /dev/null
+++ b/NSS.iml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client_config.json b/client_config.json
new file mode 100755
index 0000000..9e26dfe
--- /dev/null
+++ b/client_config.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/conf/client_config.json b/conf/client_config.json
new file mode 100755
index 0000000..a370ca3
--- /dev/null
+++ b/conf/client_config.json
@@ -0,0 +1 @@
+{"recent_address_list":["139.162.22.111"],"upload_speed":2740596,"download_speed":2859752,"server_port":150,"server_address":"139.162.22.111","protocal":"udp","socks5_port":1085,"auto_start":true}
\ No newline at end of file
diff --git a/conf/config.xml b/conf/config.xml
new file mode 100755
index 0000000..e3dc6b6
--- /dev/null
+++ b/conf/config.xml
@@ -0,0 +1,9 @@
+
+
+ 127.0.0.1
+ 1085
+ 127.0.0.1
+ 1081
+ aes-256-cfb
+ sssssssss
+
diff --git a/conf/pac.xml b/conf/pac.xml
new file mode 100755
index 0000000..6b0c7bf
--- /dev/null
+++ b/conf/pac.xml
@@ -0,0 +1,2478 @@
+
+
+ gimpshop.com
+ *.*.*
+ directcreative.com
+ speedpluss.org
+ mingpaovan.com
+ wikinews.org
+ joachims.org
+ maiio.net
+ idv.tw
+ mail-archive.com
+ surfeasy.com.au
+ hihistory.net
+ alexlur.org
+ finalion.jp
+ nrk.no
+ nyt.com
+ cmule.com
+ gappp.org
+ givemesomethingtoread.com
+ yahoo.com.tw
+ robtex.com
+ thelifeyoucansave.com
+ perfspot.com
+ ugo.com
+ army.mil
+ amoiist.com
+ uderzo.it
+ zillionk.com
+ placemix.com
+ twitstat.com
+ erabaru.net
+ zhongmeng.org
+ tinypaste.com
+ wo.tc
+ youtu.be
+ prozz.net
+ tiananmenuniv.com
+ freemorenews.com
+ penchinese.net
+ mesotw.com
+ favotter.net
+ privacybox.de
+ liaowangxizang.net
+ firstfivefollowers.com
+ rfamobile.org
+ xanga.com
+ godfootsteps.org
+ dalailama.com
+ bigsound.org
+ retweetist.com
+ fizzik.com
+ bbg.gov
+ imagezilla.net
+ myforum.com.hk
+ imlive.com
+ webshots.com
+ ptt.cc
+ lsforum.net
+ bigfools.com
+ ziplib.com
+ makemymood.com
+ foxdie.us
+ juliereyc.com
+ 5i01.com
+ beijingspring.com
+ drewolanoff.com
+ twiffo.com
+ blinkx.com
+ michaelmarketl.com
+ views.fm
+ kcome.org
+ acgkj.com
+ branch.com
+ soupofmedia.com
+ autoproxy-gfwlist.googlecode.com
+ po2b.com
+ slideshare.net
+ dyndns.org
+ wikileaks.lu
+ sohcradio.com
+ allgirlsallowed.org
+ pts.org.tw
+ twitonmsn.com
+ 5maodang.com
+ idouga.com
+ whyx.org
+ peacehall.com
+ instapaper.com
+ pure18.com
+ greatfirewallofchina.org
+ lagranepoca.com
+ sstatic.net
+ rfa.org
+ sokamonline.com
+ im.tv
+ hulu.com
+ twiyia.com
+ sethwklein.net
+ dupola.com
+ dupola.net
+ coolaler.com
+ ngensis.com
+ googlepages.com
+ mp
+ freeweibo.com
+ novelasia.com
+ v70.us
+ zfreet.com
+ fgmtv.org
+ rssmeme.com
+ futuremessage.org
+ wsj.com
+ ieasynews.net
+ openleaks.org
+ benjaminste.in
+ asahichinese.com
+ twicsy.com
+ sweux.com
+ chrispederick.com
+ amzs.me
+ lkcn.net
+ woxinghuiguo.com
+ wefong.com
+ savemedia.com
+ livingstream.com
+ shangfang.org
+ hkzone.org
+ samsoff.es
+ kcsoftwares.com
+ twip.me
+ zannel.com
+ gaopi.net
+ emacsblog.org
+ tokyocn.com
+ robustnessiskey.com
+ wangruowang.org
+ internetfreedom.org
+ linpie.com
+ fc2.com
+ ghostery.com
+ taolun.info
+ bestforchina.org
+ m-team.cc
+ e-hentai.org
+ cna.com.tw
+ setty.com.tw
+ wikipedia.org
+ sorting-algorithms.com
+ kusocity.com
+ twttr.com
+ post.ly
+ backchina.com
+ thespeeder.com
+ tuidang.net
+ sinopitt.info
+ mongodb.org
+ orzistic.org
+ golang.org
+ betfair.com
+ oursogo.com
+ art-or-porn.com
+ omy.sg
+ middle-way.net
+ ap.org
+ dajiyuan.com
+ laqingdan.net
+ youmaker.com
+ anonymizer.com
+ cnavista.com.tw
+ nuvid.com
+ syx86.com
+ engadget.com
+ typepad.com
+ matsushimakaede.com
+ kickstarter.com
+ plm.org.hk
+ falsefire.com
+ fuckgfw.com
+ topnews.in
+ ihakka.net
+ hkatvnews.com
+ dfas.mil
+ pullfolio.com
+ yousendit.com
+ lupm.org
+ tweetdeck.com
+ t35.com
+ plunder.com
+ pbworks.com
+ tpi.org.tw
+ freerk.com
+ networkedblogs.com
+ hahlo.com
+ theampfactory.com
+ gvm.com.tw
+ coolder.com
+ civilhrfront.org
+ rnw.nl
+ apiary.io
+ e-gold.com
+ jitouch.com
+ youtubecn.com
+ livingonline.us
+ your-freedom.net
+ pct.org.tw
+ fringenetwork.com
+ epochtimes.jp
+ deck.ly
+ spinejs.com
+ tvants.com
+ kagyuoffice.org.tw
+ ventureswell.com
+ womensrightsofchina.org
+ smhric.org
+ 6-4.net
+ mash.to
+ ifanqiang.com
+ quadedge.com
+ openid.net
+ rangzen.org
+ fbcdn.net
+ page2rss.com
+ morbell.com
+ boxcar.io
+ freedomhouse.org
+ footwiball.com
+ shixiao.org
+ ecstart.com
+ desc.se
+ sod.co.jp
+ usa.gov
+ twbbs.tw
+ chromeadblock.com
+ rocmp.org
+ gardennetworks.org
+ njuice.com
+ dailyme.com
+ oclp.hk
+ search.com
+ hkhkhk.com
+ puffstore.com
+ advanscene.com
+ onlylady.cn
+ miroguide.com
+ centurys.net
+ planetsuzy.org
+ cdpwu.org
+ myav.com.tw
+ dw-world.com
+ fangbinxing.com
+ xuchao.org
+ usfk.mil
+ brandonhutchinson.com
+ rapidsharedata.com
+ qmzdd.com
+ foxbusiness.com
+ diigo.com
+ xml-training-guide.com
+ freeoz.org
+ blogimg.jp
+ twitterkr.com
+ tanc.org
+ anontext.com
+ twurl.nl
+ pilotmoon.com
+ fulue.com
+ turningtorso.com
+ sytes.net
+ mk5000.com
+ americangreencard.com
+ zhenghui.org
+ cao.im
+ 50webs.com
+ vtunnel.com
+ nintendium.com
+ xiezhua.com
+ sex8.cc
+ thedw.us
+ markmail.org
+ radioaustralia.net.au
+ seevpn.com
+ rileyguide.com
+ 908taiwan.org
+ lerosua.org
+ twitgoo.com
+ livestream.com
+ facesofnyfw.com
+ mpettis.com
+ pdetails.com
+ natado.com
+ fourthinternational.org
+ globalrescue.net
+ slinkset.com
+ chinaxchina.com
+ chinasocialdemocraticparty.com
+ marines.mil
+ chinese-hermit.net
+ nextmedia.com
+ jiruan.net
+ calameo.com
+ tweetboner.biz
+ vimperator.org
+ furl.net
+ wordsandturds.com
+ cnn.com
+ dvorak.org
+ suoluo.org
+ fly4ever.me
+ ntdtv.org
+ lsm.org
+ nytimes.com
+ summify.com
+ geohot.com
+ heqinglian.net
+ xinmiao.com.hk
+ skype.com
+ gtricks.com
+ uniteddaily.com.my
+ westkit.net
+ bill2-software.com
+ gstatic.com
+ nydus.ca
+ brizzly.com
+ wallpapercasa.com
+ bonbonme.com
+ mypopescu.com
+ ezpc.tk
+ justfreevpn.com
+ taipei.gov.tw
+ fxnetworks.com
+ cl.ly
+ theguardian.co
+ csdparty.com
+ labiennale.org
+ cpj.org
+ fw.cm
+ sogclub.com
+ weeewooo.net
+ iset.com.tw
+ pornrapidshare.com
+ qtweeter.com
+ fuckcnnic.net
+ 92ccav.com
+ yimg.com
+ eriversoft.com
+ hrcir.com
+ emule-ed2k.com
+ ghost.org
+ twhirl.org
+ vevo.com
+ huaxia-news.com
+ giga-web.jp
+ broadbook.com
+ goldwave.com
+ dok-forum.net
+ ied2k.net
+ chinesen.de
+ myactimes.com
+ skykiwi.com
+ hugoroy.eu
+ conoyo.com
+ izaobao.us
+ alwaysdata.net
+ xvideos.com
+ iicns.com
+ foolsmountain.com
+ torrentcrazy.com
+ so-net.net.tw
+ xinsheng.net
+ scmp.com
+ zozotown.com
+ shopping.com
+ xjp.cc
+ matainja.com
+ supertweet.net
+ geometrictools.com
+ ibros.org
+ fangeming.com
+ shellmix.com
+ gov.tw
+ icl-fi.org
+ a-normal-day.com
+ a5.com.ru
+ apetube.com
+ biantailajiao.com
+ chosun.com
+ baidu.jp
+ philly.com
+ tweete.net
+ laogai.org
+ mingpaonews.com
+ gopetition.com
+ longtermly.net
+ realraptalk.com
+ gongwt.com
+ fooooo.com
+ gamebase.com.tw
+ in.com
+ vidoemo.com
+ xrea.com
+ morningsun.org
+ tibetalk.com
+ blogtd.org
+ helpzhuling.org
+ htmldog.com
+ mimivip.com
+ htl.li
+ tap11.com
+ yilubbs.com
+ zuola.com
+ pengyulong.com
+ provideocoalition.com
+ fan-qiang.com
+ twt.tl
+ ithome.com.tw
+ dy24k.info
+ chinainperspective.com
+ cms.gov
+ lookpic.com
+ ultravpn.fr
+ webs-tv.net
+ hakkatv.org.tw
+ wretch.cc
+ urbanoutfitters.com
+ mobileways.de
+ c-est-simple.com
+ myfreshnet.com
+ plays.com.tw
+ journalofdemocracy.org
+ cookingtothegoodlife.com
+ taiwandaily.net
+ bloomberg.de
+ catcatbox.com
+ thevivekspot.com
+ mingpaomonthly.com
+ plus28.com
+ tunnelbear.com
+ line.me
+ twt.fm
+ twipple.jp
+ premeforwindows7.com
+ ladbrokes.com
+ bookshelfporn.com
+ wikimapia.org
+ catfightpayperview.xxx
+ goagent.biz
+ bloodshed.net
+ catholic.org.hk
+ chuizi.net
+ uygur.org
+ googlesile.com
+ ka-wai.com
+ wujie.net
+ hkepc.com
+ bjzc.org
+ nexton-net.jp
+ entermap.com
+ brightkite.com
+ bwsj.hk
+ pdproxy.com
+ ustream.tv
+ lyricsquote.com
+ psblog.name
+ flightcaster.com
+ mad-ar.ch
+ gdzf.org
+ reuters.com
+ unblock.cn.com
+ whereiswerner.com
+ cdpweb.org
+ sugarsync.com
+ nuzcom.com
+ want-daily.com
+ helloqueer.com
+ cuiweiping.net
+ baby-kingdom.com
+ dotplane.com
+ ccdtr.org
+ wikileaks.pl
+ bot.nu
+ dalianmeng.org
+ vansky.com
+ latimes.com
+ google.co.jp
+ tvunetworks.com
+ futurechinaforum.org
+ hikinggfw.org
+ qoos.com
+ target.com
+ waqn.com
+ chinarightsia.org
+ guomin.us
+ cyberghostvpn.com
+ billywr.com
+ twittertim.es
+ felixcat.net
+ toodoc.com
+ yam.com
+ porn.com
+ stoptibetcrisis.net
+ xuchao.net
+ generesis.com
+ minimalmac.com
+ yidio.com
+ aolnews.com
+ newcenturymc.com
+ newcenturynews.com
+ shaunthesheep.com
+ newsminer.com
+ jwmusic.org
+ socialwhale.com
+ drtuber.com
+ devio.us
+ wikilivres.info
+ gmbd.cn
+ hkgreenradio.org
+ mtw.tl
+ ziddu.com
+ slavasoft.com
+ internationalrivers.org
+ ttv.com.tw
+ tv.com
+ securitykiss.com
+ akiba-online.com
+ wikimedia.org
+ aculo.us
+ bitly.com
+ breakingtweets.com
+ sinomontreal.ca
+ secretchina.com
+ gradconnection.com
+ scmpchinese.com
+ rfi.my
+ voagd.com
+ bebo.com
+ mobatek.net
+ atgfw.org
+ weiboleak.com
+ support
+ osfoora.com
+ hrichina.org
+ tibetwrites.org
+ mingpaony.com
+ chinatweeps.com
+ topify.com
+ transgressionism.org
+ slickvpn.com
+ stuffimreading.com
+ mizzmona.com
+ lester850.info
+ stackfile.com
+ uyghurcongress.org
+ bullog.org
+ 24smile.org
+ megurineluka.com
+ friendfeed.com
+ liudejun.com
+ fanswong.com
+ sexinsex.net
+ boxun.com
+ tiandixing.org
+ oizoblog.com
+ exploader.net
+ roodo.com
+ tbpic.info
+ putlocker.com
+ eevpn.com
+ dafagood.com
+ sinocast.com
+ opera.com
+ mpfinance.com
+ pornvisit.com
+ taiwannation.com.tw
+ ifjc.org
+ upload4u.info
+ prosiben.de
+ bayvoice.net
+ sina.com.tw
+ referer.us
+ trendsmap.com
+ fgmtv.net
+ readingtimes.com.tw
+ 1pondo.tv
+ xfm.pp.ru
+ xfiles.to
+ newtaiwan.com.tw
+ epochtimes-romania.com
+ 4chan.org
+ bbc.in
+ romanandreg.com
+ urlparser.com
+ peopo.org
+ ipicture.ru
+ dotsub.com
+ mathiew-badimon.com
+ rockmelt.com
+ twittbot.net
+ sockslist.net
+ keepandshare.com
+ avoision.com
+ coveringweb.com
+ unix100.com
+ sogoo.org
+ goldenmelody.com.tw
+ wanderinghorse.net
+ vot.org
+ chinacomments.org
+ wikileaks.ch
+ ronjoneswriter.com
+ bbsfeed.com
+ facebook.net
+ mymaji.com
+ iask.bz
+ tubecao.com
+ dontmovetochina.com
+ hidemyass.com
+ myparagliding.com
+ pandora.com
+ getcloudapp.com
+ klip.me
+ imagevenue.com
+ chinafreepress.org
+ streetvoice.com
+ zhe.la
+ hsjp.net
+ xh4n.cn
+ botanwang.com
+ dropbox.com
+ hellouk.org
+ animecrazy.net
+ navigeaters.com
+ s8forum.com
+ picturesocial.com
+ bullogger.com
+ 888.com
+ offbeatchina.com
+ seezone.net
+ frontlinedefenders.org
+ theblemish.com
+ internet.org
+ anthonycalzadilla.com
+ feelssh.com
+ rsf.org
+ lvhai.org
+ boardreader.com
+ owl.li
+ geocities.com
+ nobelprize.org
+ pornmm.net
+ wuerkaixi.com
+ sina.com.hk
+ heiyo.info
+ foxtang.com
+ tnaflix.com
+ tuidang.org
+ paperb.us
+ billypan.com
+ zvereff.com
+ openvpn.net
+ pastebin.com
+ kaiyuan.de
+ ameblo.jp
+ findbook.tw
+ ccthere.com
+ markmilian.com
+ goagentplus.com
+ cdpusa.org
+ newgrounds.com
+ xpdo.net
+ rapbull.net
+ innermongolia.org
+ feedbooks.mobi
+ tumblweed.org
+ feedzshare.com
+ blogcatalog.com
+ xcritic.com
+ lsmchinese.org
+ davidziegler.net
+ sneakme.net
+ hwinfo.com
+ vpnfire.com
+ law.com
+ tsemtulku.com
+ spb.com
+ i1.hk
+ parislemon.com
+ vimeo.com
+ grandtrial.org
+ holyspiritspeaks.org
+ 2008xianzhang.info
+ archive.is
+ zaobao.com
+ tmi.me
+ nobodycanstop.us
+ whylover.com
+ starp2p.com
+ dalailamaworld.com
+ lastfm.es
+ hkfront.org
+ pbxes.com
+ dnscrypt.org
+ getfreedur.com
+ bobulate.com
+ sevenload.com
+ lockdown.com
+ idsam.com
+ twitter4j.org
+ liansi.org
+ metacafe.com
+ twistory.net
+ zaozon.com
+ peeasian.com
+ mixero.com
+ thepiratebay.org
+ toutfr.com
+ tokyo-247.com
+ twreg.info
+ twitzap.com
+ bignews.org
+ tora.to
+ fileserve.com
+ muzu.tv
+ shitaotv.org
+ wengewang.org
+ geek-art.net
+ gongmeng.info
+ china21.com
+ wenhui.ch
+ uni.cc
+ feedburner.com
+ webfee.tk
+ sharecool.org
+ sex-11.com
+ vanemu.cn
+ marc.info
+ fapdu.com
+ coolloud.org.tw
+ verizon.net
+ sacom.hk
+ imdb.com
+ wikiwiki.jp
+ wufi.org.tw
+ bt95.com
+ hackthatphone.net
+ googledrive.com
+ neverforget8964.org
+ naver.jp
+ yong.hu
+ israbox.com
+ iask.ca
+ duckduckgo.com
+ riku.me
+ prestige-av.com
+ alwaysdata.com
+ ogaoga.org
+ org.uk
+ williamhill.com
+ expatshield.com
+ secureserver.net
+ gardennetworks.com
+ ntdtv.co
+ actimes.com.au
+ tjholowaychuk.com
+ huanghuagang.org
+ vanilla-jp.com
+ nsc.gov.tw
+ ntdtv.ca
+ wwitv.com
+ chinaaffairs.org
+ privatetunnel.com
+ kinghost.com
+ cdig.info
+ usgs.gov
+ hkreporter.com
+ newscn.org
+ simpleproductivityblog.com
+ box.net
+ freexinwen.com
+ scriptspot.com
+ chenguangcheng.com
+ karayou.com
+ saiq.me
+ heungkongdiscuss.com
+ ggssl.com
+ zhuichaguoji.org
+ china101.com
+ hideipvpn.com
+ sina.com
+ orn.jp
+ gartlive.com
+ clientsfromhell.net
+ allinfa.com
+ ccavtop10.com
+ twittergadget.com
+ cecc.gov
+ opendemocracy.net
+ clipfish.de
+ xskywalker.com
+ paint.net
+ vcfbuilder.org
+ procopytips.com
+ internetdefenseleague.org
+ pidown.com
+ openinkpot.org
+ mobypicture.com
+ cdnews.com.tw
+ lovequicksilver.com
+ singtao.ca
+ soc.mil
+ woeser.com
+ weijingsheng.org
+ doubleaf.com
+ hanunyi.com
+ tinychat.com
+ mp3ye.eu
+ 9bis.com
+ qkshare.com
+ time.com
+ newspeak.cc
+ tripod.com
+ chevronwp7.com
+ spencertipping.com
+ jackjia.com
+ c-spanvideo.org
+ zonaeuropa.com
+ minghui-school.org
+ stupidvideos.com
+ mingpaosf.com
+ tiscali.it
+ hongzhi.li
+ hihiforum.com
+ moztw.org
+ slheng.com
+ flnet.org
+ chinayouth.org.hk
+ twitiq.com
+ chinaaid.org
+ vaayoo.com
+ userdefined2.com
+ bloglines.com
+ whydidyoubuymethat.com
+ pixnet.net
+ photofocus.com
+ laomiu.com
+ songjianjun.com
+ genuitec.com
+ com.uk
+ monitorchina.org
+ t66y.com
+ files2me.com
+ wforum.com
+ gaeproxy.googlecode.com
+ bestvpnservice.com
+ killwall.com
+ tchrd.org
+ chinamz.org
+ feministteacher.com
+ shadowsocks.org
+ wattpad.com
+ wow-life.net
+ twitvid.com
+ twitter.jp
+ kzeng.info
+ thebodyshop-usa.com
+ youxu.info
+ lianyue.net
+ youtube.com
+ zhreader.com
+ tcno.net
+ ncn.org
+ 12bet.com
+ tweeplike.me
+ analyze-v.com
+ edubridge.com
+ cari.com.my
+ ezpeer.com
+ 2000fun.com
+ bnrmetal.com
+ nf.id.au
+ sowers.org.hk
+ yzzk.com
+ tampabay.com
+ danke4china.net
+ epochtimes.co.kr
+ nowtorrents.com
+ atlaspost.com
+ chinaway.org
+ stickam.com
+ dotheyfolloweachother.com
+ asianews.it
+ fmnnow.com
+ 9001700.com
+ showtime.jp
+ thetrotskymovie.com
+ vimgolf.com
+ kendincos.net
+ kurtmunger.com
+ igvita.com
+ maruta.be
+ xtube.com
+ qusi8.net
+ hk-pub.com
+ nurgo-software.com
+ byethost8.com
+ freebearblog.org
+ touch99.com
+ graphis.ne.jp
+ myeclipseide.com
+ linux-engineer.net
+ great-roc.org
+ rthk.hk
+ chinainperspective.net
+ ozchinese.com
+ twilio.com
+ istockphoto.com
+ sinoants.com
+ goodreaders.com
+ asiaharvest.org
+ lidecheng.com
+ t.co
+ twstar.net
+ epochtimes.com
+ ilove80.be
+ new-akiba.com
+ xmusic.fm
+ ntdtv.ru
+ tvboxnow.com
+ wisevid.com
+ hku.hk
+ zsrhao.com
+ bet365.com
+ power.com
+ ernestmandel.org
+ boxunblog.com
+ geocities.jp
+ tibetonline.tv
+ fanqianghou.com
+ modfetish.com
+ jobso.tv
+ ait.org.tw
+ ow.ly
+ ignitedetroit.net
+ topshare.us
+ unholyknight.com
+ cctongbao.com
+ neighborhoodr.com
+ sinocism.com
+ twitthat.com
+ ranyunfei.com
+ sharpdaily.com.hk
+ porn2.com
+ hyperrate.com
+ minzhuhua.net
+ ganges.com
+ tweepmag.com
+ ipvanish.com
+ idiomconnection.com
+ bitshare.com
+ iconpaper.org
+ mcfog.com
+ dajiyuan.eu
+ over-blog.com
+ tynsoe.org
+ homeservershow.com
+ khmusic.com.tw
+ dabr.me
+ hiitch.com
+ tmagazine.com
+ zuo.la
+ kissbbao.cn
+ tycool.com
+ skyhighpremium.com
+ kui.name
+ eyevio.jp
+ yyii.org
+ proxy.org
+ thomasbernhard.org
+ itweet.net
+ futureme.org
+ greatzhonghua.org
+ muzi.net
+ alvinalexander.com
+ fqrouter.com
+ hua-yue.net
+ skimtube.com
+ duckmylife.com
+ twitter.com
+ lenwhite.com
+ epochtimes.se
+ tokyo-hot.com
+ asdfg.jp
+ cnd.org
+ imageshack.us
+ getjetso.com
+ pubu.com.tw
+ yymaya.com
+ seesmic.com
+ videomo.com
+ hotpotato.com
+ retweeteffect.com
+ warehouse333.com
+ sproutcore.com
+ getsmartlinks.com
+ heix.pp.ru
+ sadpanda.us
+ aboluowang.com
+ jayparkinsonmd.com
+ fuckgfw.org
+ wangafu.net
+ bralio.com
+ sourceforge.net
+ pornstarclub.com
+ wordboner.com
+ jqueryui.com
+ mcadforums.com
+ freegao.com
+ twibs.com
+ ccue.com
+ wanglixiong.com
+ zhanbin.net
+ aol.com
+ kompozer.net
+ plusbb.com
+ tweetymail.com
+ simplecd.org
+ jbtalks.cc
+ privatepaste.com
+ lalulalu.com
+ fastly.net
+ freetibet.org
+ nuexpo.com
+ businessweek.com
+ ssh91.com
+ isgreat.org
+ 666kb.com
+ hrw.org
+ tidyread.com
+ ajaxplorer.info
+ userdefined.com
+ caobian.info
+ keso.cn
+ incredibox.fr
+ twibbon.com
+ isuntv.com
+ tvider.com
+ helpeachpeople.com
+ hutianyi.net
+ amnesty.org
+ xys.org
+ namsisi.com
+ redtube.com
+ teamseesmic.com
+ utom.us
+ tibet.org.tw
+ md-t.org
+ zhongguotese.net
+ msguancha.com
+ perlhowto.com
+ multiproxy.org
+ wengewang.com
+ sexhu.com
+ 0rz.tw
+ zonble.net
+ jkforum.net
+ sis001.us
+ whatblocked.com
+ cotweet.com
+ xuite.net
+ citizenlab.org
+ faststone.org
+ vapurl.com
+ value-domain.com
+ erights.net
+ anobii.com
+ pign.net
+ mog.com
+ fsurf.com
+ fredwilson.vc
+ zacebook.com
+ hechaji.com
+ x-berry.com
+ tkforum.tk
+ pagodabox.com
+ dl-laby.jp
+ thesartorialist.com
+ soup.io
+ youporn.com
+ dayabook.com
+ dailidaili.com
+ mx981.com
+ fangong.org
+ chinaaid.us
+ powercx.com
+ 9bis.net
+ duplicati.com
+ 141hongkong.com
+ bonjourlesgeeks.com
+ interestinglaugh.com
+ ippotv.com
+ peerpong.com
+ 7capture.com
+ uhrp.org
+ freewallpaper4.me
+ marco.org
+ trtc.com.tw
+ here4news.com
+ mixpod.com
+ fengzhenghu.com
+ taiwan-sex.com
+ xiaod.in
+ packetix.net
+ im88.tw
+ epochtimes.ru
+ chinatimes.com
+ j.mp
+ voachinese.com
+ laoyang.info
+ codeshare.io
+ dailymotion.com
+ new-3lunch.net
+ bcc.com.tw
+ news100.com.tw
+ voachineseblog.com
+ eamonnbrennan.com
+ topstyle4.com
+ wpoforum.com
+ freealim.com
+ vpngate.net
+ x1949x.com
+ twitterfeed.com
+ palacemoon.com
+ avdb.in
+ mhradio.org
+ webworkerdaily.com
+ fillthesquare.org
+ dadazim.com
+ thehousenews.com
+ yvesgeleyn.com
+ loved.hk
+ dontfilter.us
+ catholic.org.tw
+ civicparty.hk
+ on.cc
+ zarias.com
+ ccue.ca
+ pastie.org
+ web2project.net
+ aisex.com
+ curvefish.com
+ slime.com.tw
+ marxists.org
+ chubun.com
+ liuhanyu.com
+ boxun.tv
+ xuzhiyong.net
+ joeedelman.com
+ dynawebinc.com
+ jbtalks.com
+ iu45.com
+ w3.org
+ davidslog.com
+ tv-intros.com
+ dafahao.com
+ mingpaotor.com
+ nekoslovakia.net
+ epochtimes.de
+ zhong.pp.ru
+ chinalawtranslate.com
+ freessh.us
+ cuhkacs.org
+ fanglizhi.info
+ fdbox.com
+ ozyoyo.com
+ panluan.net
+ shenzhoufilm.com
+ cafepress.com
+ debian.org
+ fanyue.info
+ worldcat.org
+ dtiserv2.com
+ mgstage.com
+ xnxx.com
+ co.hk
+ twitpic.com
+ path.com
+ nicovideo.jp
+ zeutch.com
+ newchen.com
+ lrfz.com
+ freeopenvpn.com
+ pbs.org
+ myspace.com
+ idaiwan.com
+ foxsub.com
+ tiffanyarment.com
+ get-digital-help.com
+ secretgarden.no
+ mixx.com
+ twibase.com
+ uploaded.to
+ ialmostlaugh.com
+ tzangms.com
+ thechinabeat.org
+ co.tv
+ jingpin.org
+ facebook.com
+ space-scape.com
+ dadi360.com
+ tomsc.com
+ 4sq.com
+ myaudiocast.com
+ friendfeed-media.com
+ nodesnoop.com
+ newyorktimes.com
+ rxhj.net
+ qienkuen.org
+ uwants.net
+ culture.tw
+ identi.ca
+ list.ly
+ duckload.com
+ videobam.com
+ stoweboyd.com
+ zkaip.com
+ vpnpronet.com
+ nps.gov
+ wqyd.org
+ togetter.com
+ taipeisociety.org
+ torproject.org
+ tw
+ gclooney.com
+ googlesyndication.com
+ hkej.com
+ blog.de
+ koolsolutions.com
+ mmmca.com
+ great-firewall.com
+ chrlcg-hk.org
+ movabletype.com
+ immigration.gov.tw
+ dw-world.de
+ anchorfree.com
+ gospelherald.com
+ purevpn.com
+ voacantonese.com
+ djangosnippets.org
+ hasaowall.com
+ tweetwally.com
+ pokerstrategy.com
+ xing.com
+ podictionary.com
+ stickeraction.com
+ derekhsu.homeip.net
+ sis.xxx
+ wzyboy.im
+ buzzurl.jp
+ twistar.cc
+ taa-usa.org
+ appledaily.com
+ nysingtao.com
+ tuxtraining.com
+ cz.cc
+ xbookcn.com
+ witopia.net
+ wenyunchao.com
+ travelinlocal.com
+ thetibetpost.com
+ xyy69.com
+ szbbs.net
+ canadameet.com
+ backpackers.com.tw
+ uncyclopedia.info
+ helloandroid.com
+ greatfirewallofchina.net
+ tibetanyouthcongress.org
+ alexdong.com
+ alabout.com
+ sexandsubmission.com
+ pbxes.org
+ hutong9.net
+ verybs.com
+ blogspot.jp
+ avidemux.org
+ alkasir.com
+ shinychan.com
+ yasukuni.or.jp
+ shenshou.org
+ tube.com
+ xizang-zhiye.org
+ galenwu.com
+ areca-backup.org
+ bao.li
+ multiupload.com
+ greenvpn.net
+ thedieline.com
+ berlintwitterwall.com
+ dfanning.com
+ molihua.org
+ hungerstrikeforaids.org
+ npa.go.jp
+ 10musume.com
+ wet123.com
+ newlandmagazine.com.au
+ gather.com
+ bcchinese.net
+ sesawe.net
+ usejump.com
+ funp.com
+ mh4u.org
+ wukangrui.net
+ jeanyim.com
+ chinaworker.info
+ maxgif.com
+ pixelqi.com
+ sjum.cn
+ mgoon.com
+ cbsnews.com
+ chengmingmag.com
+ all-that-is-interesting.com
+ soumo.info
+ xpud.org
+ naacoalition.org
+ anyu.org
+ latelinenews.com
+ s1heng.com
+ e-info.org.tw
+ globaljihad.net
+ wenku.com
+ mitbbs.com
+ kakao.com
+ wiredbytes.com
+ qxbbs.org
+ yx51.net
+ popyard.org
+ state.gov
+ yunchao.net
+ dolc.de
+ ironpython.net
+ alasbarricadas.org
+ oxid.it
+ tweetmeme.com
+ rerouted.org
+ disp.cc
+ aiweiweiblog.com
+ 123rf.com
+ macrovpn.com
+ badoo.com
+ ukchinese.com
+ memrijttm.org
+ mycould.com
+ mixedmedialabs.com
+ frommel.net
+ mininova.org
+ shahamat-english.com
+ yegle.net
+ teck.in
+ bbsland.com
+ blogtd.net
+ sshtunnel.googlecode.com
+ popularpages.net
+ savevid.com
+ pornoxo.com
+ lsmkorean.org
+ tiney.com
+ netme.cc
+ squarespace.com
+ hkgolden.com
+ voatibetan.com
+ speckleapp.com
+ newtalk.tw
+ shkspr.mobi
+ politicalchina.org
+ ddc.com.tw
+ thepiratebay.se
+ linuxreviews.org
+ cdp1998.org
+ twisternow.com
+ 881903.com
+ moby.to
+ woopie.tv
+ zdnet.com.tw
+ nch.com.tw
+ dojin.com
+ ned.org
+ ibiblio.org
+ fakku.net
+ wuala.com
+ daxa.cn
+ lightbox.com
+ yhcw.net
+ iphonix.fr
+ twit2d.com
+ netflix.com
+ gutteruncensored.com
+ hdtvb.net
+ shapeservices.com
+ westernwolves.com
+ psiphon.ca
+ rthk.org.hk
+ washeng.net
+ purepdf.com
+ larsgeorge.com
+ hloli.net
+ co.uk
+ twbbs.net.tw
+ zgzcjj.net
+ linkideo.com
+ softether.co.jp
+ phuquocservices.com
+ drgan.net
+ pcdiscuss.com
+ sitemaps.org
+ abc.pp.ru
+ linuxtoy.org
+ thedailywh.at
+ addictedtocoffee.de
+ getchu.com
+ geekerhome.com
+ greatroc.org
+ alternate-tools.com
+ marxist.com
+ kanzhongguo.com
+ greatroc.tw
+ pk.com
+ palmislife.com
+ dlsite.com
+ qooza.hk
+ crossthewall.net
+ twbbs.org
+ xinshijue.com
+ shvoong.com
+ woopie.jp
+ hqcdp.org
+ freenewscn.com
+ hkjp.org
+ stoneip.info
+ googleusercontent.com
+ amazon.com
+ old-cat.net
+ shadow.ma
+ instagram.com
+ ht.ly
+ sydneytoday.com
+ olympicwatch.org
+ whippedass.com
+ tafaward.com
+ falunart.org
+ unknownspace.org
+ tweetrans.com
+ jyxf.net
+ vmixcore.com
+ uushare.com
+ wikileaks.de
+ flickrhivemind.net
+ sis001.com
+ workatruna.com
+ memedia.cn
+ gpass1.com
+ megarotic.com
+ pikchur.com
+ dev102.com
+ zengjinyan.org
+ centralnation.com
+ puffinbrowser.com
+ pandora.tv
+ amiblockedornot.com
+ zhinengluyou.com
+ bannedbook.org
+ chinesenewsnet.com
+ nokogiri.org
+ wolfax.com
+ ifcss.org
+ pwned.com
+ thumbzilla.com
+ icerocket.com
+ koornk.com
+ leecheukyan.org
+ theqii.info
+ blip.tv
+ tweetbackup.com
+ updatestar.com
+ gzone-anime.info
+ 1984bbs.org
+ penthouse.com
+ ff.im
+ uploadstation.com
+ expofutures.com
+ alliance.org.hk
+ wearn.com
+ efcc.org.hk
+ zattoo.com
+ askynz.net
+ zaobao.com.sg
+ heartyit.com
+ qixianglu.cn
+ bettween.com
+ gun-world.net
+ kenengba.com
+ iphone-dev.org
+ crackle.com
+ twftp.org
+ zlib.net
+ multiply.com
+ lipuman.com
+ monlamit.org
+ porntube.com
+ tunein.com
+ uwants.com
+ gotw.ca
+ tumutanzi.com
+ opera-mini.net
+ dizhidizhi.com
+ pbwiki.com
+ ruyiseek.com
+ 12vpn.com
+ eltondisney.com
+ free.fr
+ 1bao.org
+ extremetube.com
+ yeelou.com
+ corumcollege.com
+ merit-times.com.tw
+ exblog.co.jp
+ hkheadline.com
+ uocn.org
+ wikileaks.org
+ taiwanyes.com
+ chinesepen.org
+ itaboo.info
+ thisiswhyyouarefat.com
+ martincartoons.com
+ theinternetwishlist.com
+ twitgether.com
+ hecaitou.net
+ collateralmurder.com
+ baixing.me
+ lesscss.org
+ uk.to
+ wikisource.org
+ nanyang.com
+ wikia.com
+ michaelanti.com
+ 2shared.com
+ chinagreenparty.org
+ fireofliberty.org
+ isohunt.com
+ ibtimes.com
+ meirixiaochao.com
+ rutube.ru
+ erepublik.com
+ briefdream.com
+ vocn.tv
+ ironicsoftware.com
+ htxt.it
+ mayimayi.com
+ businesstimes.com.cn
+ dayoneapp.com
+ beijing1989.com
+ kingdomsalvation.org
+ vpnbook.com
+ ping.fm
+ pentalogic.net
+ wan-press.org
+ hudatoriq.web.id
+ url.com.tw
+ duihua.org
+ bowenpress.com
+ fotop.net
+ liveleak.com
+ tvb.com
+ ulike.net
+ 365singles.com.ar
+ calebelston.com
+ turbobit.net
+ liujianshu.com
+ daylife.com
+ tuitui.info
+ allinfo.com
+ lsd.org.hk
+ mingjinglishi.com
+ qx.net
+ wepn.info
+ tenacy.com
+ parade.com
+ mobile01.com
+ cynscribe.com
+ lockestek.com
+ dtic.mil
+ jinhai.de
+ x-art.com
+ atchinese.com
+ gazotube.com
+ nownews.com
+ 64wiki.com
+ bloglovin.com
+ v-state.org
+ popyard.com
+ easyweb.hk
+ oiktv.com
+ ytht.net
+ willw.net
+ voy.com
+ chinahush.com
+ eyespirit.info
+ wangjinbo.org
+ dowei.org
+ tianhuayuan.com
+ ncol.com
+ goagent.googlecode.com
+ orzdream.com
+ noobbox.com
+ gongminliliang.com
+ heywire.com
+ cdjp.org
+ code1984.com
+ gzm.tv
+ sharebee.com
+ youthbao.com
+ chinadigitaltimes.net
+ tagwalk.com
+ chinagfw.org
+ wenxuecity.com
+ veoh.com
+ dongyangjing.com
+ lizhizhuangbi.com
+ tangben.com
+ logbot.net
+ fc2blog.net
+ techlifeweb.com
+ junefourth-20.net
+ seapuff.com
+ blogspot.hk
+ waikeung.org
+ jinbushe.org
+ my-proxy.com
+ kun.im
+ shodanhq.com
+ dw.de
+ kimy.com.tw
+ fflick.com
+ sino-monthly.com
+ slutload.com
+ postadult.com
+ falundafa.org
+ ifttt.com
+ yi.org
+ tube8.com
+ epochtimes.ie
+ opnir.com
+ marguerite.su
+ chenpokong.com
+ 2-hand.info
+ dit-inc.us
+ wikibooks.org
+ longhair.hk
+ dxiong.com
+ plixi.com
+ ntu.edu.tw
+ weekmag.info
+ img.ly
+ tuanzt.com
+ bit.ly
+ pornhub.com
+ mihua.org
+ ck101.com
+ wikileaks.eu
+ wallornot.org
+ softether.org
+ joeyrobert.org
+ twapperkeeper.com
+ rushbee.com
+ zoho.com
+ sendspace.com
+ proxomitron.info
+ allmovie.com
+ google.com
+ badassjs.com
+ howtoforge.com
+ fanqiangyakexi.net
+ googlevideo.com
+ torvpn.com
+ qvodzy.org
+ hacken.cc
+ ismprofessional.net
+ presentationzen.com
+ huping.net
+ velkaepocha.sk
+ chinamule.com
+ trulyergonomic.com
+ atnext.com
+ nccwatch.org.tw
+ globalmuseumoncommunism.org
+ streamingthe.net
+ qanote.com
+ falunhr.org
+ thehungrydudes.com
+ wordpress.com
+ nexttv.com.tw
+ discuss.com.hk
+ usmc.mil
+ basetimesheightdividedby2.com
+ oauth.net
+ wetpussygames.com
+ zshare.net
+ 89-64.org
+ linglingfa.com
+ juziyue.com
+ ruanyifeng.com
+ cnyes.com
+ aiph.net
+ skybet.com
+ vatn.org
+ hypeshell.com
+ megavideo.com
+ rojo.com
+ tibetfund.org
+ awardwinningfjords.com
+ falundafamuseum.org
+ jiepang.com
+ eulam.com
+ pchome.com.tw
+ thehun.net
+ tistory.com
+ blingblingsquad.net
+ nakido.com
+ fzh999.net
+ creaders.net
+ hkbf.org
+ wqlhw.com
+ overlapr.com
+ us.to
+ tl.gd
+ ourdearamy.com
+ epochtimes.fr
+ google.com.hk
+ wapedia.mobi
+ allaboutalpha.com
+ graylog2.org
+ metrolife.ca
+ gigporno.ru
+ moviefap.com
+ twtkr.com
+ rferl.org
+ shwchurch3.com
+ sysadmin1138.net
+ electionsmeter.com
+ cyberctm.com
+ rsf-chinese.org
+ sparrowmailapp.com
+ sex.com
+ xuzhuoer.com
+ xmovies.com
+ duoweitimes.com
+ rapidshare8.com
+ chingcheong.com
+ xhamster.com
+ moegirl.org
+ freemoren.com
+ pornbase.org
+ danwei.org
+ greenparty.org.tw
+ van698.com
+ mlcool.com
+ uighurbiz.net
+ omgili.com
+ blogspot.in
+ sharpdaily.hk
+ jiehua.cz
+ livevideo.com
+ weblagu.com
+ nlfreevpn.com
+ zyzc9.com
+ wiredpen.com
+ panoramio.com
+ sopcast.org
+ mysinablog.com
+ sogrady.me
+ teashark.com
+ etizer.org
+ westca.com
+ forums-free.com
+ oikos.com.tw
+ 315lz.com
+ letscorp.net
+ powerapple.com
+ hk32168.com
+ 51.ca
+ salvation.org.hk
+ rfi.fr
+ getiton.com
+ awflasher.com
+ getlantern.org
+ i2runner.com
+ hidecloud.com
+ gcpnews.com
+ marxist.net
+ vegorpedersen.com
+ twifan.com
+ tweetphoto.com
+ ovi.com
+ cubicle17.com
+ greatfire.org
+ gabocorp.com
+ tibetonline.com
+ change.org
+ turbotwitter.com
+ chinaaid.me
+ thisav.com
+ free-ssh.com
+ tubewolf.com
+ xgmyd.com
+ gfw.org.ua
+ chinesedailynews.com
+ viki.com
+ ecministry.net
+ mooo.com
+ sinoquebec.com
+ yahoo.co.jp
+ slandr.net
+ favorious.com
+ rhcloud.com
+ sthoo.com
+ softwarebychuck.com
+ illusionfactory.com
+ efksoft.com
+ staticflickr.com
+ freenet-china.org
+ cams.com
+ 21andy.com
+ megabyet.net
+ greatfirewall.biz
+ islam.org.hk
+ tiananmenmother.org
+ mirrorbooks.com
+ chinageeks.org
+ dongtaiwang.com
+ twimg.com
+ hardsextube.com
+ soundcloud.com
+ 4bluestones.biz
+ tweepguide.com
+ twitlonger.com
+ revleft.com
+ october-review.org
+ isunaffairs.com
+ codeboxapp.com
+ zinio.com
+ snaptu.com
+ fc2china.com
+ sendoid.com
+ giganews.com
+ huaglad.com
+ youthwant.com.tw
+ eventful.com
+ yeeyi.com
+ istef.info
+ playboy.com
+ youjizz.com
+ thelius.org
+ imkev.com
+ websitepulse.com
+ listorious.com
+ open.com.hk
+ youversion.com
+ rlwlw.com
+ twitcause.com
+ flickr.com
+ ajsands.com
+ softether-download.com
+ waiwaier.com
+ site90.net
+ hootsuite.com
+ dongtaiwang.net
+ telecomspace.com
+ delcamp.net
+ dalailama.ru
+ vpnpop.com
+ 301works.org
+ chinaeweekly.com
+ youpai.org
+ echofon.com
+ nokola.com
+ tsctv.net
+ aol.ca
+ tsunagarumon.com
+ hinet.net
+ dropboxusercontent.com
+ cherrysave.com
+ mondex.org
+ dwnews.com
+ hjclub.info
+ h1n1china.org
+ raidcall.com.tw
+ goofind.com
+ weiming.info
+ hotspotshield.com
+ sopcast.com
+ aobo.com.au
+ fangongheike.com
+ i2p2.de
+ kingstone.com.tw
+ proxifier.com
+ twilog.org
+ oursteps.com.au
+ twittermail.com
+ reflectivecode.com
+ freechal.com
+ fail.hk
+ tt1069.com
+ 6park.com
+ piring.com
+ internetpopculture.com
+ tuite.googlecode.com
+ vpngate.jp
+ ub0.cc
+ zootool.com
+ huaxin.ph
+ melon-peach.com
+ d0z.net
+ cenci.tk
+ tianzhu.org
+ hkptu.org
+ gamer.com.tw
+ wellplacedpixels.com
+ recordhistory.org
+ exblog.jp
+ tsquare.tv
+ renminbao.com
+ brucewang.net
+ sesawe.org
+ vincnd.com
+ edoors.com
+ southnews.com.tw
+ etaiwannews.com
+ malaysiakini.com
+ udn.com
+ am730.com.hk
+ wexiaobo.org
+ unicode.org
+ soifind.com
+ favstar.fm
+ zomobo.net
+ braumeister.org
+ sammyjs.org
+ wujieliulan.com
+ revver.com
+ twitturly.com
+ getsocialscope.com
+ compython.net
+ webbang.net
+ citizensradio.org
+ gowalla.com
+ paper.li
+ dtiblog.com
+ sejie.com
+ meteorshowersonline.com
+ faydao.com
+ trialofccp.org
+ fb.com
+ comedycentral.com
+ linksalpha.com
+ zmw.cn
+ tweetmylast.fm
+ blogspot.fr
+ 6v6dota.com
+ omnitalk.com
+ idlcoyote.com
+ ning.com
+ libertytimes.com.tw
+ democrats.org
+ orchidbbs.com
+ china21.org
+ bbcchinese.com
+ af.mil
+ pin6.com
+ sankaizok.com
+ feer.com
+ avaaz.org
+ bloomfortune.com
+ sfileydy.com
+ nanyangpost.com
+ python.com
+ sandnoble.com
+ read100.com
+ classicalguitarblog.net
+ buugaa.com
+ amnestyusa.org
+ thereallove.kr
+ antiwave.net
+ fawanghuihui.org
+ x-wall.org
+ daolan.net
+ gfwinterceptor.googlecode.com
+ taiwanus.net
+ seraph.me
+ szetowah.org.hk
+ yuanming.net
+ geekmanuals.com
+ guancha.org
+ chinayuanmin.org
+ nga.mil
+ my903.com
+ archive.org
+ tamiaode.tk
+ netlog.com
+ onmoon.com
+ jiaoyou8.com
+ twibble.de
+ gaymap.cc
+ xinhuanet.org
+ yorkbbs.ca
+ scribd.com
+ blogspot.com
+ taragana.com
+ kl.am
+ rti.org.tw
+ relaxbbs.com
+ cellulo.info
+ owind.com
+ ucam.org
+ phonegap.com
+ navy.mil
+ tibet.net
+ uncyclomedia.org
+ vpncup.com
+ oopsforum.com
+ orientaldaily.com.my
+ hkbc.net
+ youtube-nocookie.com
+ winwhispers.info
+ savetibet.org
+ sohfrance.org
+ thegatesnotes.com
+ netcolony.com
+ kechara.com
+ break.com
+ cenews.eu
+ xxxx.com.au
+ muouju.com
+ h-china.org
+ voanews.com
+ proxyroad.com
+ tttan.com
+ dpp.org.tw
+ gdbt.net
+ hnjhj.com
+ thkphoto.com
+ la-forum.org
+ weiquanwang.org
+ nabble.com
+ tweetedtimes.com
+ okayfreedom.com
+ twimbow.com
+ cclife.org
+ zhenlibu.info
+ hkday.net
+ 1984bbs.com
+ iphonehacks.com
+ atebits.com
+ solozorro.tk
+ justtristan.com
+ pmates.com
+ waigaobu.com
+ naol.ca
+ adultfriendfinder.com
+ collateralmurder.org
+ al-qimmah.net
+ twitbrowser.net
+ jbtalks.my
+ mediafire.com
+ theatrum-belli.com
+ geocities.co.jp
+ listentoyoutube.com
+ somee.com
+ yahoo.com.hk
+ sexhuang.com
+ stonegames.net
+ dzze.com
+ python.com.tw
+ throughnightsfire.com
+ liuxiaotong.com
+ blogs.com
+ artsy.net
+ mashable.com
+ axureformac.com
+ 64tianwang.com
+ domain.club.tw
+ imageflea.com
+ wangruoshui.net
+ dougscripts.com
+ yogichen.org
+ bewww.net
+ gongm.in
+ shenyunperformingarts.org
+ sheikyermami.com
+ pekingduck.org
+ radiotime.com
+ chinasoul.org
+ kwongwah.com.my
+ blinw.com
+ atj.org.tw
+ aenhancers.com
+ ytimg.com
+ freevpn.nl
+ netfirms.com
+ windowsphoneme.com
+ xiaochuncnjp.com
+ chinainterimgov.org
+ xysblogs.org
+ epochtimes-bg.com
+ samair.ru
+ taiwankiss.com
+ hotpot.hk
+ books.com.tw
+ proxlet.com
+ gyalwarinpoche.com
+ focusvpn.com
+ dphk.org
+ oulove.org
+ filefactory.com
+ ifanr.com
+ urlborg.com
+ minzhuzhongguo.org
+ fb.me
+ icij.org
+ twindexx.com
+ twitreferral.com
+ linuxconfig.org
+ cts.com.tw
+ sanmin.com.tw
+ logmike.com
+ ultraxs.com
+ highrockmedia.com
+ theappleblog.com
+ taiwannews.com.tw
+ twa.sh
+ tacem.org
+ percy.in
+ apigee.com
+ itshidden.com
+ birdhouseapp.com
+ dongde.com
+ mefeedia.com
+ embr.in
+ clb.org.hk
+ chaturbate.com
+ strongvpn.com
+ asianwomensfilm.de
+ bugclub.org
+ xvedios.com
+ mpinews.com
+ x365x.com
+ arctosia.com
+ tiantibooks.org
+ worstthingieverate.com
+ kodingen.com
+ twyac.org
+ shizhao.org
+ budaedu.org
+ rcinet.ca
+ soh.tw
+ date.fm
+ izihost.org
+ threatchaos.com
+ paper-replika.com
+ mychat.to
+ farwestchina.com
+ inxian.com
+ xxbbx.com
+ taiwantt.org.tw
+ voa.mobi
+ huaren.us
+ twblogger.com
+ penchinese.com
+ everyday-carry.com
+ hotfile.com
+ sitetag.us
+ twaud.io
+ singtao.com
+ idemocracy.asia
+ chinainperspective.org
+ soundofhope.org
+ toonel.net
+ city9x.com
+ ufreevpn.com
+ sufeng.org
+ veempiire.com
+ delicious.com
+ thebcomplex.com
+ noypf.com
+ radiovaticana.org
+ igfw.net
+ chandoo.org
+ tomayko.com
+ tonyyan.net
+ ucdc1998.org
+ hkjc.com
+ xiaoma.org
+ so-ga.net
+ sapikachu.net
+ pacificpoker.com
+ unpo.org
+ sitebro.tw
+ blockcn.com
+ prayforchina.net
+ gmhz.org
+ spotify.com
+ gunsamerica.com
+ openwebster.com
+ liu.lu
+ muselinks.co.jp
+ 36rain.com
+ peacefire.org
+ cytode.us
+ yahoo.com
+ hongmeimei.com
+ waffle1999.com
+ globalvoicesonline.org
+ vft.com.tw
+ huluim.com
+ livestation.com
+ onmoon.net
+ pixnet.in
+ nanzao.com
+ tiananmenuniv.net
+ sinica.edu.tw
+ wezone.net
+ cochina.org
+ astonmartinnews.com
+ baywords.com
+ twitoaster.com
+ s135.com
+ iredmail.org
+ fring.com
+ spankwire.com
+ nytco.com
+ fofg.org
+ qtrac.eu
+ proxypy.net
+ tweepml.org
+ izles.net
+ cactusvpn.com
+ blogger.com
+ have8.com
+ retweetrank.com
+ mychinamyhome.com
+ hellotxt.com
+ chrispederick.net
+ goodreads.com
+ cjb.net
+ andfaraway.net
+ cdp2006.org
+ xyy69.info
+ youthnetradio.org
+ forum4hk.com
+ chinalawandpolicy.com
+ civisec.org
+ junauza.com
+ mihk.hk
+ palm.com
+ student.tw
+ svwind.com
+ chrlawyers.hk
+ pinoy-n.com
+ girlbanker.com
+ usacn.com
+ privateinternetaccess.com
+ hgseav.com
+ advertfan.com
+ xiaohexie.com
+ lazarsearlymusic.com
+ chinesetalks.net
+ bloomberg.com
+ fleshbot.com
+ higfw.com
+ vinniev.com
+ justin.tv
+ adultkeep.net
+ jieshibaobao.com
+ bfsh.hk
+ loiclemeur.com
+ bfnn.org
+ faiththedog.info
+ gtap.googlecode.com
+ picidae.net
+ epochweekly.com
+ free-gate.org
+ freelotto.com
+ himemix.com
+ tweetree.com
+ rfachina.com
+ truthcn.com
+ xcity.jp
+ newsancai.com
+ game735.com
+ caochangqing.com
+ liberal.org.hk
+ mrtweet.com
+ gamez.com.tw
+ qi-gong.me
+ steel-storm.com
+ turntable.fm
+ couchdbwiki.com
+ flecheinthepeche.fr
+ swift-tools.net
+ mmaaxx.com
+ fdc89.jp
+ emory.edu
+ eic-av.com
+ topsy.com
+ kyohk.net
+ pose.com
+ neolee.cn
+ edicypages.com
+ designerol.com
+ knowledgerush.com
+ ebookee.com
+ so-news.com
+ mthruf.com
+ getfoxyproxy.org
+ pathtosharepoint.com
+ logiqx.com
+ doxygen.org
+ say2.info
+ deutsche-welle.de
+ dabr.mobi
+ catch22.net
+ digitalnomadsproject.org
+ github.com
+ blogspot.de
+ pcdvd.com.tw
+ getyouram.com
+ compileheart.com
+ hidden-advent.org
+ megaporn.com
+ hougaige.com
+ aboutgfw.com
+ comefromchina.com
+ e-spacy.com
+ funf.tw
+ worldjournal.com
+ olumpo.com
+ finler.net
+ zoozle.net
+ vcf-online.org
+ wikimedia.org.mo
+ de-sci.org
+ stuffimreading.net
+ state168.com
+ cantonese.asia
+ fzh999.com
+ ipobar.com
+ topshareware.com
+ pokerstars.com
+ bloomberg.cn
+ qstatus.com
+ muzi.com
+ syx86.cn
+ changp.com
+ techparaiso.com
+ posterous.com
+ interfaceaddiction.com
+ cnitter.com
+ chinaaid.net
+ fscked.org
+ storagenewsletter.com
+ guishan.org
+ christusrex.org
+ syncback.com
+ tweetboard.com
+ powerpointninja.com
+ orient-doll.com
+ navicat.com
+ twittercounter.com
+ git-scm.com
+ martau.com
+ inmediahk.net
+ aliengu.com
+ taweet.com
+ 0to255.com
+ duping.net
+ tkcs-collins.com
+ trustedbi.com
+ nighost.org
+ babynet.com.hk
+ mmdays.com
+ appspot.com
+ dollf.com
+ epochtimestr.com
+ hellonewyork.us
+ 1eew.com
+ taiwannation.com
+ wezhiyong.org
+ csuchen.de
+ wahas.com
+ lookingglasstheatre.org
+ furinkan.com
+ observechina.net
+ chinachannel.hk
+ sysresccd.org
+ meetup.com
+ renyurenquan.org
+ chinese-memorial.org
+ canyu.org
+ jgoodies.com
+ china-week.com
+ pureconcepts.net
+ sowiki.net
+ kangye.org
+ br.st
+ yfrog.com
+ msn.com.tw
+ mingpao.com
+ crd-net.org
+ hk
+ googleapis.com
+ ydy.com
+ bipic.net
+ tibet.com
+ naitik.net
+ cuihua.org
+ gs-discuss.com
+ diaoyuislands.org
+ wozy.in
+ xthost.info
+ myopenid.com
+ hkdailynews.com.hk
+ glennhilton.com
+ christianstudy.com
+ freenetproject.org
+ ebookbrowse.com
+ gaoming.net
+ madmenunbuttoned.com
+ laptoplockdown.com
+ freakshare.com
+ e-traderland.net
+ twtrland.com
+ huhaitai.com
+ askstudent.com
+ yipub.com
+ truveo.com
+ tabtter.jp
+ franklc.com
+ weigegebyc.dreamhosters.com
+ iblogserv-f.net
+ kanzhongguo.eu
+ freewebs.com
+ ig.com.br
+ dwheeler.com
+ famunion.com
+ 173ng.com
+ minghui.org
+ isaacmao.com
+ perfectvpn.net
+ freeman2.com
+ sinonet.ca
+ jpopforum.net
+ sobees.com
+ free4u.com.ar
+ oyax.com
+ littlebigdetails.com
+ mingjingnews.com
+ sharkdolphin.com
+ limiao.net
+ jimoparty.com
+ assembla.com
+ dribbble.com
+ njactb.org
+ twiggit.org
+ lookatgame.com
+ darpa.mil
+ wtfpeople.com
+ tumblr.com
+ 1-apple.com.tw
+
diff --git a/conf/port_map.json b/conf/port_map.json
new file mode 100755
index 0000000..15520e5
--- /dev/null
+++ b/conf/port_map.json
@@ -0,0 +1 @@
+{"map_list":[{"dst_port":8338,"listen_port":1085,"name":"fuckGFW"}]}
\ No newline at end of file
diff --git a/conf/update.properties b/conf/update.properties
new file mode 100755
index 0000000..e62a421
--- /dev/null
+++ b/conf/update.properties
@@ -0,0 +1,2 @@
+version=-2
+url=http://baidu.com
\ No newline at end of file
diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml
new file mode 100755
index 0000000..69d7d31
--- /dev/null
+++ b/dependency-reduced-pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+ NSS
+ NSS
+ 1.0-SNAPSHOT
+
+
+
+ maven-compiler-plugin
+
+
+ 1.8
+
+
+
+ maven-shade-plugin
+ 2.3
+
+
+ package
+
+ shade
+
+
+
+
+ net.fs.client.FSClient
+
+
+
+
+
+
+
+
+
+ UTF-8
+
+
+
diff --git a/finalspeed-debian b/finalspeed-debian
new file mode 100755
index 0000000..980d212
--- /dev/null
+++ b/finalspeed-debian
@@ -0,0 +1,91 @@
+#!/bin/bash
+### BEGIN INIT INFO
+# Provides: finalspeed
+# Required-Start: $network $syslog $local_fs $remote_fs
+# Required-Stop: $network $local_fs $remote_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Start or stop the finalspeed server
+# Description: Start or stop the finalspeed server
+### END INIT INFO
+
+# Author: 91yun
+
+name=finalspeed
+BIN=/fs/fs.sh
+conf=/fs/fs.conf
+logname=/fs/server.log
+
+start(){
+ ulimit -s 65535
+ ulimit -n 65535
+ tram=$( free -m | awk '/Mem/ {print $2}' )
+ if [ "$tram" -le 128 ]; then
+ nohup java -Xmx64M -XX:MaxGCPauseMillis=30 -jar /fs/fs.jar > $logname 2>&1 &
+ elif [ "$tram" -le 256 ]; then
+ nohup java -Xmx128M -XX:MaxGCPauseMillis=30 -jar /fs/fs.jar > $logname 2>&1 &
+ elif [ "$tram" -le 512 ]; then
+ nohup java -Xmx256M -XX:MaxGCPauseMillis=30 -jar /fs/fs.jar > $logname 2>&1 &
+ elif [ "$tram" -gt 512 ]; then
+ nohup java -Xmx512M -XX:MaxGCPauseMillis=30 -jar /fs/fs.jar > $logname 2>&1 &
+ fi
+ RETVAL=$?
+ if [ "$RETVAL" = "0" ]; then
+ tail $logname
+ echo "$name start success"
+ else
+ tail $logname
+ echo "$name start failed"
+ fi
+}
+
+stop(){
+ pid=`ps -ef | grep fs.jar | grep -v grep | awk '{print $2}'`
+ if [[ ! -z $pid ]]; then
+ ps -ef | grep fs.jar | grep -v grep | awk '{print $2}' | xargs kill -s 9
+ RETVAL=$?
+ if [ "$RETVAL" = "0" ]; then
+ echo "$name stop success"
+ else
+ echo "$name stop failed"
+ fi
+ else
+ echo "$name is not running"
+ RETVAL=1
+ fi
+}
+
+status(){
+ pid=`ps -ef | grep fs.jar | grep -v grep | awk '{print $2}'`
+ if [[ -z $pid ]]; then
+ echo "$name is not running"
+ RETVAL=1
+ else
+ echo "$name is running with PID $pid"
+ tail $logname
+ RETVAL=0
+ fi
+}
+
+case "$1" in
+'start')
+ stop
+ start
+ ;;
+'stop')
+ stop
+ ;;
+'status')
+ status
+ ;;
+'restart')
+ stop
+ start
+ RETVAL=$?
+ ;;
+*)
+ echo "Usage: $0 { start | stop | restart | status }"
+ RETVAL=1
+ ;;
+esac
+exit $RETVAL
\ No newline at end of file
diff --git a/img/offline.png b/img/offline.png
new file mode 100755
index 0000000..faa2049
Binary files /dev/null and b/img/offline.png differ
diff --git a/pf.conf b/pf.conf
new file mode 100644
index 0000000..2ead535
--- /dev/null
+++ b/pf.conf
@@ -0,0 +1 @@
+block drop quick proto tcp from any to 139.162.101.111 port = 150
diff --git a/pom.xml b/pom.xml
new file mode 100755
index 0000000..3525ce0
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,244 @@
+
+
+ 4.0.0
+
+ NSS
+ NSS
+ 1.0-SNAPSHOT
+
+ UTF-8
+
+
+
+
+ src/main/java
+ target/classes
+
+
+
+ maven-assembly-plugin
+ 2.6
+
+
+ jar-with-dependencies
+
+
+
+ net.fs.netty.Start
+
+
+
+
+
+ make-assembly
+ package
+
+ assembly
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-source
+ generate-sources
+
+ add-source
+
+
+
+
+
+
+
+
+
+
+
+ maven-resources-plugin
+
+
+ copy-resources
+ compile
+
+ copy-resources
+
+
+ ${basedir}/apprun/conf
+
+
+
+ ${basedir}/src/main/resources/
+ true
+
+
+
+
+
+
+
+ maven-compiler-plugin
+ 3.1
+
+
+ ${maven.compiler.target}
+ ${project.build.sourceEncoding}
+
+ 1.8
+ UTF-8
+
+ ${project.basedir}/libs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy
+ package
+
+ copy-dependencies
+
+
+ ${basedir}/apprun/${project.artifactId}/libs
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+
+ run
+
+ package
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ io.netty
+ netty-all
+ 4.0.41.Final
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.52
+
+
+ com.mashape.unirest
+ unirest-java
+ 1.4.9
+
+
+ log4j
+ log4j
+ 1.2.12
+
+
+ commons-logging
+ commons-logging
+ 1.1.3
+
+
+
+ commons-cli
+ commons-cli
+ 1.3.1
+
+
+
+ com.alibaba
+ fastjson
+ 1.2.7
+
+
+
+ com.sun.jna
+ jna
+ 4.1.1
+ system
+ ${project.basedir}/libs/jna.jar
+
+
+
+ com.miglayout
+ miglayout-swing
+ 4.2
+
+
+
+
+ org.pcap4j
+ pcap4j-core
+ 1.5.0
+
+
+
+
+
+
+ org.pcap4j
+ pcap4j-packetfactory-static
+ 1.5.0
+
+
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.21
+
+
+
+ org.slf4j
+ slf4j-nop
+ 1.7.21
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/net/fs/cap/CapEnv.java b/src/main/java/net/fs/cap/CapEnv.java
new file mode 100755
index 0000000..18fe7a5
--- /dev/null
+++ b/src/main/java/net/fs/cap/CapEnv.java
@@ -0,0 +1,530 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.cap;
+
+import net.fs.netty.SocksServer;
+import net.fs.rudp.Route;
+import net.fs.utils.ByteShortConvert;
+import net.fs.utils.MLog;
+import org.pcap4j.core.*;
+import org.pcap4j.core.PcapNetworkInterface.PromiscuousMode;
+import org.pcap4j.packet.*;
+import org.pcap4j.packet.EthernetPacket.EthernetHeader;
+import org.pcap4j.packet.IpV4Packet.IpV4Header;
+import org.pcap4j.packet.TcpPacket.TcpHeader;
+import org.pcap4j.util.MacAddress;
+
+import java.io.IOException;
+import java.net.*;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.LinkedBlockingQueue;
+
+
+public class CapEnv {
+
+ public MacAddress gateway_mac;
+
+ public MacAddress local_mac;
+
+ Inet4Address local_ipv4;
+
+ public PcapHandle sendHandle;
+
+ VDatagramSocket vDatagramSocket;
+
+ String testIp_tcp="";
+
+ String testIp_udp="5.5.5.5";
+
+ String selectedInterfaceName=null;
+
+ String selectedInterfaceDes="";
+
+ PcapNetworkInterface nif;
+
+ private final int COUNT=-1;
+
+ private final int READ_TIMEOUT=1;
+
+ private final int SNAPLEN= 10*1024;
+
+ HashMap tunTable=new HashMap();
+
+ LinkedBlockingQueue packetList=new LinkedBlockingQueue();
+
+ Random random=new Random();
+
+ boolean client=false;
+
+ short listenPort;
+
+ TunManager tcpManager=null;
+
+ CapEnv capEnv;
+
+ Thread versinMonThread;
+
+ boolean detect_by_tcp=true;
+
+ public boolean tcpEnable=false;
+
+ public boolean fwSuccess=true;
+
+ boolean ppp=false;
+
+ {
+ capEnv=this;
+ }
+
+ public CapEnv(boolean isClient,boolean fwSuccess){
+ this.client=isClient;
+ this.fwSuccess=fwSuccess;
+ tcpManager=new TunManager(this);
+ }
+
+ public void init() throws Exception{
+ initInterface();
+ Thread thread_process=new Thread(){
+
+ public void run(){
+ while(true){
+ try {
+ Packet packet=packetList.take();
+ EthernetPacket packet_eth=(EthernetPacket) packet;
+ EthernetHeader head_eth=packet_eth.getHeader();
+
+ IpV4Packet ipV4Packet=null;
+ if(ppp){
+ ipV4Packet=getIpV4Packet_pppoe(packet_eth);
+ }else {
+ if(packet_eth.getPayload() instanceof IpV4Packet){
+ ipV4Packet=(IpV4Packet) packet_eth.getPayload();
+ }
+ }
+ if(ipV4Packet!=null){
+ IpV4Header ipV4Header=ipV4Packet.getHeader();
+ if(ipV4Packet.getPayload() instanceof TcpPacket){
+ TcpPacket tcpPacket=(TcpPacket) ipV4Packet.getPayload();
+ TcpHeader tcpHeader=tcpPacket.getHeader();
+ if(client){
+ TCPTun conn=tcpManager.getTcpConnection_Client(ipV4Header.getSrcAddr().getHostAddress(),tcpHeader.getSrcPort().value(), tcpHeader.getDstPort().value());
+ if(conn!=null){
+ conn.process_client(capEnv,packet,head_eth,ipV4Header,tcpPacket,false);
+ }
+ }else {
+ TCPTun conn=null;conn = tcpManager.getTcpConnection_Server(ipV4Header.getSrcAddr().getHostAddress(),tcpHeader.getSrcPort().value());
+ if(
+ tcpHeader.getDstPort().value()==listenPort){
+ if(tcpHeader.getSyn()&&!tcpHeader.getAck()&&conn==null){
+ conn=new TCPTun(capEnv,ipV4Header.getSrcAddr(),tcpHeader.getSrcPort().value());
+ tcpManager.addConnection_Server(conn);
+ }
+ conn = tcpManager.getTcpConnection_Server(ipV4Header.getSrcAddr().getHostAddress(),tcpHeader.getSrcPort().value());
+ if(conn!=null){
+ conn.process_server(packet,head_eth,ipV4Header,tcpPacket,true);
+ }
+ }
+ }
+ }else if(packet_eth.getPayload() instanceof IllegalPacket){
+ MLog.println("IllegalPacket!!!");
+ }
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (IllegalRawDataException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ };
+ thread_process.start();
+
+
+
+ Thread systemSleepScanThread=new Thread(){
+ public void run(){
+ long t=System.currentTimeMillis();
+ while(true){
+ if(System.currentTimeMillis()-t>5*1000){
+ for(int i=0;i<10;i++){
+ MLog.info("休眠恢复... "+(i+1));
+ try {
+ boolean success=initInterface();
+ if(success){
+ MLog.info("休眠恢复成功 "+(i+1));
+ break;
+ }
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+
+ try {
+ Thread.sleep(5*1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+ t=System.currentTimeMillis();
+ try {
+ Thread.sleep(1*1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ };
+ systemSleepScanThread.start();
+ }
+
+ PromiscuousMode getMode(PcapNetworkInterface pi){
+ PromiscuousMode mode=null;
+ String string=(pi.getDescription()+":"+pi.getName()).toLowerCase();
+ if(string.contains("wireless")){
+ mode= PromiscuousMode.NONPROMISCUOUS;
+ }else {
+ mode= PromiscuousMode.PROMISCUOUS;
+ }
+ return mode;
+ }
+
+ boolean initInterface() throws Exception{
+ boolean success=false;
+ detectInterface();
+ List allDevs = Pcaps.findAllDevs();
+ MLog.println("Network Interface List: ");
+ for(PcapNetworkInterface pi:allDevs){
+ String desString="";
+ if(pi.getDescription()!=null){
+ desString=pi.getDescription();
+ }
+ MLog.info(" "+desString+" "+pi.getName());
+ if(pi.getName().equals(selectedInterfaceName)
+ &&desString.equals(selectedInterfaceDes)){
+ nif=pi;
+ //break;
+ }
+ }
+ if(nif!=null){
+ String desString="";
+ if(nif.getDescription()!=null){
+ desString=nif.getDescription();
+ }
+ success=true;
+ MLog.info("Selected Network Interface:\n"+" "+desString+" "+nif.getName());
+ if(fwSuccess){
+ tcpEnable=true;
+ }
+ }else {
+ tcpEnable=false;
+ MLog.info("Select Network Interface failed,can't use TCP protocal!\n");
+ }
+ if(tcpEnable){
+ sendHandle = nif.openLive(SNAPLEN,getMode(nif), READ_TIMEOUT);
+ final PcapHandle handle= nif.openLive(SNAPLEN, getMode(nif), READ_TIMEOUT);
+
+ final PacketListener listener= new PacketListener() {
+ @Override
+ public void gotPacket(Packet packet) {
+
+ try {
+ if(packet instanceof EthernetPacket){
+ packetList.add(packet);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ };
+
+ Thread thread=new Thread(){
+
+ public void run(){
+ try {
+ handle.loop(COUNT, listener);
+ PcapStat ps = handle.getStats();
+ handle.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ };
+ thread.start();
+ }
+
+ if(!client){
+ MLog.info("FinalSpeed server start success.");
+ }
+ return success;
+
+ }
+
+ void detectInterface() {
+ List allDevs = null;
+ HashMap handleTable=new HashMap();
+ try {
+ allDevs = Pcaps.findAllDevs();
+ } catch (PcapNativeException e1) {
+ e1.printStackTrace();
+ return;
+ }
+ for(final PcapNetworkInterface pi:allDevs){
+ try {
+ final PcapHandle handle = pi.openLive(SNAPLEN, getMode(pi), READ_TIMEOUT);
+ handleTable.put(pi, handle);
+ final PacketListener listener= new PacketListener() {
+ @Override
+ public void gotPacket(Packet packet) {
+
+ try {
+ if(packet instanceof EthernetPacket){
+ EthernetPacket packet_eth=(EthernetPacket) packet;
+ EthernetHeader head_eth=packet_eth.getHeader();
+
+ if(head_eth.getType().value()==0xffff8864){
+ ppp=true;
+ PacketUtils.ppp=ppp;
+ }
+
+ IpV4Packet ipV4Packet=null;
+ IpV4Header ipV4Header=null;
+
+ if(ppp){
+ ipV4Packet=getIpV4Packet_pppoe(packet_eth);
+ }else {
+ if(packet_eth.getPayload() instanceof IpV4Packet){
+ ipV4Packet=(IpV4Packet) packet_eth.getPayload();
+ }
+ }
+ if(ipV4Packet!=null){
+ ipV4Header=ipV4Packet.getHeader();
+
+ if(ipV4Header.getSrcAddr().getHostAddress().equals(testIp_tcp)){
+ local_mac=head_eth.getDstAddr();
+ gateway_mac=head_eth.getSrcAddr();
+ local_ipv4=ipV4Header.getDstAddr();
+ selectedInterfaceName=pi.getName();
+ if(pi.getDescription()!=null){
+ selectedInterfaceDes=pi.getDescription();
+ }
+ //MLog.println("local_mac_tcp1 "+gateway_mac+" gateway_mac "+gateway_mac+" local_ipv4 "+local_ipv4);
+ }
+ if(ipV4Header.getDstAddr().getHostAddress().equals(testIp_tcp)){
+ local_mac=head_eth.getSrcAddr();
+ gateway_mac=head_eth.getDstAddr();
+ local_ipv4=ipV4Header.getSrcAddr();
+ selectedInterfaceName=pi.getName();
+ if(pi.getDescription()!=null){
+ selectedInterfaceDes=pi.getDescription();
+ }
+ //MLog.println("local_mac_tcp2 local_mac "+local_mac+" gateway_mac "+gateway_mac+" local_ipv4 "+local_ipv4);
+ }
+ //udp
+ if(ipV4Header.getDstAddr().getHostAddress().equals(testIp_udp)){
+ local_mac=head_eth.getSrcAddr();
+ gateway_mac=head_eth.getDstAddr();
+ local_ipv4=ipV4Header.getSrcAddr();
+ selectedInterfaceName=pi.getName();
+ if(pi.getDescription()!=null){
+ selectedInterfaceDes=pi.getDescription();
+ }
+ //MLog.println("local_mac_udp "+gateway_mac+" gateway_mac"+gateway_mac+" local_ipv4 "+local_ipv4);
+ }
+
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ };
+
+ Thread thread=new Thread(){
+
+ public void run(){
+ try {
+ handle.loop(COUNT, listener);
+ PcapStat ps = handle.getStats();
+ handle.close();
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+ }
+
+ };
+ thread.start();
+ } catch (PcapNativeException e1) {
+
+ }
+
+ }
+
+ //detectMac_udp();
+ try {
+ detectMac_tcp();
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ }
+
+
+ Iterator it=handleTable.keySet().iterator();
+ while(it.hasNext()){
+ PcapNetworkInterface pi=it.next();
+ PcapHandle handle=handleTable.get(pi);
+ try {
+ handle.breakLoop();
+ } catch (NotOpenException e) {
+ e.printStackTrace();
+ }
+ //handle.close();//linux下会阻塞
+ }
+ }
+
+ IpV4Packet getIpV4Packet_pppoe(EthernetPacket packet_eth) throws IllegalRawDataException {
+ IpV4Packet ipV4Packet=null;
+ byte[] pppData=packet_eth.getPayload().getRawData();
+ if(pppData.length>8&&pppData[8]==0x45){
+ byte[] b2=new byte[2];
+ System.arraycopy(pppData, 4, b2, 0, 2);
+ short len=(short) ByteShortConvert.toShort(b2, 0);
+ int ipLength=toUnsigned(len)-2;
+ byte[] ipData=new byte[ipLength];
+ //设置ppp参数
+ PacketUtils.pppHead_static[2]=pppData[2];
+ PacketUtils.pppHead_static[3]=pppData[3];
+ if(ipLength==(pppData.length-8)){
+ System.arraycopy(pppData, 8, ipData, 0, ipLength);
+ ipV4Packet= IpV4Packet.newPacket(ipData, 0, ipData.length);
+ }else {
+ MLog.println("长度不符!");
+ }
+ }
+ return ipV4Packet;
+ }
+
+
+
+ public static String printHexString(byte[] b) {
+ StringBuffer sb=new StringBuffer();
+ for (int i = 0; i < b.length; i++)
+ {
+ String hex = Integer.toHexString(b[i] & 0xFF);
+ hex= hex.replaceAll(":", " ");
+ if (hex.length() == 1)
+ {
+ hex = '0' + hex;
+ }
+ sb.append(hex + " ");
+ }
+ return sb.toString();
+ }
+
+ public void createTcpTun_Client(String dstAddress,short dstPort) throws Exception{
+ Inet4Address serverAddress=(Inet4Address) Inet4Address.getByName(dstAddress);
+ TCPTun conn=new TCPTun(this,serverAddress,dstPort,local_mac,gateway_mac);
+ tcpManager.addConnection_Client(conn);
+ boolean success=false;
+ for(int i=0;i<6;i++){
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if(conn.preDataReady){
+ success=true;
+ break;
+ }
+ }
+ if(success){
+ tcpManager.setDefaultTcpTun(conn);
+ }else {
+ tcpManager.removeTun(conn);
+ tcpManager.setDefaultTcpTun(null);
+ throw new Exception("创建隧道失败!");
+ }
+ }
+
+ private void detectMac_tcp() throws UnknownHostException{
+ InetAddress address=InetAddress.getByName("www.bing.com");
+ final int por=80;
+ testIp_tcp=address.getHostAddress();
+ for(int i=0;i<5;i++){
+ try {
+ Route.es.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ Socket socket=new Socket(testIp_tcp,por);
+ socket.close();
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ Thread.sleep(500);
+ if(local_mac!=null){
+ break;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private void detectMac_udp(){
+ for(int i=0;i<10;i++){
+ try {
+ DatagramSocket ds=new DatagramSocket();
+ DatagramPacket dp=new DatagramPacket(new byte[1000], 1000);
+ dp.setAddress(InetAddress.getByName(testIp_udp));
+ dp.setPort(5555);
+ ds.send(dp);
+ ds.close();
+ Thread.sleep(500);
+ if(local_mac!=null){
+ break;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+
+ }
+
+ public short getListenPort() {
+ return listenPort;
+ }
+
+ public void setListenPort(short listenPort) {
+ this.listenPort = listenPort;
+ if(!client){
+ MLog.info("Listen tcp port: "+toUnsigned(listenPort));
+ }
+ }
+
+ public static int toUnsigned(short s) {
+ return s & 0x0FFFF;
+ }
+
+}
diff --git a/src/main/java/net/fs/cap/CapServer.java b/src/main/java/net/fs/cap/CapServer.java
new file mode 100755
index 0000000..37161f1
--- /dev/null
+++ b/src/main/java/net/fs/cap/CapServer.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.cap;
+
+
+public class CapServer {
+
+ CapServer(){
+ CapEnv capEnv=null;
+ try {
+ capEnv=new CapEnv(false,true);
+ capEnv.init();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/src/main/java/net/fs/cap/CustomTcpSackOption.java b/src/main/java/net/fs/cap/CustomTcpSackOption.java
new file mode 100755
index 0000000..188c641
--- /dev/null
+++ b/src/main/java/net/fs/cap/CustomTcpSackOption.java
@@ -0,0 +1,375 @@
+/*_##########################################################################
+ _##
+ _## Copyright (C) 2014 Pcap4J.org
+ _##
+ _##########################################################################
+*/
+
+package net.fs.cap;
+
+import org.pcap4j.packet.IllegalRawDataException;
+import org.pcap4j.packet.LengthBuilder;
+import org.pcap4j.packet.TcpPacket.TcpOption;
+import org.pcap4j.packet.namednumber.TcpOptionKind;
+import org.pcap4j.util.ByteArrays;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.pcap4j.util.ByteArrays.INT_SIZE_IN_BYTES;
+
+/**
+ * @author Kaito Yamada
+ * @since pcap4j 1.2.0
+ */
+public final class CustomTcpSackOption implements TcpOption {
+
+ /*
+ * http://tools.ietf.org/html/rfc2018
+ *
+ * +--------+--------+
+ * | Kind=5 | Length |
+ * +--------+--------+--------+--------+
+ * | Left Edge of 1st Block |
+ * +--------+--------+--------+--------+
+ * | Right Edge of 1st Block |
+ * +--------+--------+--------+--------+
+ * | |
+ * / . . . /
+ * | |
+ * +--------+--------+--------+--------+
+ * | Left Edge of nth Block |
+ * +--------+--------+--------+--------+
+ * | Right Edge of nth Block |
+ * +--------+--------+--------+--------+
+ */
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3308738405807657257L;
+
+ private final TcpOptionKind kind = TcpOptionKind.SACK;
+ private final byte length;
+ private final List sacks = new ArrayList();
+
+ /**
+ * A static factory method.
+ * This method validates the arguments by {@link ByteArrays#validateBounds(byte[], int, int)},
+ * which may throw exceptions undocumented here.
+ *
+ * @param rawData rawData
+ * @param offset offset
+ * @param length length
+ * @return a new TcpSackOption object.
+ * @throws IllegalRawDataException if parsing the raw data fails.
+ */
+ public static CustomTcpSackOption newInstance(
+ byte[] rawData, int offset, int length
+ ) throws IllegalRawDataException {
+ ByteArrays.validateBounds(rawData, offset, length);
+ return new CustomTcpSackOption(rawData, offset, length);
+ }
+
+ private CustomTcpSackOption(byte[] rawData, int offset, int length) throws IllegalRawDataException {
+ if (length < 2) {
+ StringBuilder sb = new StringBuilder(50);
+ sb.append("The raw data length must be more than 1. rawData: ")
+ .append(ByteArrays.toHexString(rawData, " "))
+ .append(", offset: ")
+ .append(offset)
+ .append(", length: ")
+ .append(length);
+ throw new IllegalRawDataException(sb.toString());
+ }
+ if (rawData[offset] != kind.value()) {
+ StringBuilder sb = new StringBuilder(100);
+ sb.append("The kind must be: ")
+ .append(kind.valueAsString())
+ .append(" rawData: ")
+ .append(ByteArrays.toHexString(rawData, " "))
+ .append(", offset: ")
+ .append(offset)
+ .append(", length: ")
+ .append(length);
+ throw new IllegalRawDataException(sb.toString());
+ }
+
+ this.length = rawData[1 + offset];
+ int lengthFieldAsInt = getLengthAsInt();
+ if (lengthFieldAsInt < 2) {
+ throw new IllegalRawDataException(
+ "The value of length field must be more than 1 but: " + lengthFieldAsInt
+ );
+ }
+
+ if ((lengthFieldAsInt - 2) % (INT_SIZE_IN_BYTES * 2) != 0) {
+ StringBuilder sb = new StringBuilder(100);
+ sb.append(
+ "The value of length field must be an integer multiple of 8 octets long but: "
+ )
+ .append(lengthFieldAsInt);
+ throw new IllegalRawDataException(sb.toString());
+ }
+ if (length < lengthFieldAsInt) {
+ StringBuilder sb = new StringBuilder(100);
+ sb.append("rawData is too short. length field: ")
+ .append(lengthFieldAsInt)
+ .append(", rawData: ")
+ .append(ByteArrays.toHexString(rawData, " "))
+ .append(", offset: ")
+ .append(offset)
+ .append(", length: ")
+ .append(length);
+ throw new IllegalRawDataException(sb.toString());
+ }
+
+ for (int i = 2; i < lengthFieldAsInt; i += INT_SIZE_IN_BYTES * 2) {
+ sacks.add(
+ new Sack(
+ ByteArrays.getInt(rawData, i + offset),
+ ByteArrays.getInt(rawData, i + INT_SIZE_IN_BYTES + offset)
+ )
+ );
+ }
+ }
+
+ private CustomTcpSackOption(Builder builder) {
+ if (
+ builder == null
+ || builder.sacks == null
+ ) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("builder: ").append(builder)
+ .append(" builder.sacks: ").append(builder.sacks);
+ throw new NullPointerException(sb.toString());
+ }
+
+ this.sacks.addAll(builder.sacks);
+
+ if (builder.correctLengthAtBuild) {
+ this.length = (byte)length();
+ }
+ else {
+ this.length = builder.length;
+ }
+ }
+
+ @Override
+ public TcpOptionKind getKind() {
+ return kind;
+ }
+
+ /**
+ *
+ * @return length
+ */
+ public byte getLength() { return length; }
+
+ /**
+ *
+ * @return length
+ */
+ public int getLengthAsInt() { return 0xFF & length; }
+
+ @Override
+ public int length() {
+ return sacks.size() * INT_SIZE_IN_BYTES * 2 + 2;
+ }
+
+ @Override
+ public byte[] getRawData() {
+ byte[] rawData = new byte[length()];
+ rawData[0] = kind.value();
+ rawData[1] = length;
+
+ int offset = 2;
+ for (Sack sack: sacks) {
+ System.arraycopy(
+ ByteArrays.toByteArray(sack.leftEdge), 0,
+ rawData, offset, INT_SIZE_IN_BYTES
+ );
+ System.arraycopy(
+ ByteArrays.toByteArray(sack.rightEdge), 0,
+ rawData, offset + INT_SIZE_IN_BYTES, INT_SIZE_IN_BYTES
+ );
+ offset += INT_SIZE_IN_BYTES * 2;
+ }
+
+ return rawData;
+ }
+
+ /**
+ *
+ * @return a new Builder object populated with this object's fields.
+ */
+ public Builder getBuilder() {
+ return new Builder(this);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[Kind: ")
+ .append(kind);
+ sb.append("] [Length: ")
+ .append(getLengthAsInt())
+ .append(" bytes]");
+ for (Sack sack: sacks) {
+ sb.append(" [LE: ")
+ .append(sack.getLeftEdgeAsLong())
+ .append(" RE: ")
+ .append(sack.getRightEdgeAsLong())
+ .append("]");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) { return true; }
+ if (!this.getClass().isInstance(obj)) { return false; }
+
+ CustomTcpSackOption other = (CustomTcpSackOption)obj;
+ return
+ length == other.length
+ && sacks.equals(other.sacks);
+ }
+
+ public List getSacks() {
+ return sacks;
+}
+
+@Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + length;
+ result = 31 * result + sacks.hashCode();
+ return result;
+ }
+
+ /**
+ * @author Kaito Yamada
+ * @since pcap4j 1.2.0
+ */
+ public static final class Builder
+ implements LengthBuilder {
+
+ private byte length;
+ private boolean correctLengthAtBuild;
+ private List sacks;
+
+ /**
+ *
+ */
+ public Builder() {}
+
+ private Builder(CustomTcpSackOption option) {
+ this.length = option.length;
+ }
+
+ /**
+ * @param length length
+ * @return this Builder object for method chaining.
+ */
+ public Builder length(byte length) {
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * @param sacks sacks
+ * @return this Builder object for method chaining.
+ */
+ public Builder sacks(List sacks) {
+ this.sacks = sacks;
+ return this;
+ }
+
+ @Override
+ public Builder correctLengthAtBuild(boolean correctLengthAtBuild) {
+ this.correctLengthAtBuild = correctLengthAtBuild;
+ return this;
+ }
+
+ @Override
+ public CustomTcpSackOption build() {
+ return new CustomTcpSackOption(this);
+ }
+
+ }
+
+ /**
+ * @author Kaito Yamada
+ * @since pcap4j 1.2.0
+ */
+ public static final class Sack implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1218420566089129438L;
+
+ private final int leftEdge;
+ private final int rightEdge;
+
+ /**
+ * @param leftEdge leftEdge
+ * @param rightEdge rightEdge
+ */
+ public Sack(int leftEdge, int rightEdge) {
+ this.leftEdge = leftEdge;
+ this.rightEdge = rightEdge;
+ }
+
+ /**
+ * @return leftEdge
+ */
+ public int getLeftEdge() {
+ return leftEdge;
+ }
+
+ /**
+ * @return leftEdge
+ */
+ public long getLeftEdgeAsLong() {
+ return 0xFFFFFFFFL & leftEdge;
+ }
+
+ /**
+ * @return rightEdge
+ */
+ public int getRightEdge() {
+ return rightEdge;
+ }
+
+ /**
+ * @return rightEdge
+ */
+ public long getRightEdgeAsLong() {
+ return 0xFFFFFFFFL & rightEdge;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) { return true; }
+ if (!this.getClass().isInstance(obj)) { return false; }
+
+ Sack other = (Sack)obj;
+ return
+ leftEdge == other.leftEdge
+ && rightEdge == other.rightEdge;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + leftEdge;
+ result = 31 * result + rightEdge;
+ return result;
+ }
+
+ }
+
+}
diff --git a/src/main/java/net/fs/cap/IPacket.java b/src/main/java/net/fs/cap/IPacket.java
new file mode 100755
index 0000000..1777715
--- /dev/null
+++ b/src/main/java/net/fs/cap/IPacket.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.cap;
+
+import org.pcap4j.packet.Packet;
+
+public class IPacket {
+
+ int index;
+
+ int sequence;
+
+ int legth;
+
+ Packet packet;
+
+ long firstSendTime;
+
+ long sendTime;
+
+ long reSendCount;
+
+}
diff --git a/src/main/java/net/fs/cap/PacketUtils.java b/src/main/java/net/fs/cap/PacketUtils.java
new file mode 100755
index 0000000..a1331d5
--- /dev/null
+++ b/src/main/java/net/fs/cap/PacketUtils.java
@@ -0,0 +1,369 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.cap;
+
+import net.fs.utils.ByteShortConvert;
+import org.pcap4j.packet.*;
+import org.pcap4j.packet.TcpPacket.TcpOption;
+import org.pcap4j.packet.namednumber.EtherType;
+import org.pcap4j.packet.namednumber.IpNumber;
+import org.pcap4j.packet.namednumber.IpVersion;
+import org.pcap4j.packet.namednumber.TcpPort;
+import org.pcap4j.util.MacAddress;
+
+import java.net.Inet4Address;
+import java.util.ArrayList;
+
+public class PacketUtils {
+
+ static byte ttl=64;
+
+ static short mtu=1440;
+
+ static byte shiftCount=6;
+
+ static short window=(short) (64*1024-1);
+
+ public static boolean ppp=false;
+
+ public static byte[] pppHead_static={0x11,0x00,0x44,0x44,0x00,0x44,0x00,0x21};
+
+
+ public static Packet buildIpV4(
+ MacAddress srcAddress_mac,
+ MacAddress dstAddrress_mac,
+ IpV4Packet.Builder builder_ipv4){
+
+ org.pcap4j.packet.Packet.Builder builder=null;
+ EtherType etherType=null;
+ Packet p=null;
+ if(ppp){
+ etherType= EtherType.PPPOE_SESSION_STAGE;
+
+ UnknownPacket.Builder pppBuilder=new UnknownPacket.Builder();
+ byte[] ipData=builder_ipv4.build().getRawData();
+
+ byte[] lenb=new byte[2];
+ ByteShortConvert.toByteArray((short) (ipData.length+2), lenb, 0);
+
+ byte[] pppHead=new byte[8];
+ System.arraycopy(pppHead_static, 0, pppHead, 0, pppHead.length);
+ System.arraycopy(lenb, 0, pppHead, 4, 2);
+
+ byte[] newData=new byte[pppHead.length+ipData.length];
+ System.arraycopy(pppHead, 0, newData, 0, pppHead.length);
+ System.arraycopy(ipData, 0, newData, 8, ipData.length);
+ pppBuilder.rawData(newData);
+
+ builder=pppBuilder;
+ }else {
+ etherType= EtherType.IPV4;
+ builder=builder_ipv4;
+ }
+
+ EthernetPacket.Builder etherBuilder = new EthernetPacket.Builder();
+ etherBuilder.dstAddr(dstAddrress_mac)
+ .srcAddr(srcAddress_mac)
+ .type(etherType)
+ .payloadBuilder(builder)
+ .paddingAtBuild(true);
+
+ p = etherBuilder.build();
+
+ return p;
+ }
+
+ static Packet createDataPacket(
+ MacAddress srcAddress_mac,
+ MacAddress dstAddrress_mac,
+ Inet4Address srcAddress,short srcPort,
+ Inet4Address dstAddress,short dstPort,
+ int sequence,int ack, byte[] data,short ident){
+ Packet p=null;
+
+ TcpPacket.Builder builder_tcp=new TcpPacket.Builder();
+ builder_tcp.payloadBuilder(new UnknownPacket.Builder().rawData(data));
+ builder_tcp.correctChecksumAtBuild(true);
+ builder_tcp.correctLengthAtBuild(true);
+ builder_tcp.paddingAtBuild(true);
+ builder_tcp.ack(true);
+ builder_tcp.acknowledgmentNumber(ack);
+ //builder_tcp.checksum(tcpHeader.getChecksum());
+ //builder_tcp.dataOffset((byte)8);
+ builder_tcp.dstAddr(dstAddress);
+ builder_tcp.dstPort(new TcpPort( dstPort,""));
+ builder_tcp.fin(false);
+ //builder_tcp.options(tcpHeader.getOptions());
+ //builder_tcp.padding(tcpHeader.getPadding());
+ builder_tcp.psh(false);
+ builder_tcp.reserved((byte) 0);
+ builder_tcp.rst(false);
+ builder_tcp.sequenceNumber(sequence);
+ builder_tcp.srcAddr(srcAddress);
+ builder_tcp.srcPort(new TcpPort( srcPort,""));
+ builder_tcp.syn(false);
+ builder_tcp.urg(false);
+ //builder_tcp.urgentPointer(tcpHeader.getUrgentPointer());
+ builder_tcp.window( window);
+
+ IpV4Packet.Builder builder_ipv4=new IpV4Packet.Builder();
+ builder_ipv4.correctChecksumAtBuild(true);
+ builder_ipv4.correctLengthAtBuild(true);
+ builder_ipv4.dontFragmentFlag(true);
+ builder_ipv4.paddingAtBuild(true);
+ builder_ipv4.dstAddr(dstAddress);
+ builder_ipv4.fragmentOffset( (short)0);
+ //builder_ipv4.headerChecksum(ipV4Header.getHeaderChecksum());
+ //short identification= Math.abs(random.nextInt(Short.MAX_VALUE));
+ //identification=ident;
+ builder_ipv4.identification(ident);
+ builder_ipv4.ihl((byte) 5);
+ builder_ipv4.moreFragmentFlag(false);
+ //builder_ipv4.options(ipV4Header.getOptions());
+ //builder_ipv4.padding(ipV4Header.getPadding());
+
+ builder_ipv4.protocol(IpNumber.TCP);
+ //builder_ipv4.reservedFlag(ipV4Header.getReservedFlag());
+ builder_ipv4.srcAddr(srcAddress);
+ builder_ipv4.tos(IpV4Rfc1349Tos.newInstance((byte) 0));
+ //builder_ipv4.totalLength( 52);
+ builder_ipv4.ttl(ttl);
+ builder_ipv4.version(IpVersion.IPV4);
+ builder_ipv4.payloadBuilder(builder_tcp);
+
+ p = buildIpV4(srcAddress_mac,dstAddrress_mac,builder_ipv4);
+
+ return p;
+ }
+
+ static Packet createAck(
+ MacAddress srcAddress_mac,
+ MacAddress dstAddrress_mac,
+ Inet4Address srcAddress,short srcPort,
+ Inet4Address dstAddress,short dstPort,
+ int ack_sequence,int sequence,short ident){
+
+ TcpPacket.Builder builder_tcp=new TcpPacket.Builder();
+ //builder_tcp.payloadBuilder(new UnknownPacket.Builder().rawData(new byte[0]));
+ builder_tcp.correctChecksumAtBuild(true);
+ builder_tcp.correctLengthAtBuild(true);
+ builder_tcp.paddingAtBuild(true);
+ builder_tcp.ack(true);
+ builder_tcp.acknowledgmentNumber(ack_sequence);
+ //builder_tcp.checksum(tcpHeader.getChecksum());
+ //builder_tcp.dataOffset((byte) 8);
+ builder_tcp.dstAddr(dstAddress);
+ builder_tcp.dstPort(new TcpPort( dstPort,""));
+ //builder_tcp.fin(tcpHeader.getFin());
+
+ builder_tcp.psh(false);
+ builder_tcp.reserved((byte) 0);
+ builder_tcp.rst(false);
+ builder_tcp.sequenceNumber(sequence);
+ builder_tcp.srcAddr(srcAddress);
+ builder_tcp.srcPort(new TcpPort( srcPort,""));
+ builder_tcp.syn(false);
+ builder_tcp.urg(false);
+ //builder_tcp.urgentPointer(tcpHeader.getUrgentPointer());
+ builder_tcp.window( window);
+
+ IpV4Packet.Builder builder_ipv4=new IpV4Packet.Builder();
+ builder_ipv4.correctChecksumAtBuild(true);
+ builder_ipv4.correctLengthAtBuild(true);
+ builder_ipv4.paddingAtBuild(true);
+ builder_ipv4.dstAddr(dstAddress);
+ builder_ipv4.dontFragmentFlag(true);
+ builder_ipv4.fragmentOffset( (short) 0);
+ //builder_ipv4.headerChecksum(ipV4Header.getHeaderChecksum());
+ //short identification= Math.abs(random.nextInt(Short.MAX_VALUE));
+ builder_ipv4.identification(ident);
+ builder_ipv4.ihl((byte) 5);
+ //builder_ipv4.moreFragmentFlag(ipV4Header.getMoreFragmentFlag());
+ //builder_ipv4.options(ipV4Header.getOptions());
+ //builder_ipv4.padding(ipV4Header.getPadding());
+
+ builder_ipv4.protocol(IpNumber.TCP);
+ // builder_ipv4.reservedFlag(ipV4Header.getReservedFlag());
+ builder_ipv4.srcAddr(srcAddress);
+ builder_ipv4.tos(IpV4Rfc1349Tos.newInstance((byte) 0));
+ //builder_ipv4.totalLength( 52);
+ builder_ipv4.ttl(ttl);
+ builder_ipv4.version(IpVersion.IPV4);
+ builder_ipv4.payloadBuilder(builder_tcp);
+ //
+
+ Packet p = buildIpV4(srcAddress_mac,dstAddrress_mac,builder_ipv4);
+ //System.out.println("自定义确认 "+" identification "+identification+" ack_sequence "+ack_sequence+" # "+tcpPacket.getHeader());
+ return p;
+
+ }
+
+
+ static Packet createSyncAck(
+ MacAddress srcAddress_mac,
+ MacAddress dstAddrress_mac,
+ Inet4Address srcAddress,short srcPort,
+ Inet4Address dstAddress,short dstPort,
+ int ack_sequence,int sequence,short ident){
+
+ TcpPacket.Builder builder_tcp=new TcpPacket.Builder();
+ //builder_tcp.payloadBuilder(new UnknownPacket.Builder().rawData(new byte[0]));
+ builder_tcp.correctChecksumAtBuild(true);
+ builder_tcp.correctLengthAtBuild(true);
+ builder_tcp.paddingAtBuild(true);
+ builder_tcp.ack(true);
+ builder_tcp.acknowledgmentNumber(ack_sequence);
+ //builder_tcp.checksum(tcpHeader.getChecksum());
+ //builder_tcp.dataOffset((byte) 8);
+ builder_tcp.dstAddr(dstAddress);
+ builder_tcp.dstPort(new TcpPort(dstPort,""));
+ //builder_tcp.fin(tcpHeader.getFin());
+
+
+ ArrayList tcp_options=new ArrayList();
+
+ TcpNoOperationOption nop= TcpNoOperationOption.getInstance();
+
+ TcpMaximumSegmentSizeOption seg_option=new TcpMaximumSegmentSizeOption.Builder().maxSegSize(mtu).correctLengthAtBuild(true).build();
+ tcp_options.add(seg_option);
+
+ tcp_options.add(nop);
+ tcp_options.add(nop);
+
+ TcpSackPermittedOption sack_permit_option= TcpSackPermittedOption.getInstance();
+ tcp_options.add(sack_permit_option);
+
+ tcp_options.add(nop);
+
+ TcpWindowScaleOption win_option=new TcpWindowScaleOption.Builder().shiftCount(shiftCount).correctLengthAtBuild(true).build();
+ tcp_options.add(win_option);
+
+ builder_tcp.options(tcp_options);
+
+ //builder_tcp.padding(tcpHeader.getPadding());
+ builder_tcp.psh(false);
+ builder_tcp.reserved((byte) 0);
+ builder_tcp.rst(false);
+ builder_tcp.sequenceNumber(sequence);
+ builder_tcp.srcAddr(srcAddress);
+ builder_tcp.srcPort(new TcpPort(srcPort,""));
+ builder_tcp.syn(true);
+ builder_tcp.urg(false);
+ //builder_tcp.urgentPointer(tcpHeader.getUrgentPointer());
+ builder_tcp.window( window);
+
+ IpV4Packet.Builder builder_ipv4=new IpV4Packet.Builder();
+ builder_ipv4.correctChecksumAtBuild(true);
+ builder_ipv4.correctLengthAtBuild(true);
+ builder_ipv4.paddingAtBuild(true);
+ builder_ipv4.dstAddr(dstAddress);
+ builder_ipv4.dontFragmentFlag(true);
+ builder_ipv4.fragmentOffset((short)0);
+ //builder_ipv4.headerChecksum(ipV4Header.getHeaderChecksum());
+ // short identification= Math.abs(random.nextInt(Short.MAX_VALUE));
+ builder_ipv4.identification(ident);
+ builder_ipv4.ihl((byte) 5);
+ //builder_ipv4.moreFragmentFlag(ipV4Header.getMoreFragmentFlag());
+ //builder_ipv4.options(ipV4Header.getOptions());
+ //builder_ipv4.padding(ipV4Header.getPadding());
+
+ builder_ipv4.protocol(IpNumber.TCP);
+ // builder_ipv4.reservedFlag(ipV4Header.getReservedFlag());
+ builder_ipv4.srcAddr(srcAddress);
+ builder_ipv4.tos(IpV4Rfc1349Tos.newInstance((byte) 0));
+ //builder_ipv4.totalLength( 52);
+ builder_ipv4.ttl(ttl);
+ builder_ipv4.version(IpVersion.IPV4);
+ builder_ipv4.payloadBuilder(builder_tcp);
+ //
+ Packet p = buildIpV4(srcAddress_mac,dstAddrress_mac,builder_ipv4);
+ //System.out.println("自定义确认 "+" identification "+identification+" ack_sequence "+ack_sequence+" # "+tcpPacket.getHeader());
+ return p;
+
+ }
+
+ static Packet createSync(
+ MacAddress srcAddress_mac,
+ MacAddress dstAddrress_mac,
+ Inet4Address srcAddress,short srcPort,
+ Inet4Address dstAddress,short dstPort,
+ int sequence,short ident){
+ TcpPacket.Builder builder_tcp=new TcpPacket.Builder();
+ //builder_tcp.payloadBuilder(new UnknownPacket.Builder().rawData(new byte[0]));
+ builder_tcp.correctChecksumAtBuild(true);
+ builder_tcp.correctLengthAtBuild(true);
+ builder_tcp.paddingAtBuild(true);
+ //builder_tcp.ack(true);
+ //builder_tcp.acknowledgmentNumber(ack_sequence);
+ //builder_tcp.checksum(tcpHeader.getChecksum());
+ //builder_tcp.dataOffset((byte) 8);
+ builder_tcp.dstAddr(dstAddress);
+ builder_tcp.dstPort(new TcpPort( dstPort,""));
+ //builder_tcp.fin(tcpHeader.getFin());
+
+ TcpNoOperationOption nop= TcpNoOperationOption.getInstance();
+
+ ArrayList tcp_options=new ArrayList();
+
+ TcpMaximumSegmentSizeOption seg_option=new TcpMaximumSegmentSizeOption.Builder().maxSegSize(mtu).correctLengthAtBuild(true).build();
+ tcp_options.add(seg_option);
+
+ tcp_options.add(nop);
+
+ TcpWindowScaleOption win_option=new TcpWindowScaleOption.Builder().shiftCount((byte)6).correctLengthAtBuild(true).build();
+ tcp_options.add(win_option);
+
+ tcp_options.add(nop);
+ tcp_options.add(nop);
+
+ TcpSackPermittedOption sack_permit_option= TcpSackPermittedOption.getInstance();
+ tcp_options.add(sack_permit_option);
+
+ builder_tcp.options(tcp_options);
+
+ //builder_tcp.padding(tcpHeader.getPadding());
+ builder_tcp.psh(false);
+ builder_tcp.reserved((byte) 0);
+ builder_tcp.rst(false);
+ builder_tcp.sequenceNumber(sequence);
+ builder_tcp.srcAddr(srcAddress);
+ builder_tcp.srcPort(new TcpPort( srcPort,""));
+ builder_tcp.syn(true);
+ builder_tcp.urg(false);
+ //builder_tcp.urgentPointer(tcpHeader.getUrgentPointer());
+ builder_tcp.window( window);
+
+ IpV4Packet.Builder builder_ipv4=new IpV4Packet.Builder();
+ builder_ipv4.correctChecksumAtBuild(true);
+ builder_ipv4.correctLengthAtBuild(true);
+ builder_ipv4.paddingAtBuild(true);
+ builder_ipv4.dstAddr(dstAddress);
+ builder_ipv4.dontFragmentFlag(true);
+ builder_ipv4.fragmentOffset((short)0);
+ //builder_ipv4.headerChecksum(ipV4Header.getHeaderChecksum());
+ //short identification= Math.abs(random.nextInt(Short.MAX_VALUE));
+ builder_ipv4.identification(ident);
+ builder_ipv4.ihl((byte) 5);
+ //builder_ipv4.moreFragmentFlag(ipV4Header.getMoreFragmentFlag());
+ //builder_ipv4.options(ipV4Header.getOptions());
+ //builder_ipv4.padding(ipV4Header.getPadding());
+
+ builder_ipv4.protocol(IpNumber.TCP);
+// builder_ipv4.reservedFlag(ipV4Header.getReservedFlag());
+ builder_ipv4.srcAddr(srcAddress);
+ builder_ipv4.tos(IpV4Rfc1349Tos.newInstance((byte) 0));
+ //builder_ipv4.totalLength( 52);
+ builder_ipv4.ttl(ttl);
+ builder_ipv4.version(IpVersion.IPV4);
+ builder_ipv4.payloadBuilder(builder_tcp);
+//
+ Packet p = buildIpV4(srcAddress_mac,dstAddrress_mac,builder_ipv4);
+// IpV4Packet p4=builder_ipv4.build();
+// TcpPacket tcpPacket=builder_tcp.build();
+ //selfAckTable.add(identification);
+ //System.out.println("自定义确认 "+" identification "+identification+" ack_sequence "+ack_sequence+" # "+tcpPacket.getHeader());
+ return p;
+
+ }
+
+}
diff --git a/src/main/java/net/fs/cap/SendRecord.java b/src/main/java/net/fs/cap/SendRecord.java
new file mode 100755
index 0000000..c22eaa3
--- /dev/null
+++ b/src/main/java/net/fs/cap/SendRecord.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.cap;
+
+public class SendRecord {
+
+ int sendCount;
+
+}
diff --git a/src/main/java/net/fs/cap/TCPTun.java b/src/main/java/net/fs/cap/TCPTun.java
new file mode 100755
index 0000000..4f2b076
--- /dev/null
+++ b/src/main/java/net/fs/cap/TCPTun.java
@@ -0,0 +1,414 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.cap;
+
+import net.fs.utils.MLog;
+import org.pcap4j.core.NotOpenException;
+import org.pcap4j.core.PcapHandle;
+import org.pcap4j.core.PcapNativeException;
+import org.pcap4j.packet.EthernetPacket.EthernetHeader;
+import org.pcap4j.packet.IpV4Packet.IpV4Header;
+import org.pcap4j.packet.Packet;
+import org.pcap4j.packet.TcpPacket;
+import org.pcap4j.packet.TcpPacket.TcpHeader;
+import org.pcap4j.util.MacAddress;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Random;
+
+
+public class TCPTun {
+
+ HashMap sendedTable_server=new HashMap ();
+ HashMap sendedTable_history_server=new HashMap ();
+
+ int clientSequence=Integer.MIN_VALUE;
+
+ static Random random=new Random();
+
+ PcapHandle sendHandle;
+
+ HashSet selfAckTable=new HashSet();
+
+ HashMap sendrecordTable=new HashMap();
+
+ MacAddress dstMacaAddress;
+
+ int sequenceNum=-1;
+
+ Thread sendThread;
+
+ boolean sended=false;
+
+ Packet basePacket_server;
+
+ short baseIdent=100;
+
+ IPacket dst_readed_packet,last_send_packet;
+
+ int presend_server;
+
+ ArrayList packetList=new ArrayList();
+
+ HashMap packetTable_l=new HashMap();
+
+ HashMap packetTable=new HashMap();
+
+ ArrayList unacked_list=new ArrayList();
+
+ Object syn_packetList=new Object();
+
+ int max_client_ack=Integer.MIN_VALUE;
+
+ int sendIndex=0;
+
+ long lasSetDelayTime=0;
+
+ long lastDelay=300;
+
+ Object syn_delay=new Object();
+
+ Thread resendScanThread;
+
+ boolean connectReady=false;
+
+ boolean preDataReady=false;
+
+ CapEnv capEnv;
+
+ public Inet4Address remoteAddress;
+ public short remotePort;
+ int remoteStartSequence;
+ int remoteSequence;
+ int remoteIdent;
+ int remoteSequence_max;
+
+ Inet4Address localAddress;
+ short localPort;
+ int localStartSequence=random.nextInt();
+ int localSequence;
+ int localIdent=random.nextInt(Short.MAX_VALUE-100);
+
+ Object syn_send_data=new Object();
+
+ long lastSendAckTime;
+
+ long lastReceiveDataTime;
+
+ long createTime=System.currentTimeMillis();;
+
+ String key;
+
+ Object syn_ident=new Object();
+
+ //客户端发起
+ TCPTun(CapEnv capEnv,
+ Inet4Address serverAddress, short serverPort,
+ MacAddress srcAddress_mac, MacAddress dstAddrress_mac){
+ this.capEnv=capEnv;
+ sendHandle=capEnv.sendHandle;
+ this.remoteAddress=serverAddress;
+ this.remotePort=serverPort;
+ localAddress=capEnv.local_ipv4;
+ localPort=(short)(random.nextInt(64*1024-1-10000)+10000);
+ Packet syncPacket=null;
+ try {
+ syncPacket = PacketUtils.createSync(srcAddress_mac, dstAddrress_mac, localAddress, localPort,serverAddress, serverPort, localStartSequence,getIdent());
+ try {
+ sendHandle.sendPacket(syncPacket);
+ localSequence=localStartSequence+1;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ MLog.println("发送第一次握手 "+" ident "+localIdent);
+ MLog.println(""+syncPacket);
+
+ }
+
+ //服务端接收
+ TCPTun(CapEnv capServerEnv,
+ Inet4Address remoteAddress,short remotePort){
+ this.capEnv=capServerEnv;
+ this.remoteAddress=remoteAddress;
+ this.remotePort=remotePort;
+ sendHandle=capEnv.sendHandle;
+ localPort=capServerEnv.listenPort;
+ localAddress=capEnv.local_ipv4;
+ }
+
+ void init_client(Inet4Address clientAddress,int clientPort,
+ Inet4Address serverAddress,int serverPort,
+ int client_start_sequence){
+
+ }
+
+ void init_server(Inet4Address clientAddress,int clientPort,
+ Inet4Address serverAddress,int serverPort,
+ int client_start_sequence,int server_start_sequence){
+
+ }
+
+ public void process_server(final Packet packet, EthernetHeader ethernetHeader, IpV4Header ipV4Header, TcpPacket tcpPacket, boolean client){
+ TcpHeader tcpHeader=tcpPacket.getHeader();
+
+ if(!preDataReady){
+ if(!connectReady){
+ //第一次握手
+ dstMacaAddress=ethernetHeader.getSrcAddr();
+ if(tcpHeader.getSyn()&&!tcpHeader.getAck()){
+ remoteStartSequence=tcpHeader.getSequenceNumber();
+ remoteSequence=remoteStartSequence+1;
+ remoteSequence_max=remoteSequence;
+ MLog.println("接收第一次握手 "+remoteAddress.getHostAddress()+":"+remotePort+"->"+localAddress.getHostAddress()+":"+localPort+" ident "+ipV4Header.getIdentification());
+ MLog.println(""+packet);
+ Packet responePacket=PacketUtils.createSyncAck(
+ capEnv.local_mac,
+ capEnv.gateway_mac,
+ localAddress,(short)localPort,
+ ipV4Header.getSrcAddr(),tcpHeader.getSrcPort().value(),
+ tcpHeader.getSequenceNumber()+1,localStartSequence,(short)0
+ );
+ try {
+ sendHandle.sendPacket(responePacket);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ localSequence=localStartSequence+1;
+ MLog.println("发送第二次握手 "+capEnv.local_mac+"->"+capEnv.gateway_mac+" "+localAddress+"->"+" ident "+0);
+
+ MLog.println(""+responePacket);
+ }
+
+ if(!tcpHeader.getSyn()&&tcpHeader.getAck()){
+ if(tcpPacket.getPayload()==null){
+ //第三次握手,客户端确认
+ if(tcpHeader.getAcknowledgmentNumber()==localSequence){
+ MLog.println("接收第三次握手 "+" ident "+ipV4Header.getIdentification());
+ MLog.println(packet+"");
+ Thread t1=new Thread(){
+ public void run(){
+ //startSend(basePacket_server,syc_sequence_client+1);
+ }
+ };
+ //t1.start();
+ connectReady=true;
+ }
+ }
+ //MLog.println("客户端响应preview\n "+packet);
+ //MLog.println("request "+tcp.ack());
+ sendedTable_server.remove(tcpHeader.getAcknowledgmentNumber());
+ boolean selfAck=selfAckTable.contains(ipV4Header.getIdentification());
+ //MLog.println("客户端确认 "+"selfack "+selfAck+" id "+ipV4Header.getIdentification()+" ack_sequence "+tcpHeader.getAcknowledgmentNumberAsLong()+" "+sendedTable_server.size()+"ppppppp "+tcpHeader);
+ }
+
+ }else {
+ if(tcpPacket.getPayload()!=null){
+ preDataReady=true;
+ onReceiveDataPacket( tcpPacket, tcpHeader, ipV4Header );
+ byte[] sim=getSimResponeHead();
+ sendData(sim);
+ }
+ }
+ }else {
+ if(tcpPacket.getPayload()!=null){
+ onReceiveDataPacket( tcpPacket, tcpHeader, ipV4Header );
+ TunData td=new TunData();
+ td.tun=this;
+ td.data=tcpPacket.getPayload().getRawData();
+ capEnv.vDatagramSocket.onReceinveFromTun(td);
+ }
+ }
+ if(tcpHeader.getRst()){
+ MLog.println("reset packet "+ipV4Header.getIdentification()+" "+tcpHeader.getSequenceNumber()+" "+remoteAddress.getHostAddress()+":"+remotePort+"->"+localAddress.getHostAddress()+":"+localPort+" "+" ident "+ipV4Header.getIdentification());
+ }
+
+ }
+
+ public void process_client(CapEnv capEnv, final Packet packet, EthernetHeader ethernetHeader, IpV4Header ipV4Header, TcpPacket tcpPacket, boolean client){
+
+ TcpHeader tcpHeader=tcpPacket.getHeader();
+ byte[] payload=null;
+ if(tcpPacket.getPayload()!=null){
+ payload=tcpPacket.getPayload().getRawData();
+ }
+
+ if(!preDataReady){
+ if(!connectReady){
+ if(tcpHeader.getAck()&&tcpHeader.getSyn()){
+ if(tcpHeader.getAcknowledgmentNumber()==(localStartSequence+1)){
+ MLog.println("接收第二次握手 "+" ident "+ipV4Header.getIdentification());
+ MLog.println(""+packet);
+ remoteStartSequence=tcpHeader.getSequenceNumber();
+ remoteSequence=remoteStartSequence+1;
+ remoteSequence_max=remoteSequence;
+ Packet p3=PacketUtils.createAck(capEnv.local_mac, capEnv.gateway_mac, capEnv.local_ipv4, localPort, remoteAddress, remotePort, remoteSequence , localSequence,getIdent());
+ try {
+ sendHandle.sendPacket(p3);
+ MLog.println("发送第三次握手 "+" ident "+localIdent);
+ MLog.println(""+p3);
+ connectReady=true;
+
+ byte[] sim=getSimRequestHead(remotePort);
+ sendData(sim);
+ MLog.println("发送请求 "+" ident "+localIdent);
+ } catch (PcapNativeException e) {
+ e.printStackTrace();
+ } catch (NotOpenException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }else {
+ if(tcpPacket.getPayload()!=null){
+ preDataReady=true;
+ onReceiveDataPacket( tcpPacket, tcpHeader, ipV4Header );
+ MLog.println("接收响应 "+" ident "+ipV4Header.getIdentification());
+ }
+ }
+
+ }else {
+ if(tcpPacket.getPayload()!=null){
+ //MLog.println("客户端正式接收数据 "+capClientEnv.vDatagramSocket);
+ onReceiveDataPacket( tcpPacket, tcpHeader, ipV4Header );
+ TunData td=new TunData();
+ td.tun=this;
+ td.data=tcpPacket.getPayload().getRawData();
+ capEnv.vDatagramSocket.
+ onReceinveFromTun(td);
+ }
+ }
+ if(tcpHeader.getRst()){
+ MLog.println("reset packet "+ipV4Header.getIdentification()+" "+tcpHeader.getSequenceNumber()+" "+remoteAddress.getHostAddress()+":"+remotePort+"->"+localAddress.getHostAddress()+":"+localPort);
+ }
+
+ }
+
+ void onReceiveDataPacket(TcpPacket tcpPacket, TcpHeader tcpHeader, IpV4Header ipV4Header ){
+ if(System.currentTimeMillis()-lastSendAckTime>1000){
+ int rs=tcpHeader.getSequenceNumber()+tcpPacket.getPayload().getRawData().length;
+ if(rs>remoteSequence_max){
+ remoteSequence_max=rs;
+ }
+ Packet ackPacket=PacketUtils.createAck(
+ capEnv.local_mac,
+ capEnv.gateway_mac,
+ localAddress,(short)localPort,
+ ipV4Header.getSrcAddr(),tcpHeader.getSrcPort().value(),
+ remoteSequence_max, localSequence,getIdent());
+ try {
+ sendHandle.sendPacket(ackPacket);
+ } catch (Exception e) {
+ e.printStackTrace();
+
+ }
+ lastSendAckTime=System.currentTimeMillis();
+ lastReceiveDataTime=System.currentTimeMillis();
+ }
+ }
+
+ void sendData(byte[] data){
+ Packet dataPacket=PacketUtils.createDataPacket(capEnv.local_mac,
+ capEnv.gateway_mac,
+ localAddress,localPort,
+ remoteAddress,remotePort,
+ localSequence,remoteSequence_max, data, (short) getIdent());
+ synchronized (syn_send_data) {
+ try {
+ sendHandle.sendPacket(dataPacket);
+ localSequence+=data.length;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+ short getIdent(){
+ synchronized (syn_ident) {
+ localIdent++;
+ if(localIdent>=Short.MAX_VALUE){
+ localIdent=0;
+ }
+ }
+ return (short) localIdent;
+ }
+
+ public static byte[] getSimResponeHead(){
+ StringBuffer sb=new StringBuffer();
+
+ sb.append("HTTP/1.1 200 OK"+"\r\n");
+ sb.append("Server: Apache/2.2.15 (CentOS)"+"\r\n");
+ sb.append("Accept-Ranges: bytes"+"\r\n");
+ sb.append("Content-Length: "+(Math.abs(random.nextInt()))+"\r\n");
+ sb.append("Connection: Keep-Alive"+"\r\n");
+ sb.append("Content-Type: application/octet-stream"+"\r\n");
+ sb.append("\r\n");
+
+ String simRequest=sb.toString();
+ byte[] simData=simRequest.getBytes();
+ return simData;
+ }
+
+ public static byte[] getSimRequestHead(int port){
+ StringBuffer sb=new StringBuffer();
+ String domainName=getRandomString(5+random.nextInt(10))+".com";
+ sb.append("GET /"+getRandomString(8+random.nextInt(10))+"."+getRandomString(2+random.nextInt(5))+" HTTP/1.1"+"\r\n");
+ sb.append("Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*"+"\r\n");
+ sb.append("Accept-Language: zh-CN"+"\r\n");
+ sb.append("Accept-Encoding: gzip, deflate"+"\r\n");
+ sb.append("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:36.0) Gecko/20100101 Firefox/36.0"+"\r\n");
+ sb.append("Host: "+domainName+"\r\n");
+ sb.append("Connection: Keep-Alive"+"\r\n");
+ sb.append("\r\n");
+ String simRequest=sb.toString();
+ byte[] simData=simRequest.getBytes();
+ return simData;
+ }
+
+ public static String getRandomString(int length) { //length表示生成字符串的长度
+ String base = "abcdefghkmnopqrstuvwxyz";
+ Random random = new Random();
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < length; i++) {
+ int number = random.nextInt(base.length());
+ sb.append(base.charAt(number));
+ }
+ return sb.toString();
+ }
+
+ public InetAddress getSourcrAddress() {
+ return localAddress;
+ }
+
+ public int getSourcePort() {
+ return localPort;
+ }
+
+ public void setSourcePort(short sourcePort) {
+ this.localPort = sourcePort;
+ }
+
+ public boolean isConnectReady() {
+ return connectReady;
+ }
+
+ public void setConnectReady(boolean connectReady) {
+ this.connectReady = connectReady;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+}
diff --git a/src/main/java/net/fs/cap/TunData.java b/src/main/java/net/fs/cap/TunData.java
new file mode 100755
index 0000000..a202571
--- /dev/null
+++ b/src/main/java/net/fs/cap/TunData.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.cap;
+
+
+public class TunData {
+
+ TCPTun tun;
+
+ byte[] data;
+
+}
diff --git a/src/main/java/net/fs/cap/TunManager.java b/src/main/java/net/fs/cap/TunManager.java
new file mode 100755
index 0000000..169c222
--- /dev/null
+++ b/src/main/java/net/fs/cap/TunManager.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.cap;
+
+import net.fs.rudp.CopiedIterator;
+import net.fs.utils.MLog;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+public class TunManager {
+
+ HashMap connTable=new HashMap();
+
+ static TunManager tunManager;
+
+ {
+ tunManager=this;
+ }
+
+ TCPTun defaultTcpTun;
+
+ Thread scanThread;
+
+ Object syn_scan=new Object();
+
+ CapEnv capEnv;
+
+ {
+ scanThread=new Thread(){
+ public void run(){
+ while(true){
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ scan();
+ }
+ }
+ };
+ scanThread.start();
+ }
+
+ TunManager(CapEnv capEnv){
+ this.capEnv=capEnv;
+ }
+
+ void scan(){
+ Iterator it=getConnTableIterator();
+ while(it.hasNext()){
+ String key=it.next();
+ TCPTun tun=connTable.get(key);
+ if(tun!=null){
+ if(tun.preDataReady){
+ //无数据超时
+ long t=System.currentTimeMillis()-tun.lastReceiveDataTime;
+ if(t>6000){
+ connTable.remove(key);
+ if(capEnv.client){
+ defaultTcpTun=null;
+ MLog.println("tcp隧道超时");
+ }
+ }
+ }else{
+ //连接中超时
+ if(System.currentTimeMillis()-tun.createTime>5000){
+ connTable.remove(key);
+ }
+ }
+ }
+ }
+ }
+
+ public void removeTun(TCPTun tun){
+ connTable.remove(tun.key);
+ }
+
+ Iterator getConnTableIterator(){
+ Iterator it=null;
+ synchronized (syn_scan) {
+ it=new CopiedIterator(connTable.keySet().iterator());
+ }
+ return it;
+ }
+
+ public static TunManager get(){
+ return tunManager;
+ }
+
+ public TCPTun getTcpConnection_Client(String remoteAddress,short remotePort,short localPort){
+ return connTable.get(remoteAddress+":"+remotePort+":"+localPort);
+ }
+
+ public void addConnection_Client(TCPTun conn) {
+ synchronized (syn_scan) {
+ String key=conn.remoteAddress.getHostAddress()+":"+conn.remotePort+":"+conn.localPort;
+ //MLog.println("addConnection "+key);
+ conn.setKey(key);
+ connTable.put(key, conn);
+ }
+ }
+
+ public TCPTun getTcpConnection_Server(String remoteAddress,short remotePort){
+ return connTable.get(remoteAddress+":"+remotePort);
+ }
+
+ public void addConnection_Server(TCPTun conn) {
+ synchronized (syn_scan) {
+ String key=conn.remoteAddress.getHostAddress()+":"+conn.remotePort;
+ //MLog.println("addConnection "+key);
+ conn.setKey(key);
+ connTable.put(key, conn);
+ }
+ }
+
+ public TCPTun getDefaultTcpTun() {
+ return defaultTcpTun;
+ }
+
+ public void setDefaultTcpTun(TCPTun defaultTcpTun) {
+ this.defaultTcpTun = defaultTcpTun;
+ }
+
+}
diff --git a/src/main/java/net/fs/cap/VDatagramSocket.java b/src/main/java/net/fs/cap/VDatagramSocket.java
new file mode 100755
index 0000000..7264e5b
--- /dev/null
+++ b/src/main/java/net/fs/cap/VDatagramSocket.java
@@ -0,0 +1,129 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.cap;
+
+import net.fs.rudp.Route;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class VDatagramSocket extends DatagramSocket{
+
+ boolean useTcpTun=true;
+
+ boolean client=true;
+
+ LinkedBlockingQueue packetList=new LinkedBlockingQueue ();
+
+ CapEnv capEnv;
+
+ int localPort;
+
+ Object syn_tun=new Object();
+
+ boolean tunConnecting=false;
+
+ public VDatagramSocket() throws SocketException {
+
+ }
+
+ public VDatagramSocket(int port) throws SocketException {
+ localPort=port;
+ }
+
+ public int getLocalPort() {
+ return localPort;
+ }
+
+ public void send(DatagramPacket p) throws IOException {
+ TCPTun tun=null;
+ if(client){
+ tun=capEnv.tcpManager.getDefaultTcpTun();
+ if(tun!=null){
+ if(!tun.remoteAddress.getHostAddress().equals(p.getAddress().getHostAddress())
+ ||CapEnv.toUnsigned(tun.remotePort)!=p.getPort()){
+ capEnv.tcpManager.removeTun(tun);
+ capEnv.tcpManager.setDefaultTcpTun(null);
+ }
+ }else {
+ tryConnectTun_Client(p.getAddress(),(short) p.getPort());
+ tun=capEnv.tcpManager.getDefaultTcpTun();
+ }
+ }else {
+ tun=capEnv.tcpManager.getTcpConnection_Server(p.getAddress().getHostAddress(), (short) p.getPort());
+ }
+ if(tun!=null){
+ if(tun.preDataReady){
+ tun.sendData(p.getData());
+ }else{
+ throw new IOException("隧道未连接!");
+ }
+ }else{
+
+ throw new IOException("隧道不存在! "+" thread "+Route.es.getActiveCount()+" "+p.getAddress()+":"+p.getPort());
+ }
+ }
+
+ //130端口调用
+ void tryConnectTun_Client(InetAddress dstAddress,short dstPort){
+ synchronized (syn_tun) {
+ if(capEnv.tcpManager.getDefaultTcpTun()==null){
+ if(tunConnecting){
+ try {
+ syn_tun.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }else {
+ tunConnecting=true;
+ try {
+ capEnv.createTcpTun_Client(dstAddress.getHostAddress(), dstPort);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ tunConnecting=false;
+ }
+ }
+ }
+ }
+
+
+ public synchronized void receive(DatagramPacket p) throws IOException {
+ TunData td=null;
+ try {
+ td=packetList.take();
+ p.setData(td.data);
+ p.setLength(td.data.length);
+ p.setAddress(td.tun.remoteAddress);
+ p.setPort(CapEnv.toUnsigned(td.tun.remotePort));
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ void onReceinveFromTun(TunData td){
+ packetList.add(td);
+ }
+
+ public boolean isClient() {
+ return client;
+ }
+
+ public void setClient(boolean client) {
+ this.client = client;
+ }
+
+ public CapEnv getCapEnv() {
+ return capEnv;
+ }
+
+ public void setCapEnv(CapEnv capEnv) {
+ this.capEnv = capEnv;
+ capEnv.vDatagramSocket=this;
+ }
+
+}
diff --git a/src/main/java/net/fs/client/AddMapFrame.java b/src/main/java/net/fs/client/AddMapFrame.java
new file mode 100755
index 0000000..44aa20d
--- /dev/null
+++ b/src/main/java/net/fs/client/AddMapFrame.java
@@ -0,0 +1,156 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import net.miginfocom.swing.MigLayout;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class AddMapFrame extends JDialog{
+
+ private static final long serialVersionUID = -3248779355079724594L;
+
+ ClientUI ui;
+
+ JTextField portTextField, text_port,nameTextField;
+
+ int downloadSpeed,uploadSpeed;
+
+ MapRule maprule_origin;
+
+ boolean edit=false;
+
+ AddMapFrame(final ClientUI ui,JFrame parent,final MapRule maprule_origin,final boolean edit){
+ super(parent, ModalityType.APPLICATION_MODAL);
+ this.ui=ui;
+ this.edit=edit;
+ this.maprule_origin=maprule_origin;
+ setTitle("增加映射");
+ //setSize(size);
+ if(edit){
+ setTitle("编辑映射");
+ }
+
+ JPanel panel=(JPanel) getContentPane();
+ panel.setLayout(new MigLayout("alignx center,aligny center,insets 10 10 10 10"));
+
+
+ String text=""
+ + "单位Mb ( 1Mb=128KB,10Mb=1280KB )
"
+ + ""+"请正确输入,该值会直接影响加速效果.";
+
+ JPanel p3=new JPanel();
+ panel.add(p3,"wrap");
+ p3.setBorder(BorderFactory.createEtchedBorder());
+ p3.setLayout(new MigLayout("inset 5 5 5 5"));
+
+ p3.add(new JLabel("名称:"));
+ nameTextField=new JTextField();
+ p3.add(nameTextField,"width :100: ,wrap");
+
+ p3.add(new JLabel("远程端口:"));
+ portTextField=new JTextField("");
+ p3.add(portTextField,"width :50:,wrap");
+ portTextField.setToolTipText("需要加速的端口号");
+
+ p3.add(new JLabel("本地端口: "));
+ text_port=new JTextField();
+ p3.add(text_port,"width :50: ,wrap");
+
+ JPanel p6=new JPanel();
+ panel.add(p6,"align center,wrap");
+ p6.setLayout(new MigLayout("align center"));
+
+ JButton button_ok=createButton("确定");
+ p6.add(button_ok);
+ button_ok.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ try {
+ checkName(nameTextField.getText());
+ checkPort(text_port.getText());
+ checkPort(portTextField.getText());
+ String name=nameTextField.getText();
+ int listen_port=Integer.parseInt(text_port.getText());
+ int dst_port=Integer.parseInt(portTextField.getText());
+ MapRule mapRule_new=new MapRule();
+ mapRule_new.setName(name);
+ mapRule_new.listen_port=listen_port;
+ mapRule_new.setDst_port(dst_port);
+ if(!edit){
+ ui.mapClient.portMapManager.addMapRule(mapRule_new);
+ }else {
+ ui.mapClient.portMapManager.updateMapRule(maprule_origin,mapRule_new);
+ }
+ ui.loadMapRule();
+ ui.select(mapRule_new.name);
+ setVisible(false);
+ } catch (Exception e1) {
+ //e2.printStackTrace();
+ JOptionPane.showMessageDialog(ui.mainFrame, e1.getMessage(),"消息",JOptionPane.WARNING_MESSAGE);
+ }
+ }
+ });
+
+ p6.add(new JLabel(" "));
+
+ JButton button_cancel=createButton("取消");
+ p6.add(button_cancel);
+ button_cancel.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ setVisible(false);
+ }
+ });
+
+
+ if(edit){
+ nameTextField.setText(maprule_origin.name);
+ text_port.setText(maprule_origin.listen_port+"");
+ portTextField.setText(maprule_origin.dst_port+"");
+ }
+
+ pack();
+ setLocationRelativeTo(parent);
+ setVisible(true);
+ }
+
+ void checkName(String s) throws Exception{
+ if(s.trim().equals("")){
+ throw new Exception("请输入名称");
+ }
+ }
+
+ void checkDstAddress(String s) throws Exception{
+ if(s.trim().equals("")){
+ throw new Exception("请输入目标地址");
+ }
+ }
+
+ void checkPort(String s) throws Exception{
+ int port=0;
+ try {
+ port=Integer.parseInt(s);
+ } catch (Exception e1) {
+ throw new Exception("请输入正确端口号");
+ }
+ if(port<1|port>256*256){
+ throw new Exception("请输入正确端口号");
+ }
+ }
+
+ JButton createButton(String name){
+ JButton button=new JButton(name);
+ button.setMargin(new Insets(0,5,0,5));
+ button.setFocusPainted(false);
+ return button;
+ }
+
+
+
+}
diff --git a/src/main/java/net/fs/client/AddressCellRenderer.java b/src/main/java/net/fs/client/AddressCellRenderer.java
new file mode 100755
index 0000000..dfe70e0
--- /dev/null
+++ b/src/main/java/net/fs/client/AddressCellRenderer.java
@@ -0,0 +1,66 @@
+package net.fs.client;
+
+import net.miginfocom.swing.MigLayout;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class AddressCellRenderer implements ListCellRenderer{
+
+ JPanel panel=null;
+
+ JLabel addressLabel;
+
+ Color color_normal=new Color(255,255,255);
+
+ Color color_selected=new Color(210,233,255);
+
+ JButton button_remove;
+
+ @Override
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
+ boolean cellHasFocus) {
+ if(panel==null){
+ init();
+ }
+ updateData( list, value, index, isSelected, cellHasFocus);
+ return panel;
+ }
+
+ void init(){
+ panel=new JPanel();
+ panel.setLayout(new MigLayout("insets 0 5 0 0","[grow,fill]rel[right]", "[]0[]"));
+ panel.setOpaque(true);
+ panel.setBackground(color_normal);
+ addressLabel=new JLabel("");
+ panel.add(addressLabel,"");
+ addressLabel.setOpaque(false);
+
+ button_remove=new JButton("x");
+ //panel.add(button_remove,"align right");
+ button_remove.setOpaque(false);
+ button_remove.setContentAreaFilled(false);
+ button_remove.setBorderPainted(false);
+ button_remove.setMargin(new Insets(0, 10, 0, 10));
+ button_remove.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ System.out.println(e);
+ }
+ });
+
+ }
+
+ void updateData(JList list, Object value, int index, boolean isSelected,boolean cellHasFocus){
+ addressLabel.setText(value.toString());
+ if(isSelected){
+ panel.setBackground(color_selected);
+ }else {
+ panel.setBackground(color_normal);
+ }
+ }
+
+}
diff --git a/src/main/java/net/fs/client/AlignCellRenderer.java b/src/main/java/net/fs/client/AlignCellRenderer.java
new file mode 100755
index 0000000..1fc3811
--- /dev/null
+++ b/src/main/java/net/fs/client/AlignCellRenderer.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import javax.swing.table.DefaultTableCellRenderer;
+
+public class AlignCellRenderer extends DefaultTableCellRenderer{
+ private static final long serialVersionUID = -6003599724059557606L;
+
+ public AlignCellRenderer(int align){
+ super();
+ setHorizontalAlignment(align);
+ }
+
+}
diff --git a/src/main/java/net/fs/client/ClientConfig.java b/src/main/java/net/fs/client/ClientConfig.java
new file mode 100755
index 0000000..d640be5
--- /dev/null
+++ b/src/main/java/net/fs/client/ClientConfig.java
@@ -0,0 +1,137 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import java.util.ArrayList;
+
+public class ClientConfig {
+
+ String serverAddress="";
+
+ int serverPort;
+
+ int remotePort;
+
+ public String getPasswordMd5_Proxy() {
+ return PasswordMd5_Proxy;
+ }
+
+ public void setPasswordMd5_Proxy(String passwordMd5_Proxy) {
+ PasswordMd5_Proxy = passwordMd5_Proxy;
+ }
+
+ String PasswordMd5_Proxy;
+
+ String PasswordMd5;
+
+ public String getPasswordMd5() {
+ return PasswordMd5;
+ }
+
+ public void setPasswordMd5(String passwordMd5) {
+ PasswordMd5 = passwordMd5;
+ }
+
+ int downloadSpeed,uploadSpeed;
+
+ boolean direct_cn=true;
+
+ int socks5Port=1083;
+
+ String remoteAddress;
+
+ String protocal="tcp";
+
+ boolean autoStart=false;
+
+ ArrayList recentAddressList=new ArrayList();
+
+ public String getServerAddress() {
+ return serverAddress;
+ }
+
+ public void setServerAddress(String serverAddress) {
+ this.serverAddress = serverAddress;
+ }
+
+ public int getServerPort() {
+ return serverPort;
+ }
+
+ public void setServerPort(int serverPort) {
+ this.serverPort = serverPort;
+ }
+
+ public int getRemotePort() {
+ return remotePort;
+ }
+
+ public void setRemotePort(int remotePort) {
+ this.remotePort = remotePort;
+ }
+
+ public boolean isDirect_cn() {
+ return direct_cn;
+ }
+
+ public void setDirect_cn(boolean direct_cn) {
+ this.direct_cn = direct_cn;
+ }
+
+ public int getDownloadSpeed() {
+ return downloadSpeed;
+ }
+
+ public void setDownloadSpeed(int downloadSpeed) {
+ this.downloadSpeed = downloadSpeed;
+ }
+
+ public int getUploadSpeed() {
+ return uploadSpeed;
+ }
+
+ public void setUploadSpeed(int uploadSpeed) {
+ this.uploadSpeed = uploadSpeed;
+ }
+
+ public int getSocks5Port() {
+ return socks5Port;
+ }
+
+ public void setSocks5Port(int socks5Port) {
+ this.socks5Port = socks5Port;
+ }
+
+ public String getRemoteAddress() {
+ return remoteAddress;
+ }
+
+ public void setRemoteAddress(String remoteAddress) {
+ this.remoteAddress = remoteAddress;
+ }
+
+ public String getProtocal() {
+ return protocal;
+ }
+
+ public void setProtocal(String protocal) {
+ this.protocal = protocal;
+ }
+
+ public boolean isAutoStart() {
+ return autoStart;
+ }
+
+ public void setAutoStart(boolean autoStart) {
+ this.autoStart = autoStart;
+ }
+
+ public ArrayList getRecentAddressList() {
+ return recentAddressList;
+ }
+
+ public void setRecentAddressList(ArrayList recentAddressList) {
+ this.recentAddressList = recentAddressList;
+ }
+
+}
diff --git a/src/main/java/net/fs/client/ClientNoUI.java b/src/main/java/net/fs/client/ClientNoUI.java
new file mode 100755
index 0000000..cbe92cb
--- /dev/null
+++ b/src/main/java/net/fs/client/ClientNoUI.java
@@ -0,0 +1,213 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import com.alibaba.fastjson.JSONObject;
+import net.fs.netty.SocksServer;
+import net.fs.rudp.Route;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class ClientNoUI implements ClientUII{
+
+ MapClient mapClient;
+
+ ClientConfig config;
+
+ String configFilePath="conf/client_config.json";
+
+ ClientNoUI() throws Exception {
+ loadConfig();
+ Route.localDownloadSpeed=config.downloadSpeed;
+ Route.localUploadSpeed=config.uploadSpeed;
+
+ //mapClient=new MapClient(config.getSocks5Port(),false);
+// mapClient.setUi(this);
+// mapClient.setMapServer(config.getServerAddress(), config.getServerPort(),config.getRemotePort() ,config.getPasswordMd5(),config.getPasswordMd5_Proxy(),config.isDirect_cn(),false,"");
+ }
+
+ void openUrl(String url){
+ try {
+ Desktop.getDesktop().browse(new URI(url));
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ } catch (URISyntaxException e1) {
+ e1.printStackTrace();
+ }
+ }
+
+ public void setMessage(String message){
+ //MLog.info("状态: "+message);
+ }
+
+ ClientConfig loadConfig(){
+ ClientConfig cfg=new ClientConfig();
+ if(!new File(configFilePath).exists()){
+ JSONObject json=new JSONObject();
+ try {
+ saveFile(json.toJSONString().getBytes(), configFilePath);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ String content=readFileUtf8(configFilePath);
+ JSONObject json= JSONObject.parseObject(content);
+ cfg.setServerAddress(json.getString("server_address"));
+ cfg.setServerPort(json.getIntValue("server_port"));
+ cfg.setRemotePort(json.getIntValue("remote_port"));
+ if(json.containsKey("direct_cn")){
+ cfg.setDirect_cn(json.getBooleanValue("direct_cn"));
+ }
+ cfg.setDownloadSpeed(json.getIntValue("download_speed"));
+ cfg.setUploadSpeed(json.getIntValue("upload_speed"));
+ if(json.containsKey("socks5_port")){
+ cfg.setSocks5Port(json.getIntValue("socks5_port"));
+ }
+ config=cfg;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return cfg;
+ }
+
+ public static String readFileUtf8(String path) throws Exception{
+ String str=null;
+ FileInputStream fis=null;
+ DataInputStream dis=null;
+ try {
+ File file=new File(path);
+
+ int length=(int) file.length();
+ byte[] data=new byte[length];
+
+ fis=new FileInputStream(file);
+ dis=new DataInputStream(fis);
+ dis.readFully(data);
+ str=new String(data,"utf-8");
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }finally{
+ if(fis!=null){
+ try {
+ fis.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if(dis!=null){
+ try {
+ dis.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return str;
+ }
+
+ void saveFile(byte[] data,String path) throws Exception{
+ FileOutputStream fos=null;
+ try {
+ fos=new FileOutputStream(path);
+ fos.write(data);
+ } catch (Exception e) {
+ throw e;
+ } finally {
+ if(fos!=null){
+ fos.close();
+ }
+ }
+ }
+
+ public void updateUISpeed(int conn,int downloadSpeed,int uploadSpeed){
+// String string="连接数:"+conn+" 下载:"+Tools.getSizeStringKB(downloadSpeed)+"/S"
+// +" 上传:"+Tools.getSizeStringKB(uploadSpeed)+"/S";
+// if(downloadSpeedField!=null){
+// downloadSpeedField.setText(string);
+// }
+ }
+
+ JButton createButton(String name){
+ JButton button=new JButton(name);
+ button.setMargin(new Insets(0,5,0,5));
+ button.setFocusPainted(false);
+ return button;
+ }
+
+
+ void initUI(){
+ SwingUtilities.invokeLater(new Runnable() {
+
+ public void run() {
+ Font font = new Font("宋体",Font.PLAIN,12);
+ UIManager.put("ToolTip.font",font);
+ UIManager.put("Table.font",font);
+ UIManager.put("TableHeader.font",font);
+ UIManager.put("TextField.font",font);
+ UIManager.put("ComboBox.font",font);
+ UIManager.put("TextField.font",font);
+ UIManager.put("PasswordField.font",font);
+ UIManager.put("TextArea.font,font",font);
+ UIManager.put("TextPane.font",font);
+ UIManager.put("EditorPane.font",font);
+ UIManager.put("FormattedTextField.font",font);
+ UIManager.put("Button.font",font);
+ UIManager.put("CheckBox.font",font);
+ UIManager.put("RadioButton.font",font);
+ UIManager.put("ToggleButton.font",font);
+ UIManager.put("ProgressBar.font",font);
+ UIManager.put("DesktopIcon.font",font);
+ UIManager.put("TitledBorder.font",font);
+ UIManager.put("Label.font",font);
+ UIManager.put("List.font",font);
+ UIManager.put("TabbedPane.font",font);
+ UIManager.put("MenuBar.font",font);
+ UIManager.put("Menu.font",font);
+ UIManager.put("MenuItem.font",font);
+ UIManager.put("PopupMenu.font",font);
+ UIManager.put("CheckBoxMenuItem.font",font);
+ UIManager.put("RadioButtonMenuItem.font",font);
+ UIManager.put("Spinner.font",font);
+ UIManager.put("Tree.font",font);
+ UIManager.put("ToolBar.font",font);
+ UIManager.put("OptionPane.messageFont",font);
+ UIManager.put("OptionPane.buttonFont",font);
+
+ ToolTipManager.sharedInstance().setInitialDelay(200);
+ }
+
+ });
+ }
+
+ @Override
+ public boolean login() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean updateNode(boolean testSpeed) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isOsx_fw_pf() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isOsx_fw_ipfw() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+}
diff --git a/src/main/java/net/fs/client/ClientStartNoUI.java b/src/main/java/net/fs/client/ClientStartNoUI.java
new file mode 100755
index 0000000..8c203fb
--- /dev/null
+++ b/src/main/java/net/fs/client/ClientStartNoUI.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+public class ClientStartNoUI {
+
+ public static void main(String[] args) throws Exception {
+ new ClientNoUI();
+ }
+
+}
diff --git a/src/main/java/net/fs/client/ClientUI.java b/src/main/java/net/fs/client/ClientUI.java
new file mode 100755
index 0000000..3926850
--- /dev/null
+++ b/src/main/java/net/fs/client/ClientUI.java
@@ -0,0 +1,1248 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import net.fs.rudp.Route;
+import net.fs.utils.LogOutputStream;
+import net.fs.utils.MLog;
+import net.fs.utils.StringRandom;
+import net.fs.utils.Tools;
+import net.miginfocom.swing.MigLayout;
+import net.fs.netty.SocksServer;
+import org.pcap4j.core.Pcaps;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+public class ClientUI implements ClientUII, WindowListener {
+
+ JFrame mainFrame;
+
+ JComponent mainPanel;
+
+ JComboBox text_serverAddress;
+
+ MapClient mapClient;
+
+ JLabel uploadSpeedField, downloadSpeedField, stateText;
+
+ ClientConfig config = null;
+
+ String configFilePath = "conf/client_config.json";
+ //String configFilePath = "client_config.json";
+
+ String logoImg = "img/offline.png";
+
+ String offlineImg = "img/offline.png";
+
+ String name ="大爷专版";
+
+ private TrayIcon trayIcon;
+
+ private SystemTray tray;
+
+ int serverVersion = -1;
+
+ int localVersion = 3;
+
+ boolean checkingUpdate = false;
+
+ String domain = "";
+
+ String homeUrl;
+
+ public static ClientUI ui;
+
+ JTextField text_ds, text_us;
+
+ boolean ky = true;
+
+ String errorMsg = "保存失败请检查输入信息!";
+
+ JButton button_site;
+
+ MapRuleListModel model;
+
+ public MapRuleListTable tcpMapRuleListTable;
+
+ boolean capSuccess = false;
+ Exception capException = null;
+ boolean b1 = false;
+
+ boolean success_firewall_windows = true;
+
+ boolean success_firewall_osx = true;
+
+ String systemName = null;
+
+ public boolean osx_fw_pf = false;
+
+ public boolean osx_fw_ipfw = false;
+
+ public boolean isVisible = true;
+
+ JRadioButton r_tcp, r_udp;
+
+ String updateUrl;
+
+ boolean min=false;
+
+ LogFrame logFrame;
+
+ LogOutputStream los;
+
+ boolean tcpEnable=true;
+
+ {
+ //domain = "";
+ // homeUrl = "";
+ //updateUrl = "";
+ domain = "firsh.me";
+ homeUrl = "https://firsh.me";
+ //updateUrl = "http://fs.d1sm.net/finalspeed/update.properties";
+ updateUrl = "conf/update.properties";
+ }
+
+ ClientUI(final boolean isVisible,boolean min) {
+
+ this.min=min;
+ setVisible(isVisible);
+
+ if(isVisible){
+ los=new LogOutputStream(System.out);
+ System.setOut(los);
+ System.setErr(los);
+ }
+
+
+ systemName = System.getProperty("os.name").toLowerCase();
+ MLog.info("System: " + systemName + " " + System.getProperty("os.version"));
+ ui = this;
+ mainFrame = new JFrame();
+ mainFrame.setIconImage(Toolkit.getDefaultToolkit().getImage(logoImg));
+ initUI();
+ checkQuanxian();
+ loadConfig();
+ StringRandom title = new StringRandom().getInstance();
+ mainFrame.setResizable(true); //锁定大小
+ mainFrame.setTitle(title.getRandomString(16));
+ mainFrame.addWindowListener(this);
+ mainPanel = (JPanel) mainFrame.getContentPane();
+ mainPanel.setLayout(new MigLayout("align center , insets 10 10 10 10"));
+ mainPanel.setBorder(null);
+
+ mainFrame.addWindowListener(new java.awt.event.WindowAdapter() {
+ public void windowOpened(WindowEvent evt) {
+ text_ds.requestFocus();
+ }
+ });
+
+ JPanel centerPanel = new JPanel();
+ mainPanel.add(centerPanel, "wrap");
+ centerPanel.setLayout(new MigLayout("insets 0 0 0 0"));
+
+ JPanel loginPanel = new JPanel();
+ centerPanel.add(loginPanel, "");
+ loginPanel.setLayout(new MigLayout("insets 0 0 0 0"));
+
+ JLabel label_msg = new JLabel();
+ label_msg.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
+ JPanel rightPanel = new JPanel();
+ rightPanel.setLayout(new MigLayout("insets 10 0 10 0"));
+
+ centerPanel.add(rightPanel, "width :: ,top");
+
+ JPanel mapPanel = new JPanel();
+ mapPanel.setLayout(new MigLayout("insets 0 0 0 0"));
+ mapPanel.setBorder(BorderFactory.createTitledBorder("加速列表"));
+
+ rightPanel.add(mapPanel);
+ //隐藏
+ rightPanel.setVisible(false);
+
+ model = new MapRuleListModel();
+ tcpMapRuleListTable = new MapRuleListTable(this, model);
+
+ JScrollPane tablePanel = new JScrollPane();
+ tablePanel.setViewportView(tcpMapRuleListTable);
+
+ mapPanel.add(tablePanel, "height 50:160:1024 ,growy,width :250:,wrap");
+ tablePanel.addMouseListener(new MouseListener() {
+
+ public void mouseClicked(MouseEvent e) {
+ tcpMapRuleListTable.clearSelection();
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+ public void mousePressed(MouseEvent e) {
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ }
+
+ });
+
+
+ JPanel p9 = new JPanel();
+ p9.setLayout(new MigLayout("insets 1 0 3 0 "));
+ mapPanel.add(p9, "align center,wrap");
+ JButton button_add = createButton("添加");
+ p9.add(button_add);
+ button_add.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ AddMapFrame sf = new AddMapFrame(ui, mainFrame, null, false);
+ }
+
+ });
+ JButton button_edit = createButton("修改");
+ p9.add(button_edit);
+ button_edit.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ int index = tcpMapRuleListTable.getSelectedRow();
+ if (index > -1) {
+ MapRule mapRule = model.getMapRuleAt(index);
+ AddMapFrame sf = new AddMapFrame(ui, mainFrame, mapRule, true);
+ }
+ }
+
+ });
+ JButton button_remove = createButton("删除");
+ p9.add(button_remove);
+ button_remove.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ int index = tcpMapRuleListTable.getSelectedRow();
+ if (index > -1) {
+ MapRule mapRule = model.getMapRuleAt(index);
+
+ mapClient.portMapManager.removeMapRule(mapRule.getName());
+ loadMapRule();
+ }
+ }
+
+ });
+
+ JPanel socket = new JPanel();
+ socket.setLayout(new MigLayout("insets 3 0 5 2 "));
+ mapPanel.add(socket, "align center,wrap");
+ JButton start_ss = createButton("Socket5:1081");
+ p9.add(start_ss);
+ start_ss.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ SocksServer.getInstance().start();
+ }
+ });
+
+
+ JPanel pa = new JPanel();
+ pa.setBorder(BorderFactory.createTitledBorder("服务器"));
+ pa.setLayout(new MigLayout("insets 0 0 0 0"));
+ loginPanel.add(pa, "growx,wrap");
+ JPanel p1 = new JPanel();
+ p1.setLayout(new MigLayout("insets 0 0 0 0"));
+ pa.add(p1, "wrap");
+ p1.add(new JLabel("地址:"), "width 50::");
+ text_serverAddress = new JComboBox();
+ text_serverAddress.setToolTipText("主机:端口号");
+ p1.add(text_serverAddress, "width 150::");
+ text_serverAddress.setEditable(true);
+ TextComponentPopupMenu.installToComponent(text_serverAddress);
+
+ ListCellRenderer renderer = new AddressCellRenderer();
+ text_serverAddress.setRenderer(renderer);
+ text_serverAddress.setEditable(true);
+
+ text_serverAddress.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ //System.out.println(text_serverAddress.getSelectedItem().toString());
+ }
+ });
+
+ for(int n=0;n0){
+ selectText=text_serverAddress.getModel().getElementAt(0).toString();
+ }
+ text_serverAddress.setSelectedItem(selectText);
+ }
+ }
+ }
+ });
+
+ JPanel panelr = new JPanel();
+ pa.add(panelr, "wrap");
+ panelr.setLayout(new MigLayout("insets 0 0 0 0"));
+ panelr.add(new JLabel("传输协议:"));
+ r_tcp = new JRadioButton("TCP");
+ r_tcp.setFocusPainted(false);
+ panelr.add(r_tcp);
+ r_udp = new JRadioButton("UDP");
+ r_udp.setFocusPainted(false);
+ panelr.add(r_udp);
+ ButtonGroup bg = new ButtonGroup();
+ bg.add(r_tcp);
+ bg.add(r_udp);
+ if (config.getProtocal().equals("udp")) {
+ r_udp.setSelected(true);
+ } else {
+ r_tcp.setSelected(true);
+ }
+
+
+ JPanel sp = new JPanel();
+ sp.setBorder(BorderFactory.createTitledBorder("物理带宽"));
+ sp.setLayout(new MigLayout("insets 5 5 5 5"));
+ JPanel pa1 = new JPanel();
+ sp.add(pa1, "wrap");
+ pa1.setLayout(new MigLayout("insets 0 0 0 0"));
+ loginPanel.add(sp, "wrap");
+ pa1.add(new JLabel("下载:"), "width ::");
+ text_ds = new JTextField("0");
+ pa1.add(text_ds, "width 80::");
+ text_ds.setHorizontalAlignment(JTextField.RIGHT);
+ text_ds.setEditable(false);
+
+ JButton button_set_speed = createButton("设置带宽");
+ pa1.add(button_set_speed);
+ button_set_speed.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ SpeedSetFrame sf = new SpeedSetFrame(ui, mainFrame);
+ }
+ });
+
+
+ JPanel pa2 = new JPanel();
+ sp.add(pa2, "wrap");
+ pa2.setLayout(new MigLayout("insets 0 0 0 0"));
+ loginPanel.add(sp, "wrap");
+ pa2.add(new JLabel("上传:"), "width ::");
+ text_us = new JTextField("0");
+ pa2.add(text_us, "width 80::");
+ text_us.setHorizontalAlignment(JTextField.RIGHT);
+ text_us.setEditable(false);
+
+
+ JPanel sp2 = new JPanel();
+ sp2.setLayout(new MigLayout("insets 0 0 0 0"));
+ loginPanel.add(sp2, "align center, wrap");
+
+ final JCheckBox cb=new JCheckBox("开机启动",config.isAutoStart());
+ sp2.add(cb, "align center");
+ cb.addActionListener(new ActionListener(){
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ config.setAutoStart(cb.isSelected());
+ saveConfig();
+ setAutoRun(config.isAutoStart());
+ }
+
+ });
+ JButton button_show_log=createButton("显示日志");
+ sp2.add(button_show_log,"wrap");
+ button_show_log.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if(logFrame==null){
+ logFrame=new LogFrame(ui);
+ logFrame.setSize(700, 400);
+ logFrame.setLocationRelativeTo(null);
+ los.addListener(logFrame);
+
+ if(los.getBuffer()!=null){
+ logFrame.showText(los.getBuffer().toString());
+ los.setBuffer(null);
+ }
+ }
+ logFrame.setVisible(true);
+ }
+ });
+
+ JPanel p4 = new JPanel();
+ p4.setLayout(new MigLayout("insets 5 0 0 0 "));
+ loginPanel.add(p4, "align center,wrap");
+ JButton button_save = createButton("Start");
+ p4.add(button_save);
+
+ button_site = createButton("网站");
+ p4.add(button_site);
+ button_site.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ openUrl(homeUrl);
+ }
+ });
+
+ JButton button_exit = createButton("退出");
+ p4.add(button_exit);
+ button_exit.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ System.exit(0);
+ }
+ });
+ button_save.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (config.getDownloadSpeed() == 0 || config.getUploadSpeed() == 0) {
+ SpeedSetFrame sf = new SpeedSetFrame(ui, mainFrame);
+ }
+ setMessage("");
+ saveConfig();
+ }
+ });
+
+ stateText = new JLabel("");
+ mainPanel.add(stateText, "align right ,wrap");
+
+ downloadSpeedField = new JLabel();
+ downloadSpeedField.setHorizontalAlignment(JLabel.RIGHT);
+ mainPanel.add(downloadSpeedField, "align right ");
+
+
+ updateUISpeed(0, 0, 0);
+ setMessage(" ");
+
+ text_serverAddress.setSelectedItem(getServerAddressFromConfig());
+
+ if (config.getRemoteAddress() != null && !config.getRemoteAddress().equals("") && config.getRemotePort() > 0) {
+ String remoteAddressTxt = config.getRemoteAddress() + ":" + config.getRemotePort();
+ }
+
+ int width = 500;
+ if (systemName.contains("os x")) {
+ width = 600;
+ }
+ //mainFrame.setSize(width, 380);
+
+ mainFrame.pack();
+
+ mainFrame.setLocationRelativeTo(null);
+
+ PopupMenu trayMenu = new PopupMenu();
+ tray = SystemTray.getSystemTray();
+ trayIcon = new TrayIcon(Toolkit.getDefaultToolkit().getImage(offlineImg), name, trayMenu);
+ trayIcon.setImageAutoSize(true);
+ ActionListener listener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ mainFrame.toFront();
+ setVisible(true);
+ mainFrame.setVisible(true);
+ }
+ };
+ trayIcon.addActionListener(listener);
+ trayIcon.addMouseListener(new MouseListener() {
+
+ public void mouseClicked(MouseEvent arg0) {
+ }
+
+ public void mouseEntered(MouseEvent arg0) {
+ }
+
+ public void mouseExited(MouseEvent arg0) {
+ }
+
+ public void mousePressed(MouseEvent arg0) {
+ }
+
+ public void mouseReleased(MouseEvent arg0) {
+ }
+
+ });
+
+ try {
+ tray.add(trayIcon);
+ } catch (AWTException e1) {
+ e1.printStackTrace();
+ }
+ MenuItem item3;
+ try {
+ item3 = new MenuItem("Exit");
+ //item3 = new MenuItem("Exit");
+ ActionListener al = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ exit();
+ }
+ };
+ item3.addActionListener(al);
+ trayMenu.add(item3);
+
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+
+
+ boolean tcpEnvSuccess=true;
+ checkFireWallOn();
+ if (!success_firewall_windows) {
+ tcpEnvSuccess=false;
+ if (isVisible) {
+ mainFrame.setVisible(true);
+ JOptionPane.showMessageDialog(mainFrame, "启动windows防火墙失败,请先运行防火墙服务.");
+ }
+ MLog.println("启动windows防火墙失败,请先运行防火墙服务.");
+ // System.exit(0);
+ }
+ if (!success_firewall_osx) {
+ tcpEnvSuccess=false;
+ if (isVisible) {
+ mainFrame.setVisible(true);
+ JOptionPane.showMessageDialog(mainFrame, "启动ipfw/pfctl防火墙失败,请先安装.");
+ }
+ MLog.println("启动ipfw/pfctl防火墙失败,请先安装.");
+ //System.exit(0);
+ }
+
+ Thread thread = new Thread() {
+ public void run() {
+ try {
+ Pcaps.findAllDevs();
+ b1 = true;
+ } catch (Exception e3) {
+ e3.printStackTrace();
+
+ }
+ }
+ };
+ thread.start();
+ try {
+ thread.join();
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+ //JOptionPane.showMessageDialog(mainFrame,System.getProperty("os.name"));
+ if (!b1) {
+ tcpEnvSuccess=false;
+ try {
+ SwingUtilities.invokeAndWait(new Runnable() {
+
+ @Override
+ public void run() {
+ String msg = "启动失败,请先安装libpcap,否则无法使用tcp协议";
+ if (systemName.contains("windows")) {
+ msg = "启动失败,请先安装winpcap,否则无法使用tcp协议";
+ }
+ if (isVisible) {
+ mainFrame.setVisible(true);
+ JOptionPane.showMessageDialog(mainFrame, msg);
+ }
+ MLog.println(msg);
+ if (systemName.contains("windows")) {
+ try {
+ Process p = Runtime.getRuntime().exec("winpcap_install.exe", null);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ tcpEnable=false;
+ //System.exit(0);
+ }
+ }
+
+ });
+ } catch (InvocationTargetException e2) {
+ e2.printStackTrace();
+ } catch (InterruptedException e2) {
+ e2.printStackTrace();
+ }
+ }
+
+
+ try {
+ mapClient = new MapClient(this,tcpEnvSuccess);
+ } catch (final Exception e1) {
+ e1.printStackTrace();
+ capException = e1;
+ //System.exit(0);
+ }
+
+ try {
+ SwingUtilities.invokeAndWait(new Runnable() {
+
+ @Override
+ public void run() {
+
+ if (!mapClient.route_tcp.capEnv.tcpEnable) {
+ if (isVisible) {
+ mainFrame.setVisible(true);
+ }
+ r_tcp.setEnabled(false);
+ r_udp.setSelected(true);
+ //JOptionPane.showMessageDialog(mainFrame,"无可用网络接口,只能使用udp协议.");
+ }
+
+ //System.exit(0);
+ }
+
+ });
+ } catch (InvocationTargetException e2) {
+ e2.printStackTrace();
+ } catch (InterruptedException e2) {
+ e2.printStackTrace();
+ }
+
+ mapClient.setUi(this);
+
+ mapClient.setMapServer(config.getServerAddress(), config.getServerPort(), config.getRemotePort(), null, null, config.isDirect_cn(), config.getProtocal().equals("tcp"),
+ null);
+
+ Route.es.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ checkUpdate();
+ }
+ });
+
+ setSpeed(config.getDownloadSpeed(), config.getUploadSpeed());
+ if (isVisible&!min) {
+ mainFrame.setVisible(true);
+ }
+
+ loadMapRule();
+
+ if (config.getDownloadSpeed() == 0 || config.getUploadSpeed() == 0) {
+ SpeedSetFrame sf = new SpeedSetFrame(ui, mainFrame);
+ }
+
+ //socket启动
+
+ }
+
+ String getServerAddressFromConfig(){
+ String server_addressTxt = config.getServerAddress();
+ if (config.getServerAddress() != null && !config.getServerAddress().equals("")) {
+ if (config.getServerPort() != 150
+ && config.getServerPort() != 0) {
+ server_addressTxt += (":" + config.getServerPort());
+ }
+ }
+ return server_addressTxt;
+ }
+
+ void checkFireWallOn() {
+ if (systemName.contains("os x")) {
+ String runFirewall = "ipfw";
+ try {
+ final Process p = Runtime.getRuntime().exec(runFirewall, null);
+ osx_fw_ipfw = true;
+ } catch (IOException e) {
+ //e.printStackTrace();
+ }
+ runFirewall = "pfctl";
+ try {
+ final Process p = Runtime.getRuntime().exec(runFirewall, null);
+ osx_fw_pf = true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ success_firewall_osx = osx_fw_ipfw | osx_fw_pf;
+ } else if (systemName.contains("linux")) {
+ String runFirewall = "service iptables start";
+ } else if (systemName.contains("windows")) {
+ String runFirewall = "netsh advfirewall set allprofiles state on";
+ Thread standReadThread = null;
+ Thread errorReadThread = null;
+ try {
+ final Process p = Runtime.getRuntime().exec(runFirewall, null);
+ standReadThread = new Thread() {
+ public void run() {
+ InputStream is = p.getInputStream();
+ BufferedReader localBufferedReader = new BufferedReader(new InputStreamReader(is));
+ while (true) {
+ String line;
+ try {
+ line = localBufferedReader.readLine();
+ if (line == null) {
+ break;
+ } else {
+ if (line.contains("Windows")) {
+ success_firewall_windows = false;
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ //error();
+ exit();
+ break;
+ }
+ }
+ }
+ };
+ standReadThread.start();
+
+ errorReadThread = new Thread() {
+ public void run() {
+ InputStream is = p.getErrorStream();
+ BufferedReader localBufferedReader = new BufferedReader(new InputStreamReader(is));
+ while (true) {
+ String line;
+ try {
+ line = localBufferedReader.readLine();
+ if (line == null) {
+ break;
+ } else {
+ System.out.println("error" + line);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ //error();
+ exit();
+ break;
+ }
+ }
+ }
+ };
+ errorReadThread.start();
+ } catch (IOException e) {
+ e.printStackTrace();
+ success_firewall_windows = false;
+ //error();
+ }
+
+ if (standReadThread != null) {
+ try {
+ standReadThread.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ if (errorReadThread != null) {
+ try {
+ errorReadThread.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ }
+
+ void checkQuanxian() {
+ if (systemName.contains("windows")) {
+ boolean b = false;
+ File file = new File(System.getenv("WINDIR") + "\\test.file");
+ System.out.println("auth : "+file.getAbsolutePath());
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ b = file.exists();
+ file.delete();
+
+ if (!b) {
+ //mainFrame.setVisible(true);
+ if (isVisible) {
+ JOptionPane.showMessageDialog(null, "请以管理员身份运行! ");
+ }
+ MLog.println("请以管理员身份运行,否则可能无法正常工作! ");
+ System.exit(0);
+ }
+ }
+ }
+
+ void loadMapRule() {
+ tcpMapRuleListTable.setMapRuleList(mapClient.portMapManager.getMapList());
+ }
+
+ void select(String name) {
+ int index = model.getMapRuleIndex(name);
+ if (index > -1) {
+ tcpMapRuleListTable.getSelectionModel().setSelectionInterval(index, index);
+ }
+ }
+
+ void setSpeed(int downloadSpeed, int uploadSpeed) {
+ config.setDownloadSpeed(downloadSpeed);
+ config.setUploadSpeed(uploadSpeed);
+ int s1 = (int) ((float) downloadSpeed * 1.1f);
+ text_ds.setText(" " + Tools.getSizeStringKB(s1) + "/s ");
+ int s2 = (int) ((float) uploadSpeed * 1.1f);
+ text_us.setText(" " + Tools.getSizeStringKB(s2) + "/s ");
+ Route.localDownloadSpeed = downloadSpeed;
+ Route.localUploadSpeed = config.uploadSpeed;
+
+ saveConfig();
+ }
+
+
+ void exit() {
+ mainFrame.setVisible(false);
+ System.exit(0);
+ }
+
+ void openUrl(String url) {
+ try {
+ Desktop.getDesktop().browse(new URI(url));
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ } catch (URISyntaxException e1) {
+ e1.printStackTrace();
+ }
+ }
+
+ public void setMessage(String message) {
+ stateText.setText("状态: " + message);
+ }
+
+ ClientConfig loadConfig() {
+ ClientConfig cfg = new ClientConfig();
+ if (!new File(configFilePath).exists()) {
+ JSONObject json = new JSONObject();
+ try {
+ saveFile(json.toJSONString().getBytes(), configFilePath);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ String content = readFileUtf8(configFilePath);
+ JSONObject json = JSONObject.parseObject(content);
+ cfg.setServerAddress(json.getString("server_address"));
+ cfg.setServerPort(json.getIntValue("server_port"));
+ cfg.setRemotePort(json.getIntValue("remote_port"));
+ cfg.setRemoteAddress(json.getString("remote_address"));
+ if (json.containsKey("direct_cn")) {
+ cfg.setDirect_cn(json.getBooleanValue("direct_cn"));
+ }
+ cfg.setDownloadSpeed(json.getIntValue("download_speed"));
+ cfg.setUploadSpeed(json.getIntValue("upload_speed"));
+ if (json.containsKey("socks5_port")) {
+ cfg.setSocks5Port(json.getIntValue("socks5_port"));
+ }
+ if (json.containsKey("protocal")) {
+ cfg.setProtocal(json.getString("protocal"));
+ }
+ if (json.containsKey("auto_start")) {
+ cfg.setAutoStart(json.getBooleanValue("auto_start"));
+ }
+ if (json.containsKey("recent_address_list")) {
+ JSONArray list=json.getJSONArray("recent_address_list");
+ for (int i = 0; i < list.size(); i++) {
+ cfg.getRecentAddressList().add(list.get(i).toString());
+ }
+ }
+
+ config = cfg;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return cfg;
+ }
+
+ void saveConfig() {
+ Thread thread = new Thread() {
+ public void run() {
+ boolean success = false;
+ try {
+ int serverPort = 150;
+ String addressTxt ="";
+ if(text_serverAddress.getSelectedItem()!=null){
+ addressTxt =text_serverAddress.getSelectedItem().toString();
+ }
+ addressTxt = addressTxt.trim().replaceAll(" ", "");
+
+ String serverAddress = addressTxt;
+ if (addressTxt.startsWith("[")) {
+ int index = addressTxt.lastIndexOf("]:");
+ if (index > 0) {
+ serverAddress = addressTxt.substring(0, index + 1);
+ String ports = addressTxt.substring(index + 2);
+ serverPort = Integer.parseInt(ports);
+ }
+ } else {
+ int index = addressTxt.lastIndexOf(":");
+ if (index > 0) {
+ serverAddress = addressTxt.substring(0, index);
+ String ports = addressTxt.substring(index + 1);
+ serverPort = Integer.parseInt(ports);
+ }
+ }
+
+ String protocal = "tcp";
+ if (r_udp.isSelected()) {
+ protocal = "udp";
+ }
+
+ JSONObject json = new JSONObject();
+ json.put("server_address", serverAddress);
+ json.put("server_port", serverPort);
+ json.put("download_speed", config.getDownloadSpeed());
+ json.put("upload_speed", config.getUploadSpeed());
+ json.put("socks5_port", config.getSocks5Port());
+ json.put("protocal", protocal);
+ json.put("auto_start", config.isAutoStart());
+
+
+ if(text_serverAddress.getModel().getSize()>0){
+ text_serverAddress.removeItem(addressTxt);
+ }
+ text_serverAddress.insertItemAt(addressTxt, 0);
+ text_serverAddress.setSelectedItem(addressTxt);;
+
+
+ JSONArray recentAddressList=new JSONArray();
+
+
+ int size=text_serverAddress.getModel().getSize();
+ for(int n=0;n localVersion;
+ }
+
+ public void checkUpdate() {
+ for (int i = 0; i < 3; i++) {
+ checkingUpdate = true;
+ try {
+ Properties propServer = new Properties();
+ FileInputStream in = new FileInputStream(updateUrl);
+ propServer.load(in);
+ //HttpURLConnection uc = Tools.getConnection(updateUrl);
+ //uc.setUseCaches(false);
+ //InputStream in = uc.getInputStream();
+ //propServer.load(in);
+ //propServer.getProperty("version")
+ serverVersion = Integer.parseInt(propServer.getProperty("version"));
+ break;
+ } catch (Exception e) {
+ e.printStackTrace();
+ try {
+ Thread.sleep(3 * 1000);
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+ } finally {
+ checkingUpdate = false;
+ }
+ }
+ //this.haveNewVersion()
+ if (false) {
+ int option = JOptionPane.showConfirmDialog(mainFrame, "测试更新", "提醒", JOptionPane.WARNING_MESSAGE);
+ if (option == JOptionPane.YES_OPTION) {
+ openUrl(homeUrl);
+ }
+ }
+
+ }
+
+ void initUI() {
+ SwingUtilities.invokeLater(new Runnable() {
+
+ public void run() {
+ Font font = new Font("宋体", Font.PLAIN, 12);
+ UIManager.put("ToolTip.font", font);
+ UIManager.put("Table.font", font);
+ UIManager.put("TableHeader.font", font);
+ UIManager.put("TextField.font", font);
+ UIManager.put("ComboBox.font", font);
+ UIManager.put("TextField.font", font);
+ UIManager.put("PasswordField.font", font);
+ UIManager.put("TextArea.font,font", font);
+ UIManager.put("TextPane.font", font);
+ UIManager.put("EditorPane.font", font);
+ UIManager.put("FormattedTextField.font", font);
+ UIManager.put("Button.font", font);
+ UIManager.put("CheckBox.font", font);
+ UIManager.put("RadioButton.font", font);
+ UIManager.put("ToggleButton.font", font);
+ UIManager.put("ProgressBar.font", font);
+ UIManager.put("DesktopIcon.font", font);
+ UIManager.put("TitledBorder.font", font);
+ UIManager.put("Label.font", font);
+ UIManager.put("List.font", font);
+ UIManager.put("TabbedPane.font", font);
+ UIManager.put("MenuBar.font", font);
+ UIManager.put("Menu.font", font);
+ UIManager.put("MenuItem.font", font);
+ UIManager.put("PopupMenu.font", font);
+ UIManager.put("CheckBoxMenuItem.font", font);
+ UIManager.put("RadioButtonMenuItem.font", font);
+ UIManager.put("Spinner.font", font);
+ UIManager.put("Tree.font", font);
+ UIManager.put("ToolBar.font", font);
+ UIManager.put("OptionPane.messageFont", font);
+ UIManager.put("OptionPane.buttonFont", font);
+
+ ToolTipManager.sharedInstance().setInitialDelay(130);
+ }
+
+ });
+ }
+
+ public static void setAutoRun(boolean run) {
+ String s = new File(".").getAbsolutePath();
+ String currentPaht = s.substring(0, s.length() - 1);
+ StringBuffer sb = new StringBuffer();
+ StringTokenizer st = new StringTokenizer(currentPaht, "\\");
+ while (st.hasMoreTokens()) {
+ sb.append(st.nextToken());
+ sb.append("\\\\");
+ }
+ ArrayList list = new ArrayList();
+ list.add("Windows Registry Editor Version 5.00");
+ String name="fsclient";
+// if(PMClientUI.mc){
+// name="wlg_mc";
+// }
+ if (run) {
+ list.add("[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run]");
+ list.add("\""+name+"\"=\"" + sb.toString() + "finalspeedclient.exe -min" + "\"");
+ } else {
+ list.add("[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run]");
+ list.add("\""+name+"\"=-");
+ }
+
+ File file = null;
+ try {
+ file = new File("import.reg");
+ FileWriter fw = new FileWriter(file);
+ PrintWriter pw = new PrintWriter(fw);
+ for (int i = 0; i < list.size(); i++) {
+ String ss = list.get(i);
+ if (!ss.equals("")) {
+ pw.println(ss);
+ }
+ }
+ pw.flush();
+ pw.close();
+ Process p = Runtime.getRuntime().exec("regedit /s " + "import.reg");
+ p.waitFor();
+ } catch (Exception e1) {
+ // e1.printStackTrace();
+ } finally {
+ if (file != null) {
+ file.delete();
+ }
+ }
+ }
+
+ @Override
+ public void windowOpened(WindowEvent e) {
+ }
+
+ @Override
+ public void windowClosing(WindowEvent e) {
+ mainFrame.setVisible(false);
+ }
+
+ @Override
+ public void windowClosed(WindowEvent e) {
+ }
+
+ @Override
+ public void windowIconified(WindowEvent e) {
+ }
+
+ @Override
+ public void windowDeiconified(WindowEvent e) {
+ }
+
+ @Override
+ public void windowActivated(WindowEvent e) {
+ }
+
+ @Override
+ public void windowDeactivated(WindowEvent e) {
+ }
+
+
+ @Override
+ public boolean login() {
+ return false;
+ }
+
+
+ @Override
+ public boolean updateNode(boolean testSpeed) {
+ return true;
+
+ }
+
+ public boolean isOsx_fw_pf() {
+ return osx_fw_pf;
+ }
+
+ public void setOsx_fw_pf(boolean osx_fw_pf) {
+ this.osx_fw_pf = osx_fw_pf;
+ }
+
+ public boolean isOsx_fw_ipfw() {
+ return osx_fw_ipfw;
+ }
+
+ public void setOsx_fw_ipfw(boolean osx_fw_ipfw) {
+ this.osx_fw_ipfw = osx_fw_ipfw;
+ }
+
+ public void setVisible(boolean visible) {
+ this.isVisible = visible;
+ }
+}
diff --git a/src/main/java/net/fs/client/ClientUII.java b/src/main/java/net/fs/client/ClientUII.java
new file mode 100755
index 0000000..684fecd
--- /dev/null
+++ b/src/main/java/net/fs/client/ClientUII.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+public interface ClientUII {
+
+
+ void setMessage(String message);
+
+ void updateUISpeed(int connNum, int downSpeed, int upSpeed);
+
+ boolean login();
+
+ boolean updateNode(boolean testSpeed);
+
+ boolean isOsx_fw_pf();
+
+ boolean isOsx_fw_ipfw();
+
+}
diff --git a/src/main/java/net/fs/client/FSClient.java b/src/main/java/net/fs/client/FSClient.java
new file mode 100755
index 0000000..1bfbde3
--- /dev/null
+++ b/src/main/java/net/fs/client/FSClient.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import org.apache.commons.cli.*;
+
+public class FSClient {
+
+ public static void start() throws Exception{
+// SocksServer.getInstance().start();//启动netty
+ String[] args = new String[]{"-b"};
+ CommandLineParser parser = new DefaultParser();
+ Options options = new Options();
+ options.addOption("b", "back", false, "有此参数则运行CLI版本");
+ options.addOption("min", "minimize",false, "启动窗口最小化");
+ CommandLine commandLine = null;
+ try {
+ commandLine = parser.parse(options, args);
+ } catch (ParseException e) {
+ HelpFormatter helpFormatter = new HelpFormatter();
+ helpFormatter.printHelp("java -jar finalspeed.jar [-b/--back]", options);
+ System.exit(0);
+ }
+ boolean visible=!commandLine.hasOption("b");
+ boolean min=commandLine.hasOption("min");
+
+ new ClientUI(visible,min);
+ }
+}
diff --git a/src/main/java/net/fs/client/LogFrame.java b/src/main/java/net/fs/client/LogFrame.java
new file mode 100755
index 0000000..98f38c9
--- /dev/null
+++ b/src/main/java/net/fs/client/LogFrame.java
@@ -0,0 +1,117 @@
+package net.fs.client;
+
+import net.fs.utils.LogListener;
+import net.fs.utils.LogOutputStream;
+import net.miginfocom.swing.MigLayout;
+
+import javax.swing.*;
+import javax.swing.text.BadLocationException;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class LogFrame extends JFrame implements LogListener{
+
+ private static final long serialVersionUID = 8642892909397273483L;
+
+ ClientUI ui;
+
+ JTextArea logArea;
+
+ JScrollPane scroll;
+
+ boolean autoScroll=true;
+
+ final int SCROLL_BUFFER_SIZE = 1000;
+
+ LogFrame(ClientUI ui){
+ super("日志");
+ this.ui=ui;
+ JPanel panel=(JPanel) getContentPane();
+ panel.setLayout(new MigLayout("insets 5 5 5 5"));
+
+
+ logArea=new JTextArea();
+
+ scroll = new JScrollPane(logArea);
+
+ panel.add(scroll,"width :10240:,height :10240: ,wrap");
+
+ JPanel p3=new JPanel();
+ panel.add(p3,"align center,wrap");
+ p3.setLayout(new MigLayout("inset 5 5 5 5"));
+
+ final JCheckBox cb_lock=new JCheckBox("自动滚动",autoScroll);
+ p3.add(cb_lock,"align center");
+ cb_lock.addActionListener(new ActionListener(){
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ autoScroll=cb_lock.isSelected();
+ }
+
+ });
+
+ JButton button_clear=createButton("清空");
+ p3.add(button_clear);
+ button_clear.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ logArea.setText("");
+ }
+ });
+
+ }
+
+ public void trunkTextArea(JTextArea txtWin){
+ int numLinesToTrunk = txtWin.getLineCount() - SCROLL_BUFFER_SIZE;
+ if(numLinesToTrunk > 0)
+ {
+ try
+ {
+ int posOfLastLineToTrunk = txtWin.getLineEndOffset(numLinesToTrunk - 1);
+ txtWin.replaceRange("",0,posOfLastLineToTrunk);
+ }
+ catch (BadLocationException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ void showText(String text){
+ logArea.append(text);
+ trunkTextArea(logArea);
+ if(autoScroll){
+ JScrollBar vertical = scroll.getVerticalScrollBar();
+ vertical.setValue(vertical.getMaximum() );
+ }
+ }
+
+ @Override
+ public void onAppendContent(LogOutputStream los,final String text) {
+ SwingUtilities.invokeLater(new Runnable() {
+
+ @Override
+ public void run() {
+ logArea.append(text);
+ trunkTextArea(logArea);
+ if(autoScroll){
+ logArea.setCaretPosition(logArea.getDocument().getLength());
+// JScrollBar vertical = scroll.getVerticalScrollBar();
+// vertical.setValue(vertical.getMaximum() );
+ }
+ }
+ });
+
+ }
+
+ JButton createButton(String name){
+ JButton button=new JButton(name);
+ button.setMargin(new Insets(0,5,0,5));
+ button.setFocusPainted(false);
+ return button;
+ }
+
+
+}
diff --git a/src/main/java/net/fs/client/MapClient.java b/src/main/java/net/fs/client/MapClient.java
new file mode 100755
index 0000000..9640995
--- /dev/null
+++ b/src/main/java/net/fs/client/MapClient.java
@@ -0,0 +1,466 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import net.fs.rudp.*;
+import net.fs.utils.NetStatus;
+
+import java.io.*;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.util.HashSet;
+import java.util.Random;
+
+public class MapClient implements Trafficlistener{
+
+ ConnectionProcessor imTunnelProcessor;
+
+ Route route_udp,route_tcp;
+
+ short routePort=45;
+
+ ClientUII ui;
+
+ String serverAddress="";
+
+ InetAddress address=null;
+
+ int serverPort=130;
+
+ NetStatus netStatus;
+
+ long lastTrafficTime;
+
+ int downloadSum=0;
+
+ int uploadSum=0;
+
+ Thread clientUISpeedUpdateThread;
+
+ int connNum=0;
+
+ HashSet processTable=new HashSet();
+
+ Object syn_process=new Object();
+
+ static MapClient mapClient;
+
+ PortMapManager portMapManager;
+
+ public String mapdstAddress;
+
+ public int mapdstPort;
+
+ static int monPort=25874;
+
+ String systemName=System.getProperty("os.name").toLowerCase();
+
+ boolean useTcp=true;
+
+ long clientId;
+
+ Random ran=new Random();
+
+ boolean tcpEnable;
+
+ MapClient(ClientUI ui,boolean tcpEnvSuccess) throws Exception {
+ this.ui=ui;
+ mapClient=this;
+ try {
+ final ServerSocket socket=new ServerSocket(monPort);
+ new Thread(){
+ public void run(){
+ try {
+ socket.accept();
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.exit(0);
+ }
+ }
+ }.start();
+ } catch (Exception e) {
+ //e.printStackTrace();
+ System.exit(0);
+ }
+ try {
+ route_tcp = new Route(null,routePort,Route.mode_client,true,tcpEnvSuccess);
+ } catch (Exception e1) {
+ //e1.printStackTrace();
+ throw e1;
+ }
+ try {
+ route_udp = new Route(null,routePort,Route.mode_client,false,tcpEnvSuccess);
+ } catch (Exception e1) {
+ //e1.printStackTrace();
+ throw e1;
+ }
+ netStatus=new NetStatus();
+
+ portMapManager=new PortMapManager(this);
+
+ clientUISpeedUpdateThread=new Thread(){
+ public void run(){
+ while(true){
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+ updateUISpeed();
+ }
+ }
+ };
+ clientUISpeedUpdateThread.start();
+
+ Route.addTrafficlistener(this);
+
+ }
+
+ public static MapClient get(){
+ return mapClient;
+ }
+
+ private void updateUISpeed(){
+ if(ui!=null){
+ ui.updateUISpeed(connNum,netStatus.getDownSpeed(),netStatus.getUpSpeed());
+ }
+ }
+
+ public void setMapServer(String serverAddress,int serverPort,int remotePort,String passwordMd5,String password_proxy_Md5,boolean direct_cn,boolean tcp,
+ String password){
+ if(this.serverAddress==null
+ ||!this.serverAddress.equals(serverAddress)
+ ||this.serverPort!=serverPort){
+
+ if(route_tcp.lastClientControl!=null){
+ route_tcp.lastClientControl.close();
+ }
+
+ if(route_udp.lastClientControl!=null){
+ route_udp.lastClientControl.close();
+ }
+
+ cleanRule();
+ if(serverAddress!=null&&!serverAddress.equals("")){
+ setFireWallRule(serverAddress,serverPort);
+ }
+
+ }
+ this.serverAddress=serverAddress;
+ this.serverPort=serverPort;
+ address=null;
+ useTcp=tcp;
+ resetConnection();
+ }
+
+
+ void setFireWallRule(String serverAddress,int serverPort){
+ String ip;
+ try {
+ ip = InetAddress.getByName(serverAddress).getHostAddress();
+ if(systemName.contains("mac os")){
+ if(ui.isOsx_fw_pf ()){
+ String tempPath="./pf.conf";
+ File f=new File(tempPath);
+ File d=f.getParentFile();
+ if(!d.exists()){
+ d.mkdirs();
+ }
+ if(f.exists()){
+ f.delete();
+ }
+ //必须换行结束
+ String content="block drop quick proto tcp from any to "+ip+" port = "+serverPort+"\n";
+ saveFile(content.getBytes(), tempPath);
+
+ String cmd1="pfctl -d";
+ runCommand(cmd1);
+
+ String cmd2="pfctl -Rf "+f.getAbsolutePath();
+ runCommand(cmd2);
+
+ String cmd3="pfctl -e";
+ runCommand(cmd3);
+
+ //f.delete();
+ }else if(ui.isOsx_fw_ipfw()){
+ String cmd2="sudo ipfw add 5050 deny tcp from any to "+ip+" "+serverAddress+" out";
+ runCommand(cmd2);
+ }
+ }else if(systemName.contains("linux")){
+ String cmd2="iptables -t filter -A OUTPUT -d "+ip+" -p tcp --dport "+serverPort+" -j DROP -m comment --comment tcptun_fs ";
+ runCommand(cmd2);
+ }else if (systemName.contains("windows")) {
+ try {
+ if(systemName.contains("xp")||systemName.contains("2003")){
+ String cmd_add1="ipseccmd -w REG -p \"tcptun_fs\" -r \"Block TCP/"+serverPort+"\" -f 0/255.255.255.255="+ip+"/255.255.255.255:"+serverPort+":tcp -n BLOCK -x ";
+ final Process p2 = Runtime.getRuntime().exec(cmd_add1,null);
+ p2.waitFor();
+ }else {
+ String cmd_add1="netsh advfirewall firewall add rule name=tcptun_fs protocol=TCP dir=out remoteport="+serverPort+" remoteip="+ip+" action=block ";
+ final Process p2 = Runtime.getRuntime().exec(cmd_add1,null);
+ p2.waitFor();
+ String cmd_add2="netsh advfirewall firewall add rule name=tcptun_fs protocol=TCP dir=in remoteport="+serverPort+" remoteip="+ip+" action=block ";
+ Process p3 = Runtime.getRuntime().exec(cmd_add2,null);
+ p3.waitFor();
+ }
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ void saveFile(byte[] data,String path) throws Exception{
+ FileOutputStream fos=null;
+ try {
+ fos=new FileOutputStream(path);
+ fos.write(data);
+ } catch (Exception e) {
+ throw e;
+ } finally {
+ if(fos!=null){
+ fos.close();
+ }
+ }
+ }
+
+ void cleanRule(){
+ if(systemName.contains("mac os")){
+ cleanTcpTunRule_osx();
+ }else if(systemName.contains("linux")){
+ cleanTcpTunRule_linux();
+ }else {
+ try {
+ if(systemName.contains("xp")||systemName.contains("2003")){
+ String cmd_delete="ipseccmd -p \"tcptun_fs\" -w reg -y";
+ final Process p1 = Runtime.getRuntime().exec(cmd_delete,null);
+ p1.waitFor();
+ }else {
+ String cmd_delete="netsh advfirewall firewall delete rule name=tcptun_fs ";
+ final Process p1 = Runtime.getRuntime().exec(cmd_delete,null);
+ p1.waitFor();
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ void cleanTcpTunRule_osx(){
+ String cmd2="sudo ipfw delete 5050";
+ runCommand(cmd2);
+ }
+
+
+ void cleanTcpTunRule_linux(){
+ while(true){
+ int row=getRow_linux();
+ if(row>0){
+ //MLog.println("删除行 "+row);
+ String cmd="iptables -D OUTPUT "+row;
+ runCommand(cmd);
+ }else {
+ break;
+ }
+ }
+ }
+
+ int getRow_linux(){
+ int row_delect=-1;
+ String cme_list_rule="iptables -L -n --line-number";
+ //String [] cmd={"netsh","advfirewall set allprofiles state on"};
+ Thread errorReadThread=null;
+ try {
+ final Process p = Runtime.getRuntime().exec(cme_list_rule,null);
+
+ errorReadThread=new Thread(){
+ public void run(){
+ InputStream is=p.getErrorStream();
+ BufferedReader localBufferedReader = new BufferedReader(new InputStreamReader(is));
+ while (true){
+ String line;
+ try {
+ line = localBufferedReader.readLine();
+ if (line == null){
+ break;
+ }else{
+ //System.out.println("erroraaa "+line);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ //error();
+ break;
+ }
+ }
+ }
+ };
+ errorReadThread.start();
+
+
+
+ InputStream is=p.getInputStream();
+ BufferedReader localBufferedReader = new BufferedReader(new InputStreamReader(is));
+ while (true){
+ String line;
+ try {
+ line = localBufferedReader.readLine();
+ // System.out.println("standaaa "+line);
+ if (line == null){
+ break;
+ }else{
+ if(line.contains("tcptun_fs")){
+ int index=line.indexOf(" ");
+ if(index>0){
+ String n=line.substring(0, index);
+ try {
+ if(row_delect<0){
+ //System.out.println("standaaabbb "+line);
+ row_delect=Integer.parseInt(n);
+ }
+ } catch (Exception e) {
+
+ }
+ }
+ };
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ break;
+ }
+ }
+
+
+ errorReadThread.join();
+ p.waitFor();
+ } catch (Exception e) {
+ e.printStackTrace();
+ //error();
+ }
+ return row_delect;
+ }
+
+ void resetConnection(){
+ synchronized (syn_process) {
+
+ }
+ }
+
+ public void onProcessClose(ClientProcessorInterface process){
+ synchronized (syn_process) {
+ processTable.remove(process);
+ }
+ }
+
+ synchronized public void closeAndTryConnect_Login(boolean testSpeed){
+ close();
+ boolean loginOK=ui.login();
+ if(loginOK){
+ ui.updateNode(testSpeed);
+ //testPool();
+ }
+ }
+
+ synchronized public void closeAndTryConnect(){
+ close();
+ //testPool();
+ }
+
+ public void close(){
+// closeAllProxyRequest();
+// poolManage.close();
+// CSocketPool.closeAll();
+ }
+
+ public void trafficDownload(TrafficEvent event) {
+ ////#MLog.println("下载 "+event.getTraffic());
+ netStatus.addDownload(event.getTraffic());
+ lastTrafficTime=System.currentTimeMillis();
+ downloadSum+=event.getTraffic();
+ }
+
+ public void trafficUpload(TrafficEvent event) {
+ ////#MLog.println("上传 "+event.getTraffic());
+ netStatus.addUpload(event.getTraffic());
+ lastTrafficTime=System.currentTimeMillis();
+ uploadSum+=event.getTraffic();
+ }
+
+ static void runCommand(String command){
+ Thread standReadThread=null;
+ Thread errorReadThread=null;
+ try {
+ final Process p = Runtime.getRuntime().exec(command,null);
+ standReadThread=new Thread(){
+ public void run(){
+ InputStream is=p.getInputStream();
+ BufferedReader localBufferedReader = new BufferedReader(new InputStreamReader(is));
+ while (true){
+ String line;
+ try {
+ line = localBufferedReader.readLine();
+ //System.out.println("stand "+line);
+ if (line == null){
+ break;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ break;
+ }
+ }
+ }
+ };
+ standReadThread.start();
+
+ errorReadThread=new Thread(){
+ public void run(){
+ InputStream is=p.getErrorStream();
+ BufferedReader localBufferedReader = new BufferedReader(new InputStreamReader(is));
+ while (true){
+ String line;
+ try {
+ line = localBufferedReader.readLine();
+ if (line == null){
+ break;
+ }else{
+ //System.out.println("error "+line);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ //error();
+ break;
+ }
+ }
+ }
+ };
+ errorReadThread.start();
+ standReadThread.join();
+ errorReadThread.join();
+ p.waitFor();
+ } catch (Exception e) {
+ e.printStackTrace();
+ //error();
+ }
+ }
+
+ public boolean isUseTcp() {
+ return useTcp;
+ }
+
+ public void setUseTcp(boolean useTcp) {
+ this.useTcp = useTcp;
+ }
+
+ public ClientUII getUi() {
+ return ui;
+ }
+
+ public void setUi(ClientUII ui) {
+ this.ui = ui;
+ }
+
+}
diff --git a/src/main/java/net/fs/client/MapRule.java b/src/main/java/net/fs/client/MapRule.java
new file mode 100755
index 0000000..01b1da9
--- /dev/null
+++ b/src/main/java/net/fs/client/MapRule.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import java.io.Serializable;
+import java.net.ServerSocket;
+
+public class MapRule implements Serializable{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3504577683070928480L;
+
+ int listen_port;
+
+ int dst_port;
+
+ String name;
+
+ boolean using=false;
+
+ ServerSocket serverSocket;
+
+ public int getListen_port() {
+ return listen_port;
+ }
+
+ public void setListen_port(int listen_port) {
+ this.listen_port = listen_port;
+ }
+
+ public int getDst_port() {
+ return dst_port;
+ }
+
+ public void setDst_port(int dst_port) {
+ this.dst_port = dst_port;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/src/main/java/net/fs/client/MapRuleListModel.java b/src/main/java/net/fs/client/MapRuleListModel.java
new file mode 100755
index 0000000..d6756c6
--- /dev/null
+++ b/src/main/java/net/fs/client/MapRuleListModel.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import javax.swing.table.AbstractTableModel;
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class MapRuleListModel extends AbstractTableModel{
+
+ private static final long serialVersionUID = 2267856423317178816L;
+
+ private List mapRuleList;
+
+ String titles[] ;
+
+ Class> types[] = new Class[] {String.class, String.class, String.class,String.class, String.class, String.class};
+
+ MapRuleListModel(){
+ mapRuleList=new ArrayList ();
+ titles = new String[] {""};
+ }
+
+ public void setMapRuleList(List list){
+ mapRuleList.clear();
+ if(list!=null){
+ mapRuleList.addAll(list);
+ }
+ fireTableDataChanged();
+ }
+
+ public int getMapRuleIndex(String name){
+ int index=-1;
+ int i=0;
+ for(MapRule r:mapRuleList){
+ if(name.equals(r.getName())){
+ index=i;
+ break;
+ }
+ i++;
+ }
+ return index;
+ }
+
+ List getMapRuleList(){
+ return mapRuleList;
+ }
+
+ public MapRule getMapRuleAt(int row){
+ if(row>-1&row getColumnClass(int c) {
+ return types[c];
+ }
+
+
+ public boolean isCellEditable(int row, int col) {
+ boolean b=false;
+ if(col==0){
+ b=true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/net/fs/client/MapRuleListTable.java b/src/main/java/net/fs/client/MapRuleListTable.java
new file mode 100755
index 0000000..4c1fe98
--- /dev/null
+++ b/src/main/java/net/fs/client/MapRuleListTable.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import javax.swing.*;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.List;
+
+public class MapRuleListTable extends JTable{
+
+ private static final long serialVersionUID = -547936371303904463L;
+
+ MapRuleListModel model;
+
+ MapRuleListTable table;
+
+ ClientUI ui;
+
+ MapRuleListTable(ClientUI ui,final MapRuleListModel model){
+ super();
+ this.model=model;
+ this.ui=ui;
+ table=this;
+ setModel(model);
+
+ setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ setRowSorter(null);
+
+ getColumnModel().getColumn(0).setMinWidth(30);
+
+ MapRuleRender rr=new MapRuleRender();
+ getColumnModel().getColumn(0).setCellRenderer(rr);
+ setRowHeight(50);
+
+ new Thread(){
+ public void run() {
+ while(true){
+ try {
+ Thread.sleep(1000);
+ refresh();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }.start();
+
+ addMouseListener(new MouseListener() {
+
+ @Override
+ public void mouseReleased(MouseEvent e) {}
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ if(e.getButton()==MouseEvent.BUTTON3&&e.getClickCount()==1){
+ int index=rowAtPoint(e.getPoint());
+ int modelIndex=convertRowIndexToModel(index);
+ getSelectionModel().setSelectionInterval(modelIndex, modelIndex);
+ }
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e) {}
+
+ @Override
+ public void mouseEntered(MouseEvent e) {}
+
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if(e.getButton()==MouseEvent.BUTTON1&&e.getClickCount()==2){
+ editRule();
+ }
+ }
+ });
+
+ }
+
+ void editRule(){
+ int index=getSelectedRow();
+ int modelIndex=convertRowIndexToModel(index);
+ MapRule mapRule=getModel().getMapRuleAt(modelIndex);
+ AddMapFrame sf=new AddMapFrame(ui,ui.mainFrame,mapRule,true);
+ //sf.setVisible(true);
+ }
+
+ void refresh(){
+ SwingUtilities.invokeLater(new Runnable() {
+
+ @Override
+ public void run() {
+ updateUI();
+ }
+ });
+ }
+
+ public void setMapRuleList(List list){
+ model.setMapRuleList(list);
+ }
+
+ public MapRuleListModel getModel() {
+ return model;
+ }
+
+ public void setModel(MapRuleListModel model) {
+ super.setModel(model);
+ this.model = model;
+ }
+
+}
diff --git a/src/main/java/net/fs/client/MapRuleRender.java b/src/main/java/net/fs/client/MapRuleRender.java
new file mode 100755
index 0000000..965e2ca
--- /dev/null
+++ b/src/main/java/net/fs/client/MapRuleRender.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import net.miginfocom.swing.MigLayout;
+import sun.swing.DefaultLookup;
+
+import javax.swing.*;
+import javax.swing.table.TableCellRenderer;
+import java.awt.*;
+
+public class MapRuleRender extends JLabel implements TableCellRenderer {
+
+ private static final long serialVersionUID = -3260748459008436510L;
+
+ JPanel pleft,pright,p1;
+
+ JLabel label_wan_address;
+ JLabel label2;
+
+ MapRule rule;
+
+ {
+ setOpaque(true);
+ setLayout(new MigLayout("insets 8 10 0 0"));
+ label_wan_address=new JLabel();
+ add(label_wan_address,"width :500:,wrap");
+ label_wan_address.setBackground(new Color(0f,0f,0f,0f));
+ label_wan_address.setOpaque(true);
+ label2=new JLabel();
+ add(label2,"width :500:,wrap");
+ label2.setBackground(new Color(0f,0f,0f,0f));
+ label2.setOpaque(true);
+ }
+
+
+ void update(MapRule rule,JTable table,int row){
+ this.rule=rule;
+ int rowHeight=50;
+ int h=table.getRowHeight(row);
+ if(h!=rowHeight){
+ table.setRowHeight(row, rowHeight);
+ }
+ String name=rule.getName();
+ if(name==null){
+ name="无";
+ }else if(name.trim().equals("")){
+ name="无";
+ }
+ label_wan_address.setText("名称: "+rule.name+" 远程端口: "+rule.dst_port);
+ label2.setText("本地端口: "+rule.getListen_port());
+
+ }
+
+ @Override
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected, boolean hasFocus, int row, int column) {
+ Color fg = null;
+ Color bg = null;
+ JTable.DropLocation dropLocation = table.getDropLocation();
+ if (dropLocation != null
+ && !dropLocation.isInsertRow()
+ && !dropLocation.isInsertColumn()
+ && dropLocation.getRow() == row
+ && dropLocation.getColumn() == column) {
+
+ fg = DefaultLookup.getColor(this, ui, "Table.dropCellForeground");
+ bg = DefaultLookup.getColor(this, ui, "Table.dropCellBackground");
+ isSelected = true;
+ }
+ if (isSelected) {
+ setBackground(DefaultLookup.getColor(this, ui, "Table.dropCellBackground"));
+ } else {
+ setBackground( DefaultLookup.getColor(this, ui, "Table.alternateRowColor"));
+ }
+ MapRule rule=(MapRule)value;
+ update(rule,table,row);
+ return this;
+ }
+
+}
diff --git a/src/main/java/net/fs/client/Pipe.java b/src/main/java/net/fs/client/Pipe.java
new file mode 100755
index 0000000..648385d
--- /dev/null
+++ b/src/main/java/net/fs/client/Pipe.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import net.fs.rudp.ConnectionUDP;
+import net.fs.rudp.UDPInputStream;
+import net.fs.rudp.UDPOutputStream;
+import net.fs.utils.MLog;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class Pipe {
+
+
+ int lastTime=-1;
+
+
+ boolean readed=false;
+
+ public Pipe p2;
+
+ byte[] pv;
+
+ int pvl;
+
+ int readedLength;
+
+ String successMessage;
+
+ int dstPort=-1;
+
+ public void pipe(InputStream is,UDPOutputStream tos,int initSpeed,final Pipe p2) throws Exception{
+
+ int len=0;
+ byte[] buf=new byte[100*1024];
+ boolean sendeda=false;
+ while((len=is.read(buf))>0){
+ readed=true;
+ if(!sendeda){
+ sendeda=true;
+ }
+ tos.write(buf, 0, len);
+ }
+ }
+
+
+
+ void sendSleep(long startTime,int speed,int length){
+ long needTime=(long) (1000f*length/speed);
+ long usedTime=System.currentTimeMillis()-startTime;
+ if(usedTime0){
+ readedLength+=len;
+ if(!sendedb){
+ pv=buf;
+ pvl=len;
+ sendedb=true;
+ }
+ if(dstPort>0){
+ if(ClientUI.ui!=null){
+ if(!msged){
+ msged=true;
+ //String msg="隧道链接成功 "+dstPort+" 端口 !";
+ //ClientUI.ui.setMessage(msg);
+ //MLog.println(msg);
+ }
+
+ }
+ }
+ os.write(buf, 0, len);
+ if(!sended){
+ sended=true;
+ }
+ }
+ }
+
+
+
+ public int getReadedLength() {
+ return readedLength;
+ }
+
+
+
+ public void setDstPort(int dstPort) {
+ this.dstPort = dstPort;
+ }
+
+}
diff --git a/src/main/java/net/fs/client/PortMapManager.java b/src/main/java/net/fs/client/PortMapManager.java
new file mode 100755
index 0000000..77dd1e6
--- /dev/null
+++ b/src/main/java/net/fs/client/PortMapManager.java
@@ -0,0 +1,269 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import net.fs.rudp.Route;
+
+import java.io.*;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class PortMapManager {
+
+ MapClient mapClient;
+
+ ArrayList mapList=new ArrayList();
+
+ HashMap mapRuleTable=new HashMap();
+
+ String configFilePath="conf/port_map.json";
+ //String configFilePath="port_map.json";
+
+ PortMapManager(MapClient mapClient){
+ this.mapClient=mapClient;
+ //listenPort();
+ loadMapRule();
+ }
+
+ void addMapRule(MapRule mapRule) throws Exception{
+ if(getMapRule(mapRule.name)!=null){
+ throw new Exception("映射 "+mapRule.name+" 已存在,请修改名称!");
+ }
+ ServerSocket serverSocket=null;
+ try {
+ serverSocket = new ServerSocket(mapRule.getListen_port());
+ listen(serverSocket);
+ mapList.add(mapRule);
+ mapRuleTable.put(mapRule.listen_port, mapRule);
+ saveMapRule();
+ } catch (IOException e2) {
+ //e2.printStackTrace();
+ throw new Exception("端口 "+mapRule.getListen_port()+" 已经被占用!");
+ }finally{
+// if(serverSocket!=null){
+// serverSocket.close();
+// }
+ }
+ }
+
+ void removeMapRule(String name){
+ MapRule mapRule=getMapRule(name);
+ if(mapRule!=null){
+ mapList.remove(mapRule);
+ mapRuleTable.remove(mapRule.listen_port);
+ if(mapRule.serverSocket!=null){
+ try {
+ mapRule.serverSocket.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ saveMapRule();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ void updateMapRule(MapRule mapRule_origin,MapRule mapRule_new) throws Exception{
+ if(getMapRule(mapRule_new.name)!=null&&!mapRule_origin.name.equals(mapRule_new.name)){
+ throw new Exception("映射 "+mapRule_new.name+" 已存在,请修改名称!");
+ }
+ ServerSocket serverSocket=null;
+ if(mapRule_origin.listen_port!=mapRule_new.listen_port){
+ try {
+ serverSocket = new ServerSocket(mapRule_new.getListen_port());
+ listen(serverSocket);
+ mapRule_origin.using=false;
+ if(mapRule_origin.serverSocket!=null){
+ mapRule_origin.serverSocket.close();
+ }
+ mapRule_origin.serverSocket=serverSocket;
+ mapRuleTable.remove(mapRule_origin.listen_port);
+ mapRuleTable.put(mapRule_new.listen_port, mapRule_new);
+ } catch (IOException e2) {
+ //e2.printStackTrace();
+ throw new Exception("端口 "+mapRule_new.getListen_port()+" 已经被占用!");
+ }finally{
+// if(serverSocket!=null){
+// serverSocket.close();
+// }
+ }
+ }
+ mapRule_origin.name=mapRule_new.name;
+ mapRule_origin.listen_port=mapRule_new.listen_port;
+ mapRule_origin.dst_port=mapRule_new.dst_port;
+ saveMapRule();
+
+ }
+
+ void saveMapRule() throws Exception{
+ JSONObject json=new JSONObject();
+ JSONArray json_map_list=new JSONArray();
+ json.put("map_list", json_map_list);
+ if(mapList.size()==0){
+
+ }
+ for(MapRule r:mapList){
+ JSONObject json_rule=new JSONObject();
+ json_rule.put("name", r.name);
+ json_rule.put("listen_port", r.listen_port);
+ json_rule.put("dst_port", r.dst_port);
+ json_map_list.add(json_rule);
+ }
+ try {
+ saveFile(json.toJSONString().getBytes("utf-8"), configFilePath);
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new Exception("保存失败!");
+ }
+ }
+
+ void loadMapRule(){
+ String content;
+ JSONObject json=null;
+ try {
+ content = readFileUtf8(configFilePath);
+ json= JSONObject.parseObject(content);
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+ if(json!=null&&json.containsKey("map_list")){
+ JSONArray json_map_list=json.getJSONArray("map_list");
+ for(int i=0;i getMapList() {
+ return mapList;
+ }
+
+ public void setMapList(ArrayList mapList) {
+ this.mapList = mapList;
+ }
+
+ void listen(final ServerSocket serverSocket){
+ Route.es.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ while(true){
+ try {
+ final Socket socket=serverSocket.accept();
+ Route.es.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ int listenPort=serverSocket.getLocalPort();
+ MapRule mapRule=mapRuleTable.get(listenPort);
+ if(mapRule!=null){
+ Route route=null;
+ if(mapClient.isUseTcp()){
+ route=mapClient.route_tcp;
+ }else {
+ route=mapClient.route_udp;
+ }
+ PortMapProcess process=new PortMapProcess(mapClient,route, socket,mapClient.serverAddress,mapClient.serverPort,null,
+ null,mapRule.dst_port);
+ }
+ }
+
+ });
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ void saveFile(byte[] data,String path) throws Exception{
+ FileOutputStream fos=null;
+ try {
+ fos=new FileOutputStream(path);
+ fos.write(data);
+ } catch (Exception e) {
+ throw e;
+ } finally {
+ if(fos!=null){
+ fos.close();
+ }
+ }
+ }
+
+ public static String readFileUtf8(String path) throws Exception{
+ String str=null;
+ FileInputStream fis=null;
+ DataInputStream dis=null;
+ try {
+ File file=new File(path);
+
+ int length=(int) file.length();
+ byte[] data=new byte[length];
+
+ fis=new FileInputStream(file);
+ dis=new DataInputStream(fis);
+ dis.readFully(data);
+ str=new String(data,"utf-8");
+
+ } catch (Exception e) {
+ //e.printStackTrace();
+ throw e;
+ }finally{
+ if(fis!=null){
+ try {
+ fis.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if(dis!=null){
+ try {
+ dis.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return str;
+ }
+}
diff --git a/src/main/java/net/fs/client/PortMapProcess.java b/src/main/java/net/fs/client/PortMapProcess.java
new file mode 100755
index 0000000..3da3ae5
--- /dev/null
+++ b/src/main/java/net/fs/client/PortMapProcess.java
@@ -0,0 +1,176 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import com.alibaba.fastjson.JSONObject;
+import net.fs.rudp.*;
+import net.fs.utils.MLog;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.Socket;
+import java.util.Random;
+
+public class PortMapProcess implements ClientProcessorInterface{
+
+ Random ran=new Random();
+
+ UDPInputStream tis;
+
+ UDPOutputStream tos;
+
+ String serverAddress="";
+
+ int serverPort;
+
+ ConnectionUDP conn;
+
+ MapClient mapClient;
+
+ public Socket srcSocket,dstSocket;
+
+ DataInputStream srcIs=null;
+ DataOutputStream srcOs=null;
+
+ boolean closed=false;
+ boolean success=false;
+
+ public PortMapProcess(MapClient mapClient,Route route,final Socket srcSocket,String serverAddress2,int serverPort2,String password_proxy_md5,
+ String dstAddress,final int dstPort){
+ this.mapClient=mapClient;
+ this.serverAddress=serverAddress2;
+ this.serverPort=serverPort2;
+
+ this.srcSocket=srcSocket;
+
+ try {
+ srcIs = new DataInputStream(srcSocket.getInputStream());
+ srcOs=new DataOutputStream(srcSocket.getOutputStream());
+ conn = route.getConnection(serverAddress, serverPort,null);
+ tis=conn.uis;
+ tos=conn.uos;
+
+ JSONObject requestJson=new JSONObject();
+ requestJson.put("dst_address", dstAddress);
+ requestJson.put("dst_port", dstPort);
+ byte[] requestData=requestJson.toJSONString().getBytes("utf-8");
+
+ tos.write(requestData, 0, requestData.length);
+
+
+ final Pipe p1=new Pipe();
+ final Pipe p2=new Pipe();
+
+
+ byte[] responeData=tis.read2();
+
+ String hs=new String(responeData,"utf-8");
+ JSONObject responeJSon= JSONObject.parseObject(hs);
+ int code=responeJSon.getIntValue("code");
+ String message=responeJSon.getString("message");
+ String uimessage="";
+ if(code==Constant.code_success){
+
+ Route.es.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ long t=System.currentTimeMillis();
+ p2.setDstPort(dstPort);
+ try {
+ p2.pipe(tis, srcOs,1024*1024*1024,null);
+ }catch (Exception e) {
+ e.printStackTrace();
+ }finally{
+ close();
+ if(p2.getReadedLength()==0){
+ //String msg="fs服务连接成功,加速端口"+dstPort+"连接失败1";
+ String msg="Speed服务报错 "+dstPort+" 连接失败!";
+ MLog.println(msg);
+ ClientUI.ui.setMessage(msg);
+ }
+ }
+ }
+
+ });
+
+ Route.es.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ p1.pipe(srcIs, tos,200*1024,p2);
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }finally{
+ close();
+ }
+ }
+
+ });
+ success=true;
+ uimessage=("隧道连接成功");
+ ClientUI.ui.setMessage(uimessage);
+ }else {
+ close();
+ uimessage="隧道连接失败,端口"+dstPort+" 连接失败!";
+ ClientUI.ui.setMessage(uimessage);
+ MLog.println(uimessage);
+ }
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ String msg="隧道连接失败!";
+ ClientUI.ui.setMessage(msg);
+ MLog.println(msg);
+ }
+
+ }
+
+ void close(){
+ if(!closed){
+ closed=true;
+ if(srcIs!=null){
+ try {
+ srcIs.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if(srcOs!=null){
+ try {
+ srcOs.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if(tos!=null){
+ tos.closeStream_Local();
+ }
+ if(tis!=null){
+ tis.closeStream_Local();
+ }
+ if(conn!=null){
+ conn.close_local();
+ }
+ if(srcSocket!=null){
+ try {
+ srcSocket.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ mapClient.onProcessClose(this);
+
+ }
+ }
+
+ @Override
+ public void onMapClientClose() {
+ try {
+ srcSocket.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/net/fs/client/SpeedSetFrame.java b/src/main/java/net/fs/client/SpeedSetFrame.java
new file mode 100755
index 0000000..bb116e7
--- /dev/null
+++ b/src/main/java/net/fs/client/SpeedSetFrame.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import net.miginfocom.swing.MigLayout;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class SpeedSetFrame extends JDialog{
+
+ private static final long serialVersionUID = -3248779355079724594L;
+
+ ClientUI ui;
+
+ JTextField text_ds,text_us;
+
+ SpeedSetFrame(final ClientUI ui,JFrame parent){
+ super(parent, ModalityType.APPLICATION_MODAL);
+ this.ui=ui;
+ setTitle("设置带宽");
+
+ JPanel panel=(JPanel) getContentPane();
+ panel.setLayout(new MigLayout("alignx center,aligny center,insets 10 10 10 10"));
+
+
+ panel.add(new JLabel("单位Mb ( 1Mb=128KB,10Mb=1280KB )"),"height ::,wrap");
+ panel.add(new JLabel("请正确输入,该值会直接影响加速效果."),"height ::,wrap");
+
+ JPanel p5=new JPanel();
+ panel.add(p5,"wrap");
+ p5.setLayout(new MigLayout(""));
+ p5.add(new JLabel("下载带宽:"));
+ text_ds=new JTextField("");
+ p5.add(text_ds,"width 50::");
+ p5.add(new JLabel("Mb"));
+
+ p5.add(new JLabel(" "));
+
+ p5.add(new JLabel("上传带宽:"));
+ text_us=new JTextField("");
+ p5.add(text_us,"width 50::");
+ //text_us.setEditable(false);
+ p5.add(new JLabel("Mb"));
+
+ JPanel p6=new JPanel();
+ panel.add(p6,"align center,wrap");
+ p6.setLayout(new MigLayout("align center"));
+
+ JButton button_ok=createButton("确定");
+ p6.add(button_ok);
+ button_ok.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String us=text_ds.getText().trim();
+ String ds=text_us.getText().trim();
+ try {
+ int d=(int) (Float.parseFloat(us)*1024*1024/8/1.1);
+ int u=(int) (Float.parseFloat(ds)*1024*1024/8/1.1);
+ ui.setSpeed(d, u);
+ setVisible(false);
+ } catch (Exception e2) {
+ //e2.printStackTrace();
+ JOptionPane.showMessageDialog(ui.mainFrame, "输入错误!");
+ }
+
+ }
+ });
+
+ p6.add(new JLabel(" "));
+
+ JButton button_cancel=createButton("取消");
+ p6.add(button_cancel);
+ button_cancel.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ setVisible(false);
+ }
+ });
+
+ pack();
+ setLocationRelativeTo(parent);
+ if(ui.isVisible){
+ setVisible(true);
+ //MLog.println("请在client_config.json中设置带宽");
+ } else {
+ //MLog.println("请在client_config.json中设置带宽");
+ //System.exit(0);
+ }
+ }
+
+ JButton createButton(String name){
+ JButton button=new JButton(name);
+ button.setMargin(new Insets(0,5,0,5));
+ button.setFocusPainted(false);
+ return button;
+ }
+
+
+
+}
diff --git a/src/main/java/net/fs/client/TextComponentPopupMenu.java b/src/main/java/net/fs/client/TextComponentPopupMenu.java
new file mode 100755
index 0000000..d0d356c
--- /dev/null
+++ b/src/main/java/net/fs/client/TextComponentPopupMenu.java
@@ -0,0 +1,134 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.client;
+
+import javax.swing.*;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.Position;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+public class TextComponentPopupMenu extends JPopupMenu implements
+ MouseListener, ActionListener {
+
+ private static final long serialVersionUID = 117096441855319758L;
+ private static TextComponentPopupMenu sharedInstance = null;
+
+ public static void installToComponent(JComponent c) {
+ if (c instanceof JTextField && !(c instanceof JPasswordField)) {
+ c.addMouseListener(TextComponentPopupMenu.getSharedInstance());
+ }
+ }
+
+ public static void uninstallFromComponent(JComponent c) {
+ if (c instanceof JTextField && !(c instanceof JPasswordField)) {
+ c.removeMouseListener(getSharedInstance());
+ }
+ }
+
+ JMenuItem cutItem, copyItem, pasteItem, deleteItem, selectAllItem;
+
+ public TextComponentPopupMenu() {
+ add(cutItem = new JMenuItem("剪切"));
+ add(copyItem = new JMenuItem("复制"));
+ add(pasteItem = new JMenuItem("粘贴"));
+ add(deleteItem = new JMenuItem("删除"));
+ addSeparator();
+ add(selectAllItem = new JMenuItem("全选"));
+
+ cutItem.setMnemonic('T');
+ copyItem.setMnemonic('C');
+ pasteItem.setMnemonic('P');
+ deleteItem.setMnemonic('D');
+ selectAllItem.setMnemonic('A');
+
+ cutItem.addActionListener(this);
+ copyItem.addActionListener(this);
+ pasteItem.addActionListener(this);
+ deleteItem.addActionListener(this);
+ selectAllItem.addActionListener(this);
+ }
+
+ static TextComponentPopupMenu getSharedInstance() {
+ if (sharedInstance == null) {
+ sharedInstance = new TextComponentPopupMenu();
+ }
+ return sharedInstance;
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ if (e.isPopupTrigger() && e.getSource() instanceof JTextField) {
+ JTextField textfield = (JTextField) e.getSource();
+ if (Boolean.TRUE.equals(textfield
+ .getClientProperty("DisablePopupMenu"))) {
+ return;
+ }
+ textfield.requestFocusInWindow();
+ show(textfield, e.getX(), e.getY());
+ }
+ }
+
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ public void mousePressed(MouseEvent e) {
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+ public void show(Component invoker, int x, int y) {
+ JTextComponent tc = (JTextComponent) invoker;
+ String sel = tc.getSelectedText();
+
+ boolean selected = sel != null && !sel.equals("");
+ boolean enableAndEditable = tc.isEnabled() && tc.isEditable();
+
+ cutItem.setEnabled(selected && enableAndEditable);
+ copyItem.setEnabled(selected && tc.isEnabled());
+ deleteItem.setEnabled(selected && enableAndEditable);
+ pasteItem.setEnabled(enableAndEditable);
+ selectAllItem.setEnabled(tc.isEnabled());
+
+ super.show(invoker, x, y);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ JTextComponent tc = (JTextComponent) getInvoker();
+
+ String sel = tc.getSelectedText();
+
+ if (e.getSource() == cutItem) {
+ tc.cut();
+ } else if (e.getSource() == copyItem) {
+ tc.copy();
+ } else if (e.getSource() == pasteItem) {
+ tc.paste();
+ } else if (e.getSource() == selectAllItem) {
+ tc.selectAll();
+ } else if (e.getSource() == deleteItem) {
+ Document doc = tc.getDocument();
+ int start = tc.getSelectionStart();
+ int end = tc.getSelectionEnd();
+
+ try {
+ Position p0 = doc.createPosition(start);
+ Position p1 = doc.createPosition(end);
+
+ if ((p0 != null) && (p1 != null)
+ && (p0.getOffset() != p1.getOffset())) {
+ doc.remove(p0.getOffset(), p1.getOffset() - p0.getOffset());
+ }
+ } catch (BadLocationException be) {
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/fs/netty/SocksServer.java b/src/main/java/net/fs/netty/SocksServer.java
new file mode 100755
index 0000000..607126f
--- /dev/null
+++ b/src/main/java/net/fs/netty/SocksServer.java
@@ -0,0 +1,112 @@
+package net.fs.netty;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.traffic.GlobalTrafficShapingHandler;
+import io.netty.handler.traffic.TrafficCounter;
+
+import java.lang.management.ManagementFactory;
+import java.util.concurrent.Executors;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import net.fs.client.ClientUI;
+import net.fs.client.FSClient;
+import org.apache.commons.cli.*;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import net.fs.netty.config.Config;
+import net.fs.netty.config.ConfigXmlLoader;
+import net.fs.netty.config.PacLoader;
+import net.fs.netty.mbean.IoAcceptorStat;
+import net.fs.netty.proxy.SocksServerInitializer;
+
+public class SocksServer {
+
+ private static Log logger = LogFactory.getLog(SocksServer.class);
+
+ private static final String CONFIG = "conf/config.xml";
+
+ private static final String PAC = "conf/pac.xml";
+
+ private EventLoopGroup bossGroup = null;
+ private EventLoopGroup workerGroup = null;
+ private ServerBootstrap bootstrap = null;
+ private GlobalTrafficShapingHandler trafficHandler;
+
+ private static SocksServer socksServer = new SocksServer();
+
+ public static SocksServer getInstance() {
+ return socksServer;
+ }
+
+ private SocksServer() {
+
+ }
+
+ public void start() {
+ try {
+
+ FSClient.start();
+
+ Config config = ConfigXmlLoader.load(CONFIG);
+ PacLoader.load(PAC);
+
+ bossGroup = new NioEventLoopGroup(1);
+ workerGroup = new NioEventLoopGroup();
+ bootstrap = new ServerBootstrap();
+ trafficHandler = new GlobalTrafficShapingHandler(
+ Executors.newScheduledThreadPool(2), 1000);
+
+ bootstrap
+ .group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .childHandler(
+ new SocksServerInitializer(config, trafficHandler));
+
+ logger.info("Start At Port " + config.get_localPort());
+ startMBean();
+ bootstrap.bind(config.get_localPort()).sync().channel()
+ .closeFuture().sync();
+ } catch (Exception e) {
+ logger.error("start error", e);
+ } finally {
+ stop();
+ }
+ }
+
+ public void stop() {
+ if (bossGroup != null) {
+ bossGroup.shutdownGracefully();
+ }
+ if (workerGroup != null) {
+ workerGroup.shutdownGracefully();
+ }
+ logger.info("Stop Server!");
+ }
+
+ /**
+ * java MBean 进行流量统计
+ */
+ private void startMBean() {
+ MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
+ IoAcceptorStat mbean = new IoAcceptorStat();
+
+ try {
+ ObjectName acceptorName = new ObjectName(mbean.getClass()
+ .getPackage().getName()
+ + ":type=IoAcceptorStat");
+ mBeanServer.registerMBean(mbean, acceptorName);
+ } catch (Exception e) {
+ logger.error("java MBean error", e);
+ }
+ }
+
+ public TrafficCounter getTrafficCounter() {
+ return trafficHandler.trafficCounter();
+ }
+
+}
diff --git a/src/main/java/net/fs/netty/Start.java b/src/main/java/net/fs/netty/Start.java
new file mode 100755
index 0000000..259d1c0
--- /dev/null
+++ b/src/main/java/net/fs/netty/Start.java
@@ -0,0 +1,22 @@
+package net.fs.netty;
+
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * socksserver启动类
+ *
+ * @author zhangjianxin
+ *
+ */
+public class Start {
+
+ public static void main(String[] args) {
+ SocksServer.getInstance().start();
+ try {
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+
+ }
+}
diff --git a/src/main/java/net/fs/netty/config/Config.java b/src/main/java/net/fs/netty/config/Config.java
new file mode 100755
index 0000000..324453b
--- /dev/null
+++ b/src/main/java/net/fs/netty/config/Config.java
@@ -0,0 +1,80 @@
+package net.fs.netty.config;
+
+/**
+ * 配置
+ *
+ * @author zhangjianxin
+ *
+ */
+public class Config {
+
+ private String _ipAddr;
+ private int _port;
+ private String _localIpAddr;
+ private int _localPort;
+ private String _method;
+ private String _password;
+
+ public Config() {
+
+ }
+
+ public Config(String ipAddr, int port, String localIpAddr, int localPort,
+ String method, String password) {
+ _ipAddr = ipAddr;
+ _port = port;
+ _localIpAddr = localIpAddr;
+ _localPort = localPort;
+ _method = method;
+ _password = password;
+ }
+
+ public String get_ipAddr() {
+ return _ipAddr;
+ }
+
+ public void set_ipAddr(String _ipAddr) {
+ this._ipAddr = _ipAddr;
+ }
+
+ public int get_port() {
+ return _port;
+ }
+
+ public void set_port(int _port) {
+ this._port = _port;
+ }
+
+ public String get_localIpAddr() {
+ return _localIpAddr;
+ }
+
+ public void set_localIpAddr(String _localIpAddr) {
+ this._localIpAddr = _localIpAddr;
+ }
+
+ public int get_localPort() {
+ return _localPort;
+ }
+
+ public void set_localPort(int _localPort) {
+ this._localPort = _localPort;
+ }
+
+ public String get_method() {
+ return _method;
+ }
+
+ public void set_method(String _method) {
+ this._method = _method;
+ }
+
+ public String get_password() {
+ return _password;
+ }
+
+ public void set_password(String _password) {
+ this._password = _password;
+ }
+
+}
diff --git a/src/main/java/net/fs/netty/config/ConfigXmlLoader.java b/src/main/java/net/fs/netty/config/ConfigXmlLoader.java
new file mode 100755
index 0000000..9918aab
--- /dev/null
+++ b/src/main/java/net/fs/netty/config/ConfigXmlLoader.java
@@ -0,0 +1,72 @@
+package net.fs.netty.config;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * 加载Config配置xml
+ *
+ * @author zhangjianxin
+ *
+ */
+public class ConfigXmlLoader {
+
+ private static Logger log = Logger.getLogger(ConfigXmlLoader.class);
+
+ public static Config load(String file) throws Exception {
+ InputStream in = null;
+ try {
+ DocumentBuilder builder = DocumentBuilderFactory.newInstance()
+ .newDocumentBuilder();
+ in = new FileInputStream(file);
+ Document doc = builder.parse(in);
+ NodeList list = doc.getElementsByTagName("config");
+
+ Config config = new Config();
+ if (list.getLength() > 0) {
+ Node node = list.item(0);
+ NodeList childs = node.getChildNodes();
+
+ for (int j = 0; j < childs.getLength(); j++) {
+ if ("ip_addr".equals(childs.item(j).getNodeName())) {
+ config.set_ipAddr(childs.item(j).getTextContent());
+ } else if ("port".equals(childs.item(j).getNodeName())) {
+ config.set_port(Integer.parseInt(childs.item(j)
+ .getTextContent()));
+ } else if ("local_ip_addr".equals(childs.item(j)
+ .getNodeName())) {
+ config.set_localIpAddr(childs.item(j).getTextContent());
+ } else if ("local_port"
+ .equals(childs.item(j).getNodeName())) {
+ config.set_localPort(Integer.parseInt(childs.item(j)
+ .getTextContent()));
+ } else if ("method".equals(childs.item(j).getNodeName())) {
+ config.set_method(childs.item(j).getTextContent());
+ } else if ("password".equals(childs.item(j).getNodeName())) {
+ config.set_password(childs.item(j).getTextContent());
+ }
+ }
+ }
+ log.info("load config !");
+ return config;
+ } catch (Exception e) {
+ throw e;
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/fs/netty/config/PacLoader.java b/src/main/java/net/fs/netty/config/PacLoader.java
new file mode 100755
index 0000000..0c564ce
--- /dev/null
+++ b/src/main/java/net/fs/netty/config/PacLoader.java
@@ -0,0 +1,114 @@
+package net.fs.netty.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+
+/**
+ * 加载pac配置xml
+ *
+ * @author zhangjianxin
+ *
+ */
+public class PacLoader {
+
+ private static Logger log = Logger.getLogger(PacLoader.class);
+
+ private static List domainList = new ArrayList();
+ private static List tempList = new ArrayList();
+
+ /** 重加载的间隔时间 **/
+ private static int reloadTime = 5;
+
+ private static long lastModify;
+
+ public static void load(final String filePath) throws Exception {
+ File file = new File(filePath);
+ if (!file.exists()) {
+ throw new RuntimeException("file = " + filePath + " is not exist!");
+ }
+ if (file.lastModified() == lastModify) {
+ return;
+ }
+ lastModify = file.lastModified();
+
+ loadFile(filePath);
+
+ Executors.newScheduledThreadPool(1).scheduleWithFixedDelay(
+ new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ load(filePath);
+ } catch (Exception e) {
+ log.error(e);
+ }
+ }
+ }, reloadTime, reloadTime, TimeUnit.SECONDS);
+ }
+
+ private synchronized static void loadFile(String file) throws Exception {
+ tempList.clear();
+ InputStream in = null;
+ try {
+ DocumentBuilder builder = DocumentBuilderFactory.newInstance()
+ .newDocumentBuilder();
+ in = new FileInputStream(file);
+ Document doc = builder.parse(in);
+ NodeList list = doc.getElementsByTagName("domain");
+
+ if (list.getLength() > 0) {
+ for (int j = 0; j < list.getLength(); j++) {
+ tempList.add(list.item(j).getTextContent());
+ }
+ }
+ setDomainList(tempList);
+ log.info("Load PAC Success!");
+ } catch (Exception e) {
+ throw e;
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private synchronized static void setDomainList(List tempList) {
+ domainList.clear();
+ domainList.addAll(tempList);
+ }
+
+ /**
+ * 指定的host是否需要代理
+ *
+ * @param host
+ * @return
+ */
+ public synchronized static boolean isProxy(String host) {
+ for (String domain : domainList) {
+ if (host.contains(domain)) {
+ return true;
+ }
+ }
+ //return false;
+ //默认配置PAC
+ //测试阶段采用全部经过NETTY的请求都要经过代理 SS-Server:443
+ return true;
+ }
+}
diff --git a/src/main/java/net/fs/netty/encryption/CryptBase.java b/src/main/java/net/fs/netty/encryption/CryptBase.java
new file mode 100755
index 0000000..10ded06
--- /dev/null
+++ b/src/main/java/net/fs/netty/encryption/CryptBase.java
@@ -0,0 +1,143 @@
+package net.fs.netty.encryption;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.SecureRandom;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.logging.Logger;
+
+import javax.crypto.SecretKey;
+
+import org.bouncycastle.crypto.StreamBlockCipher;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+
+public abstract class CryptBase implements ICrypt {
+
+ protected final String _name;
+ protected final SecretKey _key;
+ protected final ShadowSocksKey _ssKey;
+ protected final int _ivLength;
+ protected final int _keyLength;
+ protected boolean _encryptIVSet;
+ protected boolean _decryptIVSet;
+ protected byte[] _encryptIV;
+ protected byte[] _decryptIV;
+ protected final Lock encLock = new ReentrantLock();
+ protected final Lock decLock = new ReentrantLock();
+ protected StreamBlockCipher encCipher;
+ protected StreamBlockCipher decCipher;
+ private Logger logger = Logger.getLogger(CryptBase.class.getName());
+
+ public CryptBase(String name, String password) {
+ _name = name.toLowerCase();
+ _ivLength = getIVLength();
+ _keyLength = getKeyLength();
+ _ssKey = new ShadowSocksKey(password, _keyLength);
+ _key = getKey();
+ }
+
+ protected void setIV(byte[] iv, boolean isEncrypt) {
+ if (_ivLength == 0) {
+ return;
+ }
+
+ if (isEncrypt) {
+ _encryptIV = new byte[_ivLength];
+ System.arraycopy(iv, 0, _encryptIV, 0, _ivLength);
+ try {
+ encCipher = getCipher(isEncrypt);
+ ParametersWithIV parameterIV = new ParametersWithIV(
+ new KeyParameter(_key.getEncoded()), _encryptIV);
+ encCipher.init(isEncrypt, parameterIV);
+ } catch (InvalidAlgorithmParameterException e) {
+ logger.info(e.toString());
+ }
+ } else {
+ _decryptIV = new byte[_ivLength];
+ System.arraycopy(iv, 0, _decryptIV, 0, _ivLength);
+ try {
+ decCipher = getCipher(isEncrypt);
+ ParametersWithIV parameterIV = new ParametersWithIV(
+ new KeyParameter(_key.getEncoded()), _decryptIV);
+ decCipher.init(isEncrypt, parameterIV);
+ } catch (InvalidAlgorithmParameterException e) {
+ logger.info(e.toString());
+ }
+ }
+ }
+
+ @Override
+ public void encrypt(byte[] data, ByteArrayOutputStream stream) {
+ synchronized (encLock) {
+ stream.reset();
+ if (!_encryptIVSet) {
+ _encryptIVSet = true;
+ byte[] iv = randomBytes(_ivLength);
+ setIV(iv, true);
+ try {
+ stream.write(iv);
+ } catch (IOException e) {
+ logger.info(e.toString());
+ }
+
+ }
+
+ _encrypt(data, stream);
+ }
+ }
+
+ @Override
+ public void encrypt(byte[] data, int length, ByteArrayOutputStream stream) {
+ byte[] d = new byte[length];
+ System.arraycopy(data, 0, d, 0, length);
+ encrypt(d, stream);
+ }
+
+ @Override
+ public void decrypt(byte[] data, ByteArrayOutputStream stream) {
+ byte[] temp;
+ synchronized (decLock) {
+ stream.reset();
+ if (!_decryptIVSet) {
+ _decryptIVSet = true;
+ setIV(data, false);
+ temp = new byte[data.length - _ivLength];
+ System.arraycopy(data, _ivLength, temp, 0, data.length
+ - _ivLength);
+ } else {
+ temp = data;
+ }
+
+ _decrypt(temp, stream);
+ }
+ }
+
+ @Override
+ public void decrypt(byte[] data, int length, ByteArrayOutputStream stream) {
+ byte[] d = new byte[length];
+ System.arraycopy(data, 0, d, 0, length);
+ decrypt(d, stream);
+ }
+
+ private byte[] randomBytes(int size) {
+ byte[] bytes = new byte[size];
+ new SecureRandom().nextBytes(bytes);
+ return bytes;
+ }
+
+ protected abstract StreamBlockCipher getCipher(boolean isEncrypted)
+ throws InvalidAlgorithmParameterException;
+
+ protected abstract SecretKey getKey();
+
+ protected abstract void _encrypt(byte[] data, ByteArrayOutputStream stream);
+
+ protected abstract void _decrypt(byte[] data, ByteArrayOutputStream stream);
+
+ protected abstract int getIVLength();
+
+ protected abstract int getKeyLength();
+}
diff --git a/src/main/java/net/fs/netty/encryption/CryptFactory.java b/src/main/java/net/fs/netty/encryption/CryptFactory.java
new file mode 100755
index 0000000..3060536
--- /dev/null
+++ b/src/main/java/net/fs/netty/encryption/CryptFactory.java
@@ -0,0 +1,44 @@
+package net.fs.netty.encryption;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import net.fs.netty.encryption.impl.AesCrypt;
+import net.fs.netty.encryption.impl.BlowFishCrypt;
+import net.fs.netty.encryption.impl.CamelliaCrypt;
+import net.fs.netty.encryption.impl.SeedCrypt;
+
+public class CryptFactory {
+
+ private static Log logger = LogFactory.getLog(CryptFactory.class);
+
+ private static Map crypts = new HashMap();
+
+ static {
+ crypts.putAll(AesCrypt.getCiphers());
+ crypts.putAll(CamelliaCrypt.getCiphers());
+ crypts.putAll(BlowFishCrypt.getCiphers());
+ crypts.putAll(SeedCrypt.getCiphers());
+ }
+
+ public static ICrypt get(String name, String password) {
+ String className = crypts.get(name);
+ if (className == null) {
+ return null;
+ }
+
+ try {
+ Class> clazz = Class.forName(className);
+ Constructor> constructor = clazz.getConstructor(String.class,
+ String.class);
+ return (ICrypt) constructor.newInstance(name, password);
+ } catch (Exception e) {
+ logger.error("get crypt error", e);
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/net/fs/netty/encryption/ICrypt.java b/src/main/java/net/fs/netty/encryption/ICrypt.java
new file mode 100755
index 0000000..885c549
--- /dev/null
+++ b/src/main/java/net/fs/netty/encryption/ICrypt.java
@@ -0,0 +1,21 @@
+package net.fs.netty.encryption;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * crypt 加密
+ *
+ * @author zhangjianxin
+ *
+ */
+public interface ICrypt {
+
+ void encrypt(byte[] data, ByteArrayOutputStream stream);
+
+ void encrypt(byte[] data, int length, ByteArrayOutputStream stream);
+
+ void decrypt(byte[] data, ByteArrayOutputStream stream);
+
+ void decrypt(byte[] data, int length, ByteArrayOutputStream stream);
+
+}
diff --git a/src/main/java/net/fs/netty/encryption/ShadowSocksKey.java b/src/main/java/net/fs/netty/encryption/ShadowSocksKey.java
new file mode 100755
index 0000000..8fd8fa2
--- /dev/null
+++ b/src/main/java/net/fs/netty/encryption/ShadowSocksKey.java
@@ -0,0 +1,86 @@
+package net.fs.netty.encryption;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+
+import javax.crypto.SecretKey;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Shadowsocks key generator
+ */
+public class ShadowSocksKey implements SecretKey {
+
+ private static final long serialVersionUID = 1L;
+ private static Log logger = LogFactory.getLog(ShadowSocksKey.class);
+ private final static int KEY_LENGTH = 32;
+ private byte[] _key;
+ private int _length;
+
+ public ShadowSocksKey(String password) {
+ _length = KEY_LENGTH;
+ _key = init(password);
+ }
+
+ public ShadowSocksKey(String password, int length) {
+ _length = length;
+ _key = init(password);
+ }
+
+ private byte[] init(String password) {
+ MessageDigest md = null;
+ byte[] keys = new byte[KEY_LENGTH];
+ byte[] temp = null;
+ byte[] hash = null;
+ byte[] passwordBytes = null;
+ int i = 0;
+
+ try {
+ md = MessageDigest.getInstance("MD5");
+ passwordBytes = password.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ logger.error("ShadowSocksKey: Unsupported string encoding", e);
+ } catch (Exception e) {
+ logger.error(e);
+ return null;
+ }
+
+ while (i < keys.length) {
+ if (i == 0) {
+ hash = md.digest(passwordBytes);
+ temp = new byte[passwordBytes.length + hash.length];
+ } else {
+ System.arraycopy(hash, 0, temp, 0, hash.length);
+ System.arraycopy(passwordBytes, 0, temp, hash.length,
+ passwordBytes.length);
+ hash = md.digest(temp);
+ }
+ System.arraycopy(hash, 0, keys, i, hash.length);
+ i += hash.length;
+ }
+
+ if (_length != KEY_LENGTH) {
+ byte[] keysl = new byte[_length];
+ System.arraycopy(keys, 0, keysl, 0, _length);
+ return keysl;
+ }
+ return keys;
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return "shadowsocks";
+ }
+
+ @Override
+ public String getFormat() {
+ return "RAW";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return _key;
+ }
+}
diff --git a/src/main/java/net/fs/netty/encryption/impl/AesCrypt.java b/src/main/java/net/fs/netty/encryption/impl/AesCrypt.java
new file mode 100755
index 0000000..5bdb696
--- /dev/null
+++ b/src/main/java/net/fs/netty/encryption/impl/AesCrypt.java
@@ -0,0 +1,118 @@
+package net.fs.netty.encryption.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.crypto.StreamBlockCipher;
+import org.bouncycastle.crypto.engines.AESFastEngine;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import org.bouncycastle.crypto.modes.OFBBlockCipher;
+import net.fs.netty.encryption.CryptBase;
+
+/**
+ * AES 实现类
+ *
+ * @author zhangjianxin
+ *
+ */
+public class AesCrypt extends CryptBase {
+
+ public final static String CIPHER_AES_128_CFB = "aes-128-cfb";
+ public final static String CIPHER_AES_192_CFB = "aes-192-cfb";
+ public final static String CIPHER_AES_256_CFB = "aes-256-cfb";
+ public final static String CIPHER_AES_128_OFB = "aes-128-ofb";
+ public final static String CIPHER_AES_192_OFB = "aes-192-ofb";
+ public final static String CIPHER_AES_256_OFB = "aes-256-ofb";
+
+ public static Map getCiphers() {
+ Map ciphers = new HashMap<>();
+ ciphers.put(CIPHER_AES_128_CFB, AesCrypt.class.getName());
+ ciphers.put(CIPHER_AES_192_CFB, AesCrypt.class.getName());
+ ciphers.put(CIPHER_AES_256_CFB, AesCrypt.class.getName());
+ ciphers.put(CIPHER_AES_128_OFB, AesCrypt.class.getName());
+ ciphers.put(CIPHER_AES_192_OFB, AesCrypt.class.getName());
+ ciphers.put(CIPHER_AES_256_OFB, AesCrypt.class.getName());
+
+ return ciphers;
+ }
+
+ public AesCrypt(String name, String password) {
+ super(name, password);
+ }
+
+ @Override
+ public int getKeyLength() {
+ if (_name.equals(CIPHER_AES_128_CFB)
+ || _name.equals(CIPHER_AES_128_OFB)) {
+ return 16;
+ } else if (_name.equals(CIPHER_AES_192_CFB)
+ || _name.equals(CIPHER_AES_192_OFB)) {
+ return 24;
+ } else if (_name.equals(CIPHER_AES_256_CFB)
+ || _name.equals(CIPHER_AES_256_OFB)) {
+ return 32;
+ }
+
+ return 0;
+ }
+
+ @Override
+ protected StreamBlockCipher getCipher(boolean isEncrypted)
+ throws InvalidAlgorithmParameterException {
+ AESFastEngine engine = new AESFastEngine();
+ StreamBlockCipher cipher;
+
+ if (_name.equals(CIPHER_AES_128_CFB)) {
+ cipher = new CFBBlockCipher(engine, getIVLength() * 8);
+ } else if (_name.equals(CIPHER_AES_192_CFB)) {
+ cipher = new CFBBlockCipher(engine, getIVLength() * 8);
+ } else if (_name.equals(CIPHER_AES_256_CFB)) {
+ cipher = new CFBBlockCipher(engine, getIVLength() * 8);
+ } else if (_name.equals(CIPHER_AES_128_OFB)) {
+ cipher = new OFBBlockCipher(engine, getIVLength() * 8);
+ } else if (_name.equals(CIPHER_AES_192_OFB)) {
+ cipher = new OFBBlockCipher(engine, getIVLength() * 8);
+ } else if (_name.equals(CIPHER_AES_256_OFB)) {
+ cipher = new OFBBlockCipher(engine, getIVLength() * 8);
+ } else {
+ throw new InvalidAlgorithmParameterException(_name);
+ }
+
+ return cipher;
+ }
+
+ @Override
+ public int getIVLength() {
+ return 16;
+ }
+
+ @Override
+ protected SecretKey getKey() {
+ return new SecretKeySpec(_ssKey.getEncoded(), "AES");
+ }
+
+ @Override
+ protected void _encrypt(byte[] data, ByteArrayOutputStream stream) {
+ int noBytesProcessed;
+ byte[] buffer = new byte[data.length];
+
+ noBytesProcessed = encCipher.processBytes(data, 0, data.length, buffer,
+ 0);
+ stream.write(buffer, 0, noBytesProcessed);
+ }
+
+ @Override
+ protected void _decrypt(byte[] data, ByteArrayOutputStream stream) {
+ int noBytesProcessed;
+ byte[] buffer = new byte[data.length];
+
+ noBytesProcessed = decCipher.processBytes(data, 0, data.length, buffer,
+ 0);
+ stream.write(buffer, 0, noBytesProcessed);
+ }
+}
diff --git a/src/main/java/net/fs/netty/encryption/impl/BlowFishCrypt.java b/src/main/java/net/fs/netty/encryption/impl/BlowFishCrypt.java
new file mode 100755
index 0000000..a66cc78
--- /dev/null
+++ b/src/main/java/net/fs/netty/encryption/impl/BlowFishCrypt.java
@@ -0,0 +1,80 @@
+package net.fs.netty.encryption.impl;
+
+import org.bouncycastle.crypto.StreamBlockCipher;
+import org.bouncycastle.crypto.engines.BlowfishEngine;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import net.fs.netty.encryption.CryptBase;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Blow fish cipher implementation
+ */
+public class BlowFishCrypt extends CryptBase {
+
+ public final static String CIPHER_BLOWFISH_CFB = "bf-cfb";
+
+ public static Map getCiphers() {
+ Map ciphers = new HashMap<>();
+ ciphers.put(CIPHER_BLOWFISH_CFB, BlowFishCrypt.class.getName());
+
+ return ciphers;
+ }
+
+ public BlowFishCrypt(String name, String password) {
+ super(name, password);
+ }
+
+ @Override
+ public int getKeyLength() {
+ return 16;
+ }
+
+ @Override
+ protected StreamBlockCipher getCipher(boolean isEncrypted) throws InvalidAlgorithmParameterException {
+ BlowfishEngine engine = new BlowfishEngine();
+ StreamBlockCipher cipher;
+
+ if (_name.equals(CIPHER_BLOWFISH_CFB)) {
+ cipher = new CFBBlockCipher(engine, getIVLength() * 8);
+ }
+ else {
+ throw new InvalidAlgorithmParameterException(_name);
+ }
+
+ return cipher;
+ }
+
+ @Override
+ public int getIVLength() {
+ return 8;
+ }
+
+ @Override
+ protected SecretKey getKey() {
+ return new SecretKeySpec(_ssKey.getEncoded(), "AES");
+ }
+
+ @Override
+ protected void _encrypt(byte[] data, ByteArrayOutputStream stream) {
+ int noBytesProcessed;
+ byte[] buffer = new byte[data.length];
+
+ noBytesProcessed = encCipher.processBytes(data, 0, data.length, buffer, 0);
+ stream.write(buffer, 0, noBytesProcessed);
+ }
+
+ @Override
+ protected void _decrypt(byte[] data, ByteArrayOutputStream stream) {
+ int noBytesProcessed;
+ byte[] buffer = new byte[data.length];
+
+ noBytesProcessed = decCipher.processBytes(data, 0, data.length, buffer, 0);
+ stream.write(buffer, 0, noBytesProcessed);
+ }
+}
diff --git a/src/main/java/net/fs/netty/encryption/impl/CamelliaCrypt.java b/src/main/java/net/fs/netty/encryption/impl/CamelliaCrypt.java
new file mode 100755
index 0000000..5202da7
--- /dev/null
+++ b/src/main/java/net/fs/netty/encryption/impl/CamelliaCrypt.java
@@ -0,0 +1,100 @@
+package net.fs.netty.encryption.impl;
+
+import org.bouncycastle.crypto.StreamBlockCipher;
+import org.bouncycastle.crypto.engines.CamelliaEngine;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import net.fs.netty.encryption.CryptBase;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Camellia cipher implementation
+ */
+public class CamelliaCrypt extends CryptBase {
+
+ public final static String CIPHER_CAMELLIA_128_CFB = "camellia-128-cfb";
+ public final static String CIPHER_CAMELLIA_192_CFB = "camellia-192-cfb";
+ public final static String CIPHER_CAMELLIA_256_CFB = "camellia-256-cfb";
+
+ public static Map getCiphers() {
+ Map ciphers = new HashMap<>();
+ ciphers.put(CIPHER_CAMELLIA_128_CFB, CamelliaCrypt.class.getName());
+ ciphers.put(CIPHER_CAMELLIA_192_CFB, CamelliaCrypt.class.getName());
+ ciphers.put(CIPHER_CAMELLIA_256_CFB, CamelliaCrypt.class.getName());
+
+ return ciphers;
+ }
+
+ public CamelliaCrypt(String name, String password) {
+ super(name, password);
+ }
+
+ @Override
+ public int getKeyLength() {
+ if(_name.equals(CIPHER_CAMELLIA_128_CFB)) {
+ return 16;
+ }
+ else if (_name.equals(CIPHER_CAMELLIA_192_CFB)) {
+ return 24;
+ }
+ else if (_name.equals(CIPHER_CAMELLIA_256_CFB)) {
+ return 32;
+ }
+
+ return 0;
+ }
+
+ @Override
+ protected StreamBlockCipher getCipher(boolean isEncrypted) throws InvalidAlgorithmParameterException {
+ CamelliaEngine engine = new CamelliaEngine();
+ StreamBlockCipher cipher;
+
+ if (_name.equals(CIPHER_CAMELLIA_128_CFB)) {
+ cipher = new CFBBlockCipher(engine, getIVLength() * 8);
+ }
+ else if (_name.equals(CIPHER_CAMELLIA_192_CFB)) {
+ cipher = new CFBBlockCipher(engine, getIVLength() * 8);
+ }
+ else if (_name.equals(CIPHER_CAMELLIA_256_CFB)) {
+ cipher = new CFBBlockCipher(engine, getIVLength() * 8);
+ }
+ else {
+ throw new InvalidAlgorithmParameterException(_name);
+ }
+
+ return cipher;
+ }
+
+ @Override
+ public int getIVLength() {
+ return 16;
+ }
+
+ @Override
+ protected SecretKey getKey() {
+ return new SecretKeySpec(_ssKey.getEncoded(), "AES");
+ }
+
+ @Override
+ protected void _encrypt(byte[] data, ByteArrayOutputStream stream) {
+ int noBytesProcessed;
+ byte[] buffer = new byte[data.length];
+
+ noBytesProcessed = encCipher.processBytes(data, 0, data.length, buffer, 0);
+ stream.write(buffer, 0, noBytesProcessed);
+ }
+
+ @Override
+ protected void _decrypt(byte[] data, ByteArrayOutputStream stream) {
+ int noBytesProcessed;
+ byte[] buffer = new byte[data.length];
+
+ noBytesProcessed = decCipher.processBytes(data, 0, data.length, buffer, 0);
+ stream.write(buffer, 0, noBytesProcessed);
+ }
+}
diff --git a/src/main/java/net/fs/netty/encryption/impl/SeedCrypt.java b/src/main/java/net/fs/netty/encryption/impl/SeedCrypt.java
new file mode 100755
index 0000000..f646451
--- /dev/null
+++ b/src/main/java/net/fs/netty/encryption/impl/SeedCrypt.java
@@ -0,0 +1,80 @@
+package net.fs.netty.encryption.impl;
+
+import org.bouncycastle.crypto.StreamBlockCipher;
+import org.bouncycastle.crypto.engines.SEEDEngine;
+import org.bouncycastle.crypto.modes.CFBBlockCipher;
+import net.fs.netty.encryption.CryptBase;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Seed cipher implementation
+ */
+public class SeedCrypt extends CryptBase {
+
+ public final static String CIPHER_SEED_CFB = "seed-cfb";
+
+ public static Map getCiphers() {
+ Map ciphers = new HashMap<>();
+ ciphers.put(CIPHER_SEED_CFB, SeedCrypt.class.getName());
+
+ return ciphers;
+ }
+
+ public SeedCrypt(String name, String password) {
+ super(name, password);
+ }
+
+ @Override
+ public int getKeyLength() {
+ return 16;
+ }
+
+ @Override
+ protected StreamBlockCipher getCipher(boolean isEncrypted) throws InvalidAlgorithmParameterException {
+ SEEDEngine engine = new SEEDEngine();
+ StreamBlockCipher cipher;
+
+ if (_name.equals(CIPHER_SEED_CFB)) {
+ cipher = new CFBBlockCipher(engine, getIVLength() * 8);
+ }
+ else {
+ throw new InvalidAlgorithmParameterException(_name);
+ }
+
+ return cipher;
+ }
+
+ @Override
+ public int getIVLength() {
+ return 16;
+ }
+
+ @Override
+ protected SecretKey getKey() {
+ return new SecretKeySpec(_ssKey.getEncoded(), "AES");
+ }
+
+ @Override
+ protected void _encrypt(byte[] data, ByteArrayOutputStream stream) {
+ int noBytesProcessed;
+ byte[] buffer = new byte[data.length];
+
+ noBytesProcessed = encCipher.processBytes(data, 0, data.length, buffer, 0);
+ stream.write(buffer, 0, noBytesProcessed);
+ }
+
+ @Override
+ protected void _decrypt(byte[] data, ByteArrayOutputStream stream) {
+ int noBytesProcessed;
+ byte[] buffer = new byte[data.length];
+
+ noBytesProcessed = decCipher.processBytes(data, 0, data.length, buffer, 0);
+ stream.write(buffer, 0, noBytesProcessed);
+ }
+}
diff --git a/src/main/java/net/fs/netty/mbean/IoAcceptorStat.java b/src/main/java/net/fs/netty/mbean/IoAcceptorStat.java
new file mode 100755
index 0000000..c753b66
--- /dev/null
+++ b/src/main/java/net/fs/netty/mbean/IoAcceptorStat.java
@@ -0,0 +1,19 @@
+package net.fs.netty.mbean;
+
+import net.fs.netty.SocksServer;
+
+public class IoAcceptorStat implements IoAcceptorStatMBean {
+
+ @Override
+ public long getWrittenBytesThroughput() {
+ return SocksServer.getInstance().getTrafficCounter()
+ .lastWriteThroughput();
+ }
+
+ @Override
+ public long getReadBytesThroughput() {
+ return SocksServer.getInstance().getTrafficCounter()
+ .lastReadThroughput();
+ }
+
+}
diff --git a/src/main/java/net/fs/netty/mbean/IoAcceptorStatMBean.java b/src/main/java/net/fs/netty/mbean/IoAcceptorStatMBean.java
new file mode 100755
index 0000000..4907337
--- /dev/null
+++ b/src/main/java/net/fs/netty/mbean/IoAcceptorStatMBean.java
@@ -0,0 +1,18 @@
+package net.fs.netty.mbean;
+
+public interface IoAcceptorStatMBean {
+
+ /**
+ * netty写流量 bytes/s 对浏览器来说其实就是下载速度
+ *
+ * @return
+ */
+ public long getWrittenBytesThroughput();
+
+ /**
+ * netty 读流量 bytes/s 对浏览器来说其实就是上传速度
+ *
+ * @return
+ */
+ public long getReadBytesThroughput();
+}
diff --git a/src/main/java/net/fs/netty/proxy/DirectClientHandler.java b/src/main/java/net/fs/netty/proxy/DirectClientHandler.java
new file mode 100755
index 0000000..a38daf4
--- /dev/null
+++ b/src/main/java/net/fs/netty/proxy/DirectClientHandler.java
@@ -0,0 +1,26 @@
+package net.fs.netty.proxy;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.concurrent.Promise;
+
+public final class DirectClientHandler extends ChannelInboundHandlerAdapter {
+
+ private final Promise promise;
+
+ public DirectClientHandler(Promise promise) {
+ this.promise = promise;
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) {
+ ctx.pipeline().remove(this);
+ promise.setSuccess(ctx.channel());
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) {
+ promise.setFailure(throwable);
+ }
+}
diff --git a/src/main/java/net/fs/netty/proxy/InRelayHandler.java b/src/main/java/net/fs/netty/proxy/InRelayHandler.java
new file mode 100755
index 0000000..97740ac
--- /dev/null
+++ b/src/main/java/net/fs/netty/proxy/InRelayHandler.java
@@ -0,0 +1,69 @@
+package net.fs.netty.proxy;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.ReferenceCountUtil;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * 接受remoteserver的数据,发送给客户端
+ *
+ * @author zhangjianxin
+ *
+ */
+public final class InRelayHandler extends ChannelInboundHandlerAdapter {
+
+ private static Log logger = LogFactory.getLog(InRelayHandler.class);
+
+ private final Channel relayChannel;
+ private SocksServerConnectHandler connectHandler;
+
+ public InRelayHandler(Channel relayChannel,
+ SocksServerConnectHandler connectHandler) {
+ this.relayChannel = relayChannel;
+ this.connectHandler = connectHandler;
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) {
+ ctx.writeAndFlush(Unpooled.EMPTY_BUFFER);
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) {
+ try {
+ if (relayChannel.isActive()) {
+ logger.debug("get remote message" + relayChannel);
+ ByteBuf bytebuff = (ByteBuf) msg;
+ if (!bytebuff.hasArray()) {
+ int len = bytebuff.readableBytes();
+ byte[] arr = new byte[len];
+ bytebuff.getBytes(0, arr);
+ connectHandler.sendLocal(arr, arr.length, relayChannel);
+ }
+ }
+ } catch (Exception e) {
+ logger.error("receive remoteServer data error", e);
+ } finally {
+ ReferenceCountUtil.release(msg);
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) {
+ if (relayChannel.isActive()) {
+ SocksServerUtils.closeOnFlush(relayChannel);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+ cause.printStackTrace();
+ ctx.close();
+ }
+}
diff --git a/src/main/java/net/fs/netty/proxy/OutRelayHandler.java b/src/main/java/net/fs/netty/proxy/OutRelayHandler.java
new file mode 100755
index 0000000..f747189
--- /dev/null
+++ b/src/main/java/net/fs/netty/proxy/OutRelayHandler.java
@@ -0,0 +1,69 @@
+package net.fs.netty.proxy;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.util.ReferenceCountUtil;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * localserver接受到数据发送数据给remoteserver
+ *
+ * @author zhangjianxin
+ *
+ */
+public final class OutRelayHandler extends ChannelInboundHandlerAdapter {
+
+ private static Log logger = LogFactory.getLog(OutRelayHandler.class);
+
+ private final Channel relayChannel;
+ private SocksServerConnectHandler connectHandler;
+
+ public OutRelayHandler(Channel relayChannel,
+ SocksServerConnectHandler connectHandler) {
+ this.relayChannel = relayChannel;
+ this.connectHandler = connectHandler;
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) {
+ ctx.writeAndFlush(Unpooled.EMPTY_BUFFER);
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) {
+ try {
+ if (relayChannel.isActive()) {
+ ByteBuf bytebuff = (ByteBuf) msg;
+ if (!bytebuff.hasArray()) {
+ int len = bytebuff.readableBytes();
+ byte[] arr = new byte[len];
+ bytebuff.getBytes(0, arr);
+ connectHandler.sendRemote(arr, arr.length, relayChannel);
+ }
+ }
+ } catch (Exception e) {
+ logger.error("send data to remoteServer error",e);
+ } finally {
+ ReferenceCountUtil.release(msg);
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) {
+ if (relayChannel.isActive()) {
+ SocksServerUtils.closeOnFlush(relayChannel);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+ cause.printStackTrace();
+ ctx.close();
+ }
+
+}
diff --git a/src/main/java/net/fs/netty/proxy/SocksServerConnectHandler.java b/src/main/java/net/fs/netty/proxy/SocksServerConnectHandler.java
new file mode 100755
index 0000000..10b1fbf
--- /dev/null
+++ b/src/main/java/net/fs/netty/proxy/SocksServerConnectHandler.java
@@ -0,0 +1,229 @@
+package net.fs.netty.proxy;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.socks.SocksCmdRequest;
+import io.netty.handler.codec.socks.SocksCmdResponse;
+import io.netty.handler.codec.socks.SocksCmdStatus;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+import io.netty.util.concurrent.Promise;
+
+import java.io.ByteArrayOutputStream;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import net.fs.netty.config.Config;
+import net.fs.netty.config.PacLoader;
+import net.fs.netty.encryption.CryptFactory;
+import net.fs.netty.encryption.ICrypt;
+
+@ChannelHandler.Sharable
+public final class SocksServerConnectHandler extends
+ SimpleChannelInboundHandler {
+
+ private static Log logger = LogFactory
+ .getLog(SocksServerConnectHandler.class);
+
+ public static final int BUFFER_SIZE = 16384;
+
+ private final Bootstrap b = new Bootstrap();
+ private ICrypt _crypt;
+ private ByteArrayOutputStream _remoteOutStream;
+ private ByteArrayOutputStream _localOutStream;
+ private Config config;
+ private boolean isProxy = true;
+
+ public SocksServerConnectHandler(Config config) {
+ this.config = config;
+ this._crypt = CryptFactory.get(config.get_method(), config.get_password());
+ this._remoteOutStream = new ByteArrayOutputStream(BUFFER_SIZE);
+ this._localOutStream = new ByteArrayOutputStream(BUFFER_SIZE);
+ }
+
+ @Override
+ public void channelRead0(final ChannelHandlerContext ctx,
+ final SocksCmdRequest request) throws Exception {
+ Promise promise = ctx.executor().newPromise();
+ promise.addListener(new GenericFutureListener>() {
+ @Override
+ public void operationComplete(final Future future)
+ throws Exception {
+ final Channel outboundChannel = future.getNow();
+ if (future.isSuccess()) {
+ final InRelayHandler inRelay = new InRelayHandler(ctx
+ .channel(), SocksServerConnectHandler.this);
+ final OutRelayHandler outRelay = new OutRelayHandler(
+ outboundChannel, SocksServerConnectHandler.this);
+
+ ctx.channel().writeAndFlush(getSuccessResponse(request))
+ .addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(
+ ChannelFuture channelFuture) {
+ try {
+ if(isProxy){
+ sendConnectRemoteMessage(request, outboundChannel);
+ }
+
+ ctx.pipeline().remove(SocksServerConnectHandler.this);
+ outboundChannel.pipeline().addLast(inRelay);
+ ctx.pipeline().addLast(outRelay);
+ } catch (Exception e) {
+ logger.error(e);
+ }
+ }
+ });
+ } else {
+ ctx.channel().writeAndFlush(
+ new SocksCmdResponse(SocksCmdStatus.FAILURE,
+ request.addressType()));
+ SocksServerUtils.closeOnFlush(ctx.channel());
+ }
+ }
+ });
+
+ final Channel inboundChannel = ctx.channel();
+ b.group(inboundChannel.eventLoop()).channel(NioSocketChannel.class)
+ .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
+ .option(ChannelOption.SO_KEEPALIVE, true)
+ .handler(new DirectClientHandler(promise));
+
+ setProxy(request.host());
+
+ b.connect(getIpAddr(request), getPort(request)).addListener(
+ new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future)
+ throws Exception {
+ if (!future.isSuccess()) {
+ ctx.channel().writeAndFlush(getFailureResponse(request));
+ SocksServerUtils.closeOnFlush(ctx.channel());
+ }
+ }
+ });
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
+ throws Exception {
+ SocksServerUtils.closeOnFlush(ctx.channel());
+ }
+
+ public void setProxy(String host) {
+ isProxy = PacLoader.isProxy(host);
+ logger.info("HOST = " + host + ",isProxy = " + isProxy);
+ }
+
+ /**
+ * 获取远程ip地址
+ * @param request
+ * @return
+ */
+ private String getIpAddr(SocksCmdRequest request) {
+ if(isProxy) {
+ return config.get_ipAddr();
+ } else {
+ return request.host();
+ }
+ }
+
+ /**
+ * 获取远程端口
+ * @param request
+ * @return
+ */
+ private int getPort(SocksCmdRequest request) {
+ if(isProxy) {
+ return config.get_port();
+ } else {
+ return request.port();
+ }
+ }
+
+ private SocksCmdResponse getSuccessResponse(SocksCmdRequest request) {
+ return new SocksCmdResponse(SocksCmdStatus.SUCCESS,
+ request.addressType());
+ }
+
+ private SocksCmdResponse getFailureResponse(SocksCmdRequest request) {
+ return new SocksCmdResponse(SocksCmdStatus.FAILURE,
+ request.addressType());
+ }
+
+ /**
+ * localserver和remoteserver进行connect发送的数据
+ *
+ * @param request
+ * @param outboundChannel
+ */
+ private void sendConnectRemoteMessage(SocksCmdRequest request,
+ Channel outboundChannel) {
+ ByteBuf buff = Unpooled.buffer();
+ request.encodeAsByteBuf(buff);
+ if (buff.hasArray()) {
+ int len = buff.readableBytes();
+ byte[] arr = new byte[len];
+ buff.getBytes(0, arr);
+ byte[] data = remoteByte(arr);
+ sendRemote(data, data.length, outboundChannel);
+ }
+ }
+
+ /**
+ * localserver和remoteserver进行connect发送的数据
+ *
+ * +-----+-----+-------+------+----------+----------+
+ * | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+ * +-----+-----+-------+------+----------+----------+
+ * | 1 | 1 | X'00' | 1 | Variable | 2 |
+ * +-----+-----+-------+------+----------+----------+
+ *
+ * 需要跳过前面3个字节
+ *
+ * @param data
+ * @return
+ */
+ private byte[] remoteByte(byte[] data) {
+ int dataLength = data.length;
+ dataLength -= 3;
+ byte[] temp = new byte[dataLength];
+ System.arraycopy(data, 3, temp, 0, dataLength);
+ return temp;
+ }
+
+ /**
+ * 给remoteserver发送数据--需要进行加密处理
+ *
+ * @param data
+ * @param length
+ * @param channel
+ */
+ public void sendRemote(byte[] data, int length, Channel channel) {
+ if(isProxy) {
+ _crypt.encrypt(data, length, _remoteOutStream);
+ data = _remoteOutStream.toByteArray();
+ }
+ channel.writeAndFlush(Unpooled.wrappedBuffer(data));
+ logger.debug("sendRemote message:isProxy = " + isProxy +",length = " + length+",channel = " + channel);
+ }
+
+ public void sendLocal(byte[] data, int length, Channel outboundChannel) {
+ if(isProxy) {
+ _crypt.decrypt(data, length, _localOutStream);
+ data = _localOutStream.toByteArray();
+ }
+ outboundChannel.writeAndFlush(Unpooled.wrappedBuffer(data));
+ logger.debug("sendLocal message:isProxy = " + isProxy +",length = " + length + ",channel = " + outboundChannel);
+ }
+
+}
diff --git a/src/main/java/net/fs/netty/proxy/SocksServerHandler.java b/src/main/java/net/fs/netty/proxy/SocksServerHandler.java
new file mode 100755
index 0000000..4a429d5
--- /dev/null
+++ b/src/main/java/net/fs/netty/proxy/SocksServerHandler.java
@@ -0,0 +1,72 @@
+package net.fs.netty.proxy;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import net.fs.netty.config.Config;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.codec.socks.SocksAuthResponse;
+import io.netty.handler.codec.socks.SocksAuthScheme;
+import io.netty.handler.codec.socks.SocksAuthStatus;
+import io.netty.handler.codec.socks.SocksCmdRequest;
+import io.netty.handler.codec.socks.SocksCmdRequestDecoder;
+import io.netty.handler.codec.socks.SocksCmdType;
+import io.netty.handler.codec.socks.SocksInitResponse;
+import io.netty.handler.codec.socks.SocksRequest;
+
+@ChannelHandler.Sharable
+public final class SocksServerHandler extends
+ SimpleChannelInboundHandler {
+
+ private static Log logger = LogFactory.getLog(SocksServerHandler.class);
+
+ private Config config;
+
+ public SocksServerHandler(Config config) {
+ this.config = config;
+ }
+
+ @Override
+ public void channelRead0(ChannelHandlerContext ctx,
+ SocksRequest socksRequest) throws Exception {
+ switch (socksRequest.requestType()) {
+ case INIT: {
+ logger.info("localserver init");
+ ctx.pipeline().addFirst(new SocksCmdRequestDecoder());
+ ctx.write(new SocksInitResponse(SocksAuthScheme.NO_AUTH));
+ break;
+ }
+ case AUTH:
+ ctx.pipeline().addFirst(new SocksCmdRequestDecoder());
+ ctx.write(new SocksAuthResponse(SocksAuthStatus.SUCCESS));
+ break;
+ case CMD:
+ SocksCmdRequest req = (SocksCmdRequest) socksRequest;
+ if (req.cmdType() == SocksCmdType.CONNECT) {
+ logger.info("localserver connect");
+ ctx.pipeline().addLast(new SocksServerConnectHandler(config));
+ ctx.pipeline().remove(this);
+ ctx.fireChannelRead(socksRequest);
+ } else {
+ ctx.close();
+ }
+ break;
+ case UNKNOWN:
+ ctx.close();
+ break;
+ }
+ }
+
+ @Override
+ public void channelReadComplete(ChannelHandlerContext ctx) {
+ ctx.flush();
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) {
+ throwable.printStackTrace();
+ SocksServerUtils.closeOnFlush(ctx.channel());
+ }
+}
diff --git a/src/main/java/net/fs/netty/proxy/SocksServerInitializer.java b/src/main/java/net/fs/netty/proxy/SocksServerInitializer.java
new file mode 100755
index 0000000..d065999
--- /dev/null
+++ b/src/main/java/net/fs/netty/proxy/SocksServerInitializer.java
@@ -0,0 +1,34 @@
+package net.fs.netty.proxy;
+
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.codec.socks.SocksInitRequestDecoder;
+import io.netty.handler.codec.socks.SocksMessageEncoder;
+import io.netty.handler.traffic.GlobalTrafficShapingHandler;
+
+import net.fs.netty.config.Config;
+
+public final class SocksServerInitializer extends
+ ChannelInitializer {
+
+ private SocksMessageEncoder socksMessageEncoder;
+ private SocksServerHandler socksServerHandler;
+ private GlobalTrafficShapingHandler trafficHandler;
+
+ public SocksServerInitializer(Config config,
+ GlobalTrafficShapingHandler trafficHandler) {
+ this.trafficHandler = trafficHandler;
+ socksMessageEncoder = new SocksMessageEncoder();
+ socksServerHandler = new SocksServerHandler(config);
+ }
+
+ @Override
+ public void initChannel(SocketChannel socketChannel) throws Exception {
+ ChannelPipeline p = socketChannel.pipeline();
+ p.addLast(new SocksInitRequestDecoder());
+ p.addLast(socksMessageEncoder);
+ p.addLast(socksServerHandler);
+ p.addLast(trafficHandler);
+ }
+}
diff --git a/src/main/java/net/fs/netty/proxy/SocksServerUtils.java b/src/main/java/net/fs/netty/proxy/SocksServerUtils.java
new file mode 100755
index 0000000..aa4915e
--- /dev/null
+++ b/src/main/java/net/fs/netty/proxy/SocksServerUtils.java
@@ -0,0 +1,21 @@
+package net.fs.netty.proxy;
+
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFutureListener;
+
+public final class SocksServerUtils {
+
+ /**
+ * Closes the specified channel after all queued write requests are flushed.
+ */
+ public static void closeOnFlush(Channel ch) {
+ if (ch.isActive()) {
+ ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(
+ ChannelFutureListener.CLOSE);
+ }
+ }
+
+ private SocksServerUtils() {
+ }
+}
diff --git a/src/main/java/net/fs/rudp/AckListManage.java b/src/main/java/net/fs/rudp/AckListManage.java
new file mode 100755
index 0000000..519751e
--- /dev/null
+++ b/src/main/java/net/fs/rudp/AckListManage.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+public class AckListManage implements Runnable{
+ Thread mainThread;
+ HashMap taskTable;
+ public AckListManage(){
+ taskTable=new HashMap();
+ mainThread=new Thread(this);
+ mainThread.start();
+ }
+
+ synchronized void addAck(ConnectionUDP conn,int sequence){
+ if(!taskTable.containsKey(conn.connectId)){
+ AckListTask at=new AckListTask(conn);
+ taskTable.put(conn.connectId, at);
+ }
+ AckListTask at=taskTable.get(conn.connectId);
+ at.addAck(sequence);
+ }
+
+ synchronized void addLastRead(ConnectionUDP conn){
+ if(!taskTable.containsKey(conn.connectId)){
+ AckListTask at=new AckListTask(conn);
+ taskTable.put(conn.connectId, at);
+ }
+ }
+
+ public void run(){
+ while(true){
+ synchronized (this){
+ Iterator it=taskTable.keySet().iterator();
+ while(it.hasNext()){
+ int id=it.next();
+ AckListTask at=taskTable.get(id);
+ at.run();
+ }
+ taskTable.clear();
+ taskTable=null;
+ taskTable=new HashMap();
+ }
+
+ try {
+ Thread.sleep(RUDPConfig.ackListDelay);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/fs/rudp/AckListTask.java b/src/main/java/net/fs/rudp/AckListTask.java
new file mode 100755
index 0000000..9e85ea0
--- /dev/null
+++ b/src/main/java/net/fs/rudp/AckListTask.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+import net.fs.rudp.message.AckListMessage;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+
+public class AckListTask {
+ ConnectionUDP conn;
+ AckListMessage alm;
+ int lastRead=0;
+ ArrayList ackList;
+ @SuppressWarnings("unchecked")
+ HashSet set;
+ AckListTask(ConnectionUDP conn){
+ this.conn=conn;
+ ackList=new ArrayList();
+ set=new HashSet();
+ }
+
+ synchronized void addAck(int sequence){
+ ////#MLog.println("sendACK "+sequence);
+ if(!set.contains(sequence)){
+ ackList.add(sequence);
+ set.add(sequence);
+ }
+ }
+
+ synchronized void run(){
+ int offset=0;
+ int packetLength=RUDPConfig.ackListSum;
+ int length=ackList.size();
+ ////#MLog.println("ffffffffaaaaaaaaa "+length);
+ int sum=(length/packetLength);
+ if(length%packetLength!=0){
+ sum+=1;
+ }
+ if(sum==0){
+ sum=1;
+ }
+ int len=packetLength;
+ if(length<=len){
+ conn.sender.sendALMessage(ackList);
+ conn.sender.sendALMessage(ackList);
+ }else{
+ for(int i=0;i nl=copy(offset,len,ackList);
+ conn.sender.sendALMessage(nl);
+ conn.sender.sendALMessage(nl);
+// conn.sender.sendALMessage(nl);
+// conn.sender.sendALMessage(nl);
+// conn.sender.sendALMessage(nl);
+ offset+=packetLength;
+ ////#MLog.println("fffffffffa "+nl.size());
+ if(offset+len>length){
+ len=length-(sum-1)*packetLength;
+ }
+ }
+ }
+ }
+
+ ArrayList copy(int offset,int length,ArrayList ackList){
+ ArrayList nl= new ArrayList();
+ for(int i=0;i sendRecordTable=new HashMap();
+
+
+ HashMap sendRecordTable_remote=new HashMap();
+
+
+ long startSendTime=0;
+
+ int maxSpeed=(int) (1024*1024);
+
+ int initSpeed=(int) maxSpeed;
+
+ int currentSpeed=initSpeed;
+
+ int lastTime=-1;
+
+ Object syn_timeid=new Object();
+
+ long sended=0;
+
+ long markTime=0;
+
+ long lastSendPingTime,lastReceivePingTime=System.currentTimeMillis();
+
+ Random ran=new Random();
+
+ HashMap pingTable=new HashMap();
+
+ public int pingDelay=250;
+
+ int clientId_real=-1;
+
+ long needSleep_All,trueSleep_All;
+
+ int maxAcked=0;
+
+ long lastLockTime;
+
+ Route route;
+
+ InetAddress dstIp;
+
+ int dstPort;
+
+ public HashMap connTable=new HashMap();
+
+ Object syn_connTable=new Object();
+
+ Object syn_tunTable=new Object();
+
+ String password;
+
+ public ResendManage resendMange;
+
+ boolean closed=false;
+
+ {
+ resendMange = new ResendManage();
+ }
+
+ ClientControl(Route route,int clientId,InetAddress dstIp,int dstPort){
+ this.clientId=clientId;
+ this.route=route;
+ this.dstIp=dstIp;
+ this.dstPort=dstPort;
+ }
+
+ public void onReceivePacket(DatagramPacket dp){
+ byte[] dpData=dp.getData();
+ int sType=0;
+ sType=MessageCheck.checkSType(dp);
+ int remote_clientId=ByteIntConvert.toInt(dpData, 8);
+ if(sType==net.fs.rudp.message.MessageType.sType_PingMessage){
+ PingMessage pm=new PingMessage(dp);
+ sendPingMessage2(pm.getPingId(),dp.getAddress(),dp.getPort());
+ currentSpeed=pm.getDownloadSpeed()*1024;
+ }else if(sType==net.fs.rudp.message.MessageType.sType_PingMessage2){
+ PingMessage2 pm=new PingMessage2(dp);
+ lastReceivePingTime=System.currentTimeMillis();
+ Long t=pingTable.get(pm.getPingId());
+ if(t!=null){
+ pingDelay=(int) (System.currentTimeMillis()-t);
+ String protocal="";
+ if(route.isUseTcpTun()){
+ protocal="tcp";
+ }else {
+ protocal="udp";
+ }
+ //MLog.println(" receive_ping222: "+pm.getPingId()+" "+new Date());
+ MLog.println("delay_"+protocal+" "+pingDelay+"ms "+dp.getAddress().getHostAddress()+":"+dp.getPort());
+ }
+ }
+ }
+
+ public void sendPacket(DatagramPacket dp) throws IOException{
+
+ //加密
+
+ route.sendPacket(dp);
+ }
+
+ void addConnection(ConnectionUDP conn){
+ synchronized (syn_connTable) {
+ connTable.put(conn.connectId, conn);
+ }
+ }
+
+ void removeConnection(ConnectionUDP conn){
+ synchronized (syn_connTable) {
+ connTable.remove(conn.connectId);
+ }
+ }
+
+ public void close(){
+ closed=true;
+ route.clientManager.removeClient(clientId);
+ synchronized (syn_connTable) {
+ Iterator it=getConnTableIterator();
+ while(it.hasNext()){
+ final ConnectionUDP conn=connTable.get(it.next());
+ if(conn!=null){
+ Route.es.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ conn.stopnow=true;
+ conn.destroy(true);
+ }
+ });
+
+ }
+ }
+ }
+ }
+
+ Iterator getConnTableIterator(){
+ Iterator it=null;
+ synchronized (syn_connTable) {
+ it=new CopiedIterator(connTable.keySet().iterator());
+ }
+ return it;
+ }
+
+ public void updateClientId(int newClientId){
+ clientId_real=newClientId;
+ sendRecordTable.clear();
+ sendRecordTable_remote.clear();
+ }
+
+ public void onSendDataPacket(ConnectionUDP conn){
+
+ }
+
+ public void sendPingMessage(){
+ int pingid=Math.abs(ran.nextInt());
+ long pingTime=System.currentTimeMillis();
+ pingTable.put(pingid, pingTime);
+ lastSendPingTime=System.currentTimeMillis();
+ PingMessage lm=new PingMessage(0,route.localclientId,pingid,Route.localDownloadSpeed,Route.localUploadSpeed);
+ lm.setDstAddress(dstIp);
+ lm.setDstPort(dstPort);
+ try {
+ sendPacket(lm.getDatagramPacket());
+ } catch (IOException e) {
+ //e.printStackTrace();
+ }
+ }
+
+ public void sendPingMessage2(int pingId,InetAddress dstIp,int dstPort){
+ PingMessage2 lm=new PingMessage2(0,route.localclientId,pingId);
+ lm.setDstAddress(dstIp);
+ lm.setDstPort(dstPort);
+ try {
+ sendPacket(lm.getDatagramPacket());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void onReceivePing(PingMessage pm){
+ if(route.mode==2){
+ currentSpeed=pm.getDownloadSpeed()*1024;
+ MLog.println("更新对方速度: "+currentSpeed);
+ }
+ }
+
+ SendRecord getSendRecord(int timeId){
+ SendRecord record=null;
+ synchronized (syn_timeid) {
+ record=sendRecordTable.get(timeId);
+ if(record==null){
+ record=new SendRecord();
+ record.setTimeId(timeId);
+ sendRecordTable.put(timeId, record);
+ }
+ }
+ return record;
+ }
+
+ public int getCurrentTimeId(){
+ long current=System.currentTimeMillis();
+ if(startSendTime==0){
+ startSendTime=current;
+ }
+ int timeId=(int) ((current-startSendTime)/1000);
+ return timeId;
+ }
+
+ public int getTimeId(long time){
+ int timeId=(int) ((time-startSendTime)/1000);
+ return timeId;
+ }
+
+ //纳秒
+ public synchronized void sendSleep(long startTime,int length){
+ if(route.mode==1){
+ currentSpeed=Route.localUploadSpeed;
+ }
+ if(sended==0){
+ markTime=startTime;
+ }
+ sended+=length;
+ //10K sleep
+ if(sended>10*1024){
+ long needTime=(long) (1000*1000*1000f*sended/currentSpeed);
+ long usedTime=System.nanoTime()-markTime;
+ if(usedTime0){
+ if(sleepTime<=moreTime){
+ sleepTime=0;
+ trueSleep_All-=sleepTime;
+ }
+ }
+
+ long s=needTime/(1000*1000);
+ int n=(int) (needTime%(1000*1000));
+ long t1=System.nanoTime();
+ if(sleepTime>0){
+ try {
+ Thread.sleep(s, n);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ trueSleep_All+=(System.nanoTime()-t1);
+ //#MLog.println("sssssssssss "+(trueSleep_All-needSleep_All)/(1000*1000));
+ }
+ ////#MLog.println("sleepb "+sleepTime+" l "+sended+" s "+s+" n "+n+" tt "+(moreTime));
+ }
+ sended=0;
+ }
+
+ }
+
+ public Object getSynlock() {
+ return synlock;
+ }
+
+ public void setSynlock(Object synlock) {
+ this.synlock = synlock;
+ }
+
+ public void setClientId(int clientId) {
+ this.clientId = clientId;
+ }
+
+ public int getClientId_real() {
+ return clientId_real;
+ }
+
+ public void setClientId_real(int clientId_real) {
+ this.clientId_real = clientId_real;
+ lastReceivePingTime=System.currentTimeMillis();
+ }
+
+ public long getLastSendPingTime() {
+ return lastSendPingTime;
+ }
+
+ public void setLastSendPingTime(long lastSendPingTime) {
+ this.lastSendPingTime = lastSendPingTime;
+ }
+
+ public long getLastReceivePingTime() {
+ return lastReceivePingTime;
+ }
+
+ public void setLastReceivePingTime(long lastReceivePingTime) {
+ this.lastReceivePingTime = lastReceivePingTime;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+}
diff --git a/src/main/java/net/fs/rudp/ClientManager.java b/src/main/java/net/fs/rudp/ClientManager.java
new file mode 100755
index 0000000..268066e
--- /dev/null
+++ b/src/main/java/net/fs/rudp/ClientManager.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+import net.fs.utils.MLog;
+
+import java.net.InetAddress;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+
+
+public class ClientManager {
+
+ HashMap clientTable=new HashMap();
+
+ Thread mainThread;
+
+ Route route;
+
+ int receivePingTimeout=8*1000;
+
+ int sendPingInterval=1*1000;
+
+ Object syn_clientTable=new Object();
+
+ ClientManager(Route route){
+ this.route=route;
+ mainThread=new Thread(){
+ @Override
+ public void run(){
+ while(true){
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ scanClientControl();
+ }
+ }
+ };
+ mainThread.start();
+ }
+
+ void scanClientControl(){
+ Iterator it=getClientTableIterator();
+ long current=System.currentTimeMillis();
+ //MLog.println("ffffffffffff "+clientTable.size());
+ while(it.hasNext()){
+ ClientControl cc=clientTable.get(it.next());
+ if(cc!=null){
+ if(current-cc.getLastReceivePingTime()sendPingInterval){
+ cc.sendPingMessage();
+ }
+ }else {
+ //超时关闭client
+ MLog.println("超时关闭Client "+cc.dstIp.getHostAddress()+":"+cc.dstPort+" "+new Date());
+// System.exit(0);
+ synchronized (syn_clientTable) {
+ cc.close();
+ }
+ }
+ }
+ }
+ }
+
+ void removeClient(int clientId){
+ clientTable.remove(clientId);
+ }
+
+ Iterator getClientTableIterator(){
+ Iterator it=null;
+ synchronized (syn_clientTable) {
+ it=new CopiedIterator(clientTable.keySet().iterator());
+ }
+ return it;
+ }
+
+ ClientControl getClientControl(int clientId,InetAddress dstIp,int dstPort){
+ ClientControl c=clientTable.get(clientId);
+ if(c==null){
+ c=new ClientControl(route,clientId,dstIp,dstPort);
+ synchronized (syn_clientTable) {
+ clientTable.put(clientId, c);
+ }
+ }
+ return c;
+ }
+
+}
diff --git a/src/main/java/net/fs/rudp/ClientProcessorInterface.java b/src/main/java/net/fs/rudp/ClientProcessorInterface.java
new file mode 100755
index 0000000..04d53e8
--- /dev/null
+++ b/src/main/java/net/fs/rudp/ClientProcessorInterface.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+public interface ClientProcessorInterface {
+
+ public void onMapClientClose();
+
+}
diff --git a/src/main/java/net/fs/rudp/ConnInfo.java b/src/main/java/net/fs/rudp/ConnInfo.java
new file mode 100755
index 0000000..cd116da
--- /dev/null
+++ b/src/main/java/net/fs/rudp/ConnInfo.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+import net.fs.rudp.StreamPipe.HttpHost;
+
+public class ConnInfo {
+
+
+ String requestHost=null;
+
+ String requestPath=null;
+
+ boolean http=false;
+
+ HttpHost host=null;
+
+ public String getRequestHost() {
+ return requestHost;
+ }
+
+ public void setRequestHost(String requestHost) {
+ this.requestHost = requestHost;
+ }
+
+ public String getRequestPath() {
+ return requestPath;
+ }
+
+ public void setRequestPath(String requestPath) {
+ this.requestPath = requestPath;
+ }
+
+ public boolean isHttp() {
+ return http;
+ }
+
+ public void setHttp(boolean http) {
+ this.http = http;
+ }
+
+ public HttpHost getHost() {
+ return host;
+ }
+
+ public void setHost(HttpHost host) {
+ this.host = host;
+ }
+
+}
diff --git a/src/main/java/net/fs/rudp/ConnectException.java b/src/main/java/net/fs/rudp/ConnectException.java
new file mode 100755
index 0000000..f6e3d7c
--- /dev/null
+++ b/src/main/java/net/fs/rudp/ConnectException.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+import net.fs.utils.MLog;
+
+public class ConnectException extends Exception{
+
+ private static final long serialVersionUID = 8735513900170495107L;
+ String message;
+ ConnectException(String message){
+ this.message=message;
+ }
+ @Override
+ public void printStackTrace(){
+ //MLog.println("连接异常 "+message);
+ }
+
+}
diff --git a/src/main/java/net/fs/rudp/ConnectionProcessor.java b/src/main/java/net/fs/rudp/ConnectionProcessor.java
new file mode 100755
index 0000000..262b9b4
--- /dev/null
+++ b/src/main/java/net/fs/rudp/ConnectionProcessor.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+
+
+public interface ConnectionProcessor {
+ abstract void process(final ConnectionUDP conn);
+}
diff --git a/src/main/java/net/fs/rudp/ConnectionUDP.java b/src/main/java/net/fs/rudp/ConnectionUDP.java
new file mode 100755
index 0000000..00cdd3f
--- /dev/null
+++ b/src/main/java/net/fs/rudp/ConnectionUDP.java
@@ -0,0 +1,129 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.util.Random;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class ConnectionUDP {
+ public InetAddress dstIp;
+ public int dstPort;
+ public Sender sender;
+ public Receiver receiver;
+ public UDPOutputStream uos;
+ public UDPInputStream uis;
+ long connetionId;
+ Route route;
+ int mode;
+ private boolean connected=true;
+ long lastLiveTime=System.currentTimeMillis();
+ long lastSendLiveTime=0;
+
+ static Random ran=new Random();
+
+ int connectId;
+
+ ConnectionProcessor connectionProcessor;
+
+ private LinkedBlockingQueue dpBuffer=new LinkedBlockingQueue();
+
+ public ClientControl clientControl;
+
+ public boolean localClosed=false,remoteClosed=false,destroied=false;
+
+ public boolean stopnow=false;
+
+ public ConnectionUDP(Route ro,InetAddress dstIp,int dstPort,int mode,int connectId,ClientControl clientControl) throws Exception {
+ this.clientControl=clientControl;
+ this.route=ro;
+ this.dstIp=dstIp;
+ this.dstPort=dstPort;
+ this.mode=mode;
+ if(mode==1){
+ //MLog.println(" 发起连接RUDP "+dstIp+":"+dstPort+" connectId "+connectId);
+ }else if(mode==2){
+
+ //MLog.println(" 接受连接RUDP "+dstIp+":"+dstPort+" connectId "+connectId);
+ }
+ this.connectId=connectId;
+ try {
+ sender=new Sender(this);
+ receiver=new Receiver(this);
+ uos=new UDPOutputStream (this);
+ uis=new UDPInputStream(this);
+ if(mode==2){
+ ro.createTunnelProcessor().process(this);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ connected=false;
+ route.connTable.remove(connectId);
+ e.printStackTrace();
+ //#MLog.println(" 连接失败RUDP "+connectId);
+ synchronized(this){
+ notifyAll();
+ }
+ throw e;
+ }
+ //#MLog.println(" 连接成功RUDP "+connectId);
+ synchronized(this){
+ notifyAll();
+ }
+ }
+
+ public DatagramPacket getPacket(int connectId) throws InterruptedException{
+ DatagramPacket dp=(DatagramPacket)dpBuffer.take();
+ return dp;
+ }
+
+ @Override
+ public String toString(){
+ return new String(dstIp+":"+dstPort);
+ }
+
+ public boolean isConnected(){
+ return connected;
+ }
+
+ public void close_local(){
+ if(!localClosed){
+ localClosed=true;
+ if(!stopnow){
+ sender.sendCloseMessage_Conn();
+ }
+ destroy(false);
+ }
+ }
+
+ public void close_remote() {
+ if(!remoteClosed){
+ remoteClosed=true;
+ destroy(false);
+ }
+ }
+
+ //完全关闭
+ public void destroy(boolean force){
+ if(!destroied){
+ if((localClosed&&remoteClosed)||force){
+ destroied=true;
+ connected=false;
+ uis.closeStream_Local();
+ uos.closeStream_Local();
+ sender.destroy();
+ receiver.destroy();
+ route.removeConnection(this);
+ clientControl.removeConnection(this);
+ }
+ }
+ }
+
+ public void close_timeout(){
+ ////#MLog.println("超时关闭RDP连接");
+ }
+
+ void live(){
+ lastLiveTime=System.currentTimeMillis();
+ }
+}
diff --git a/src/main/java/net/fs/rudp/Constant.java b/src/main/java/net/fs/rudp/Constant.java
new file mode 100755
index 0000000..5a6b764
--- /dev/null
+++ b/src/main/java/net/fs/rudp/Constant.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+public class Constant {
+
+
+ public static int code_success=1;
+
+ public static int code_failed=0;
+
+ public static int code_no_port=41;
+
+ public static int code_password_error=42;
+
+ static int protocal_portmap=5755682;
+
+ public static int protocal_socks5proxy=544643;
+
+}
diff --git a/src/main/java/net/fs/rudp/CopiedIterator.java b/src/main/java/net/fs/rudp/CopiedIterator.java
new file mode 100755
index 0000000..7c9e171
--- /dev/null
+++ b/src/main/java/net/fs/rudp/CopiedIterator.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+public class CopiedIterator implements Iterator {
+ private Iterator iterator = null;
+ public CopiedIterator(Iterator itr) {
+ LinkedList list = new LinkedList( );
+ while(itr.hasNext( )) {
+ list.add(itr.next( ));
+ }
+ this.iterator = list.iterator( );
+ }
+ public boolean hasNext( ) {
+ return this.iterator.hasNext( );
+ }
+ public void remove( ) {
+ throw new UnsupportedOperationException("This is a read-only iterator.");
+ }
+ public Object next( ) {
+ return this.iterator.next( );
+ }
+ }
diff --git a/src/main/java/net/fs/rudp/MapSocketPorcessor.java b/src/main/java/net/fs/rudp/MapSocketPorcessor.java
new file mode 100755
index 0000000..1054b68
--- /dev/null
+++ b/src/main/java/net/fs/rudp/MapSocketPorcessor.java
@@ -0,0 +1,211 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+
+public class MapSocketPorcessor implements PipeListener{
+
+
+ Socket srcSocket;
+ Socket dstSocket;
+
+ public DataInputStream srcIs;
+ public DataOutputStream dstOs;
+ public DataInputStream dstIs;
+ public DataOutputStream srcOs;
+
+ Object srcReadOb=new Object();
+ Object dstReadOb=new Object();
+ Object dstWriteOb=new Object();
+ Object srcWriteOb=new Object();
+
+ ArrayList srcReadBuffer=new ArrayList();
+ ArrayList dstReadBuffer=new ArrayList();
+
+ Thread srcReadThread;
+ Thread dstWriteThread;
+ Thread srcWriteThread;
+ Thread dstReadThread;
+ byte[] srcPreRead=new byte[0];
+
+ int maxDstRead=1;
+ int maxSrcRead=1;
+
+ boolean isSuperSocket=false;
+
+ boolean dstReadComplete=false;
+
+ boolean srcReadComplete=false;
+
+ boolean srcWriteComplete=false;
+
+ boolean dstWriteComplete=false;
+
+ boolean dstClosed=false;
+
+ boolean srcClosed=false;
+
+ String st=" ";
+ //String st=" ";
+
+
+ String ss="";
+
+ static int n=0;
+
+ Random ran=new Random();
+
+ long id;
+
+ static int m=0,a,b;
+
+ boolean closed=false;
+
+ long lastActiveTime=System.currentTimeMillis();
+
+ //static HashMap procTable=new HashMap();
+
+ static ExecutorService es;
+
+ MapSocketPorcessor mp;
+
+ Socket socketA,socketB;
+
+ int supserSocketId=-1;
+
+ String requestHost=null;
+
+ static {
+ es=Executors.newCachedThreadPool();
+ }
+
+ public MapSocketPorcessor(){
+
+ }
+
+ public void setDstSocket(Socket dstSocket){
+ this.dstSocket=dstSocket;
+ }
+
+ public void setId(long id){
+ this.id=id;
+ }
+
+ public boolean isClosed(){
+ return closed;
+ }
+
+ public long getLastActiveTime(){
+ return lastActiveTime;
+ }
+
+ void active(){
+ lastActiveTime=System.currentTimeMillis();
+ }
+
+ public long getId(){
+ return id;
+ }
+
+ public void closeAll(){
+ closeAll(true);
+ }
+
+ public void closeAll(boolean closeDst){
+ //procTable.remove(id);
+ //Log.println("closeAll AAAAAAAAA");
+ if(!closed){
+ //Log.println("closeAll BBBBBBBBBB");
+ closed=true;
+ //#MLog.println("MapSocketPorcessor Close");
+ try {
+ srcSocket.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ if(closeDst){
+ try {
+ dstSocket.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ void tryClose(){
+ closeAll();
+ }
+
+ public void start(){
+ Runnable t=new Runnable(){
+ public void run(){
+ try {
+ ConnInfo connInfo=new ConnInfo();
+ StreamPipe p1=new StreamPipe(connInfo,srcIs,dstOs,10*1024,1000*1024);
+ StreamPipe p2=new StreamPipe(connInfo,dstIs,srcOs,10*1024,1000*1024);
+ p1.setType(StreamPipe.type_request);
+ //p1.addListener(mp);
+ //p2.addListener(mp);
+ p1.setSocketA(socketA);
+ p1.setSocketB(socketB);
+ p2.setType(StreamPipe.type_respone);
+ p2.setSocketA(socketA);
+ p2.setSocketB(socketB);
+ p1.setSupserSocketId(supserSocketId);
+ p2.setSupserSocketId(supserSocketId);
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ }
+ };
+ es.execute(t);
+ }
+
+ public void pipeClose() {
+ closeAll();
+ //pipeclose
+ }
+
+ public Socket getSocketA() {
+ return socketA;
+ }
+
+ public void setSocketA(Socket socketA) {
+ this.socketA = socketA;
+ }
+
+ public Socket getSocketB() {
+ return socketB;
+ }
+
+ public void setSocketB(Socket socketB) {
+ this.socketB = socketB;
+ }
+
+ public int getSupserSocketId() {
+ return supserSocketId;
+ }
+
+ public void setSupserSocketId(int supserSocketId) {
+ this.supserSocketId = supserSocketId;
+ }
+
+ public String getRequestHost() {
+ return requestHost;
+ }
+
+ public void setRequestHost(String requestHost) {
+ this.requestHost = requestHost;
+ }
+
+}
diff --git a/src/main/java/net/fs/rudp/MessageInterface.java b/src/main/java/net/fs/rudp/MessageInterface.java
new file mode 100755
index 0000000..4bc4e25
--- /dev/null
+++ b/src/main/java/net/fs/rudp/MessageInterface.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+import java.net.DatagramPacket;
+
+public interface MessageInterface {
+ public int getVer();
+ public int getSType();
+ public DatagramPacket getDatagramPacket();
+}
+
diff --git a/src/main/java/net/fs/rudp/PipeListener.java b/src/main/java/net/fs/rudp/PipeListener.java
new file mode 100755
index 0000000..5d2a253
--- /dev/null
+++ b/src/main/java/net/fs/rudp/PipeListener.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+public interface PipeListener {
+
+ void pipeClose();
+
+}
diff --git a/src/main/java/net/fs/rudp/RUDPConfig.java b/src/main/java/net/fs/rudp/RUDPConfig.java
new file mode 100755
index 0000000..e8ac6f1
--- /dev/null
+++ b/src/main/java/net/fs/rudp/RUDPConfig.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+public class RUDPConfig {
+
+ public static short protocal_ver=0;
+
+ public static int packageSize=800;
+ //public static int packageSize=1024;
+
+ public static boolean twice_udp=false;
+
+ public static boolean twice_tcp=false;
+
+ public static int maxWin = 5*1024;
+
+ public static int ackListDelay = 5;
+ public static int ackListSum = 300;
+
+ public static boolean double_send_start = true;
+
+ public static int reSendDelay_min = 100;
+ public static float reSendDelay = 0.37f;
+ public static int reSendTryTimes = 10;
+
+}
diff --git a/src/main/java/net/fs/rudp/ReceivePingException.java b/src/main/java/net/fs/rudp/ReceivePingException.java
new file mode 100755
index 0000000..da3f040
--- /dev/null
+++ b/src/main/java/net/fs/rudp/ReceivePingException.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+public class ReceivePingException extends Exception{
+ /**
+ *
+ */
+ private static final long serialVersionUID = -5199731243611486228L;
+ String message;
+ ReceivePingException(String message){
+ this.message=message;
+ }
+ @Override
+ public void printStackTrace(){
+ //#MLog.println("Ping寮傚父 "+message);
+ }
+}
diff --git a/src/main/java/net/fs/rudp/Receiver.java b/src/main/java/net/fs/rudp/Receiver.java
new file mode 100755
index 0000000..9b82191
--- /dev/null
+++ b/src/main/java/net/fs/rudp/Receiver.java
@@ -0,0 +1,263 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+import net.fs.rudp.message.AckListMessage;
+import net.fs.rudp.message.CloseMessage_Conn;
+import net.fs.rudp.message.CloseMessage_Stream;
+import net.fs.rudp.message.DataMessage;
+import net.fs.utils.MessageCheck;
+
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+
+public class Receiver {
+ ConnectionUDP conn;
+ Sender sender;
+ public InetAddress dstIp;
+ public int dstPort;
+ HashMap receiveTable=new HashMap();
+ int lastRead=-1;
+ int lastReceive=-1;
+ Object availOb=new Object();
+
+ boolean isReady=false;
+ Object readyOb=new Object();
+ byte[] b4=new byte[4];
+ int lastRead1=0;
+ int maxWinR=10;
+ int lastRead2=-1;
+ UDPInputStream uis;
+
+ float availWin=RUDPConfig.maxWin;
+
+ int currentRemoteTimeId;
+
+ int closeOffset;
+
+ boolean streamClose=false;
+
+ boolean reveivedClose=false;
+
+ static int m=0,x,x2,c;
+
+ boolean b=false,b2;
+
+ public int nw;
+
+ long received;
+
+ Receiver(ConnectionUDP conn){
+ this.conn=conn;
+ uis=new UDPInputStream(conn);
+ this.sender=conn.sender;
+ this.dstIp=conn.dstIp;
+ this.dstPort=conn.dstPort;
+ }
+
+ //接收流数据
+ public byte[] receive() throws ConnectException {
+ DataMessage me=null;
+ if(conn.isConnected()){
+ me=receiveTable.get(lastRead+1);
+ synchronized (availOb){
+ if(me==null){
+ //MLog.println("等待中 "+conn.connectId+" "+(lastRead+1));
+
+ try {
+ availOb.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ me=receiveTable.get(lastRead+1);
+ //MLog.println("等待完成aaa "+conn.connectId+" "+(lastRead+1));
+ }
+ }
+
+ }else{
+ //throw new ConnectException("连接未建立");
+ throw new ConnectException("");
+ }
+
+ if(!streamClose){
+ checkCloseOffset_Remote();
+ if(me==null){
+ //throw new ConnectException("连接已断开");
+ throw new ConnectException("");
+ }else {
+ }
+ conn.sender.sendLastReadDelay();
+
+ lastRead++;
+ synchronized (availOb){
+ receiveTable.remove(me.getSequence());
+ }
+
+ received+=me.getData().length;
+ //System.out.println("received "+received/1024/1024+"MB");
+ return me.getData();
+ }else{
+ //throw new ConnectException("连接已断开");
+ throw new ConnectException("");
+ }
+ }
+
+ public void onReceivePacket(DatagramPacket dp){
+ DataMessage me;
+ if(dp!=null){
+ if(conn.isConnected()){
+ int ver=MessageCheck.checkVer(dp);
+ int sType=MessageCheck.checkSType(dp);
+ if(ver==RUDPConfig.protocal_ver){
+ conn.live();
+ if(sType==net.fs.rudp.message.MessageType.sType_DataMessage){
+ me=new DataMessage(dp);
+ int timeId=me.getTimeId();
+ SendRecord record=conn.clientControl.sendRecordTable_remote.get(timeId);
+ if(record==null){
+ record=new SendRecord();
+ record.setTimeId(timeId);
+ conn.clientControl.sendRecordTable_remote.put(timeId, record);
+ }
+ record.addSended(me.getData().length);
+
+ if(timeId>currentRemoteTimeId){
+ currentRemoteTimeId=timeId;
+ }
+
+ int sequence=me.getSequence();
+
+ conn.sender.sendAckDelay(me.getSequence());
+ if(sequence>lastRead){
+ synchronized (availOb){
+ receiveTable.put(sequence, me);
+ if(receiveTable.containsKey(lastRead+1)){
+ availOb.notify();
+ }
+ }
+ }
+ }else if(sType==net.fs.rudp.message.MessageType.sType_AckListMessage){
+ AckListMessage alm=new AckListMessage(dp);
+ int lastRead3=alm.getLastRead();
+ if(lastRead3>lastRead2){
+ lastRead2=lastRead3;
+ }
+ ArrayList ackList=alm.getAckList();
+
+ for(int i=0;irc1.getAckedSize()){
+ rc1.setAckedSize(alm.getS1());
+ }
+ }
+
+ SendRecord rc2=conn.clientControl.getSendRecord(alm.getR2());
+ if(rc2!=null){
+ if(alm.getS2()>rc2.getAckedSize()){
+ rc2.setAckedSize(alm.getS2());
+ }
+ }
+
+ SendRecord rc3=conn.clientControl.getSendRecord(alm.getR3());
+ if(rc3!=null){
+ if(alm.getS3()>rc3.getAckedSize()){
+ rc3.setAckedSize(alm.getS3());
+ }
+ }
+
+ if(checkWin()){
+ conn.sender.play();
+ }
+ }else if(sType==net.fs.rudp.message.MessageType.sType_CloseMessage_Stream){
+ CloseMessage_Stream cm=new CloseMessage_Stream(dp);
+ reveivedClose=true;
+ int n=cm.getCloseOffset();
+ closeStream_Remote(n);
+ }else if(sType==net.fs.rudp.message.MessageType.sType_CloseMessage_Conn){
+ CloseMessage_Conn cm2=new CloseMessage_Conn(dp);
+ conn.close_remote();
+ }else{
+ ////#MLog.println("未处理数据包 "+sType);
+ }
+ }
+
+ }
+ }
+
+ }
+
+ public void destroy(){
+ //#MLog.println("destroy destroy destroy");
+ synchronized (availOb) {
+ receiveTable.clear();
+ }
+ }
+
+ boolean checkWin(){
+ nw=conn.sender.sendOffset-lastRead2;
+ boolean b=false;
+ if(nw=closeOffset-1){
+ streamClose=true;
+ synchronized (availOb){
+ availOb.notifyAll();
+ }
+ conn.sender.closeStream_Remote();
+ }
+ }
+ }
+ }
+
+ void closeStream_Local(){
+ if(!streamClose){
+ c++;
+ streamClose=true;
+ synchronized (availOb){
+ availOb.notifyAll();
+ }
+ conn.sender.closeStream_Local();
+ }
+ }
+
+ public int getCurrentTimeId() {
+ return currentRemoteTimeId;
+ }
+
+ public void setCurrentTimeId(int currentTimeId) {
+ this.currentRemoteTimeId = currentTimeId;
+ }
+
+
+ public int getCloseOffset() {
+ return closeOffset;
+ }
+
+
+ public void setCloseOffset(int closeOffset) {
+ this.closeOffset = closeOffset;
+ }
+
+}
diff --git a/src/main/java/net/fs/rudp/ResendItem.java b/src/main/java/net/fs/rudp/ResendItem.java
new file mode 100755
index 0000000..e27ee31
--- /dev/null
+++ b/src/main/java/net/fs/rudp/ResendItem.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+public class ResendItem {
+
+ int count;
+
+ ConnectionUDP conn;
+
+ int sequence;
+
+ long resendTime;
+
+ ResendItem(ConnectionUDP conn,int sequence){
+ this.conn=conn;
+ this.sequence=sequence;
+ }
+
+ void addCount(){
+ count++;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public ConnectionUDP getConn() {
+ return conn;
+ }
+
+ public void setConn(ConnectionUDP conn) {
+ this.conn = conn;
+ }
+
+ public int getSequence() {
+ return sequence;
+ }
+
+ public void setSequence(int sequence) {
+ this.sequence = sequence;
+ }
+
+ public long getResendTime() {
+ return resendTime;
+ }
+
+ public void setResendTime(long resendTime) {
+ this.resendTime = resendTime;
+ }
+
+}
diff --git a/src/main/java/net/fs/rudp/ResendManage.java b/src/main/java/net/fs/rudp/ResendManage.java
new file mode 100755
index 0000000..60bfd85
--- /dev/null
+++ b/src/main/java/net/fs/rudp/ResendManage.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2015 D1SM.net
+
+package net.fs.rudp;
+
+import java.util.concurrent.LinkedBlockingQueue;
+
+
+public class ResendManage implements Runnable{
+
+ boolean haveTask=false;
+ Object signalOb=new Object();
+ Thread mainThread;
+ long vTime=0;
+ long lastReSendTime;
+
+ LinkedBlockingQueue taskList=new LinkedBlockingQueue();
+
+ public ResendManage(){
+ Route.es.execute(this);
+ }
+
+ public void addTask(final ConnectionUDP conn,final int sequence){
+ ResendItem ri=new ResendItem(conn, sequence);
+ ri.setResendTime(getNewResendTime(conn));
+ taskList.add(ri);
+ }
+
+ long getNewResendTime(ConnectionUDP conn){
+ int delayAdd=conn.clientControl.pingDelay+(int) ((float)conn.clientControl.pingDelay*RUDPConfig.reSendDelay);
+ if(delayAdd0){
+ Thread.sleep(sleepTime);
+ }
+ ri.addCount();
+
+ if(ri.conn.sender.getDataMessage(ri.sequence)!=null){
+
+ if(!ri.conn.stopnow){
+ //多线程重发容易内存溢出
+// Route.es.execute(new Runnable() {
+//
+// @Override
+// public void run() {
+// ri.conn.sender.reSend(ri.sequence,ri.getCount());
+// }
+//
+// });
+ ri.conn.sender.reSend(ri.sequence,ri.getCount());
+ }
+
+ }
+ if(ri.getCount() connTable;
+ Route route;
+ Thread mainThread;
+ Thread reveiveThread;
+
+ public static ThreadPoolExecutor es;
+
+ public AckListManage delayAckManage;
+
+ Object syn_ds2Table=new Object();
+
+ Object syn_tunTable=new Object();
+
+ Random ran=new Random();
+
+ public int localclientId=Math.abs(ran.nextInt());
+
+ LinkedBlockingQueue packetBuffer=new LinkedBlockingQueue();
+
+ public static int mode_server=2;
+
+ public static int mode_client=1;
+
+ public int mode=mode_client;//1客户端,2服务端
+
+ String pocessName="";
+
+ HashSet setedTable=new HashSet();
+
+ static int vv;
+
+ HashSet closedTable=new HashSet();
+
+ public static int localDownloadSpeed,localUploadSpeed;
+
+ ClientManager clientManager;
+
+ HashSet pingTable=new HashSet();
+
+ public CapEnv capEnv=null;
+
+ public ClientControl lastClientControl;
+
+ public boolean useTcpTun=true;
+
+ public HashMap