From c15c55fe05495632f8c3891b8e0caed681d5b145 Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Thu, 29 Oct 2020 22:47:57 +0800 Subject: [PATCH] ssr: move 3rd party ssr pkg to internal --- go.mod | 8 +- go.sum | 51 +-- proxy/obfs/tls.go | 1 - proxy/redir/redir_linux.go | 3 - proxy/ssr/internal/{ => cipher}/cipher.go | 4 +- proxy/ssr/internal/client.go | 50 ++- proxy/ssr/internal/obfs/base.go | 36 ++ proxy/ssr/internal/obfs/http_post.go | 20 ++ proxy/ssr/internal/obfs/http_simple.go | 185 ++++++++++ proxy/ssr/internal/obfs/plain.go | 46 +++ proxy/ssr/internal/obfs/random_head.go | 83 +++++ proxy/ssr/internal/obfs/tls12_ticket_auth.go | 313 +++++++++++++++++ .../ssr/internal/protocol/auth_aes128_md5.go | 280 +++++++++++++++ .../ssr/internal/protocol/auth_aes128_sha1.go | 25 ++ proxy/ssr/internal/protocol/auth_chain_a.go | 320 ++++++++++++++++++ proxy/ssr/internal/protocol/auth_chain_b.go | 81 +++++ proxy/ssr/internal/protocol/auth_sha1_v4.go | 226 +++++++++++++ proxy/ssr/internal/protocol/base.go | 47 +++ proxy/ssr/internal/protocol/origin.go | 46 +++ proxy/ssr/internal/protocol/verify_sha1.go | 105 ++++++ proxy/ssr/internal/ssr/adler32.go | 31 ++ proxy/ssr/internal/ssr/crc32.go | 52 +++ proxy/ssr/internal/ssr/obfs.go | 59 ++++ proxy/ssr/internal/tools/encrypt.go | 51 +++ proxy/ssr/internal/tools/obfsutil.go | 53 +++ proxy/ssr/ssr.go | 12 +- 26 files changed, 2098 insertions(+), 90 deletions(-) rename proxy/ssr/internal/{ => cipher}/cipher.go (99%) create mode 100644 proxy/ssr/internal/obfs/base.go create mode 100644 proxy/ssr/internal/obfs/http_post.go create mode 100644 proxy/ssr/internal/obfs/http_simple.go create mode 100644 proxy/ssr/internal/obfs/plain.go create mode 100644 proxy/ssr/internal/obfs/random_head.go create mode 100644 proxy/ssr/internal/obfs/tls12_ticket_auth.go create mode 100644 proxy/ssr/internal/protocol/auth_aes128_md5.go create mode 100644 proxy/ssr/internal/protocol/auth_aes128_sha1.go create mode 100644 proxy/ssr/internal/protocol/auth_chain_a.go create mode 100644 proxy/ssr/internal/protocol/auth_chain_b.go create mode 100644 proxy/ssr/internal/protocol/auth_sha1_v4.go create mode 100644 proxy/ssr/internal/protocol/base.go create mode 100644 proxy/ssr/internal/protocol/origin.go create mode 100644 proxy/ssr/internal/protocol/verify_sha1.go create mode 100644 proxy/ssr/internal/ssr/adler32.go create mode 100644 proxy/ssr/internal/ssr/crc32.go create mode 100644 proxy/ssr/internal/ssr/obfs.go create mode 100644 proxy/ssr/internal/tools/encrypt.go create mode 100644 proxy/ssr/internal/tools/obfsutil.go diff --git a/go.mod b/go.mod index 71ef03e..14a9026 100644 --- a/go.mod +++ b/go.mod @@ -7,16 +7,16 @@ require ( github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152 + github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect github.com/insomniacslk/dhcp v0.0.0-20200922210017-67c425063dca - github.com/mzz2017/shadowsocksR v1.0.0 github.com/nadoo/conflag v0.2.3 github.com/nadoo/ipset v0.3.0 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/xtaci/kcp-go/v5 v5.6.1 golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 - golang.org/x/net v0.0.0-20201027133719-8eef5233e2a1 // indirect - golang.org/x/sys v0.0.0-20201027140754-0fcbb8f4928c // indirect - golang.org/x/tools v0.0.0-20201026223136-e84cfc6dd5ca // indirect + golang.org/x/net v0.0.0-20201029055024-942e2f445f3c // indirect + golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 // indirect + golang.org/x/tools v0.0.0-20201029135353-690a3c245f28 // indirect gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect ) diff --git a/go.sum b/go.sum index 4dad7fd..ca9f3f8 100644 --- a/go.sum +++ b/go.sum @@ -2,7 +2,6 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-camellia v0.0.0-20140412174459-3be6b3054dd1/go.mod h1:QX5ZVULjAfZJux/W62Y91HvCh9hyW6enAwcrrv/sLj0= github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d h1:CPqTNIigGweVPT4CYb+OO2E6XyRKFOmvTHwWRLgCAlE= github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d/go.mod h1:QX5ZVULjAfZJux/W62Y91HvCh9hyW6enAwcrrv/sLj0= github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb h1:zXpN5126w/mhECTkqazBkrOJIMatbPP71aSIDR5UuW4= @@ -26,13 +25,9 @@ github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+ github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20200726165900-d699427278d3/go.mod h1:HrWYfaMfyH5ODyA6hVxnedRaY7Jr4ctlyZf1xJt8gsw= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.2/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= -github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= -github.com/klauspost/reedsolomon v1.9.6/go.mod h1:+8WD025Xpby8/kG5h/HDPIFhiiuGEtZOKw+5Y4drAD8= github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54= github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -48,21 +43,12 @@ github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 h1:aFkJ6lx4FPip+S+Uw4 github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA= github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls= -github.com/mzz2017/shadowsocksR v0.0.0-20200126130347-721f53a7b15a/go.mod h1:1SJEvxD2Y+N7SK2NpCC4wSatvfGGTUo2rhPdthUFsCU= -github.com/mzz2017/shadowsocksR v1.0.0 h1:F/CdugIPUJYasqsRK4qWTo+8/mJgZHGXkwXhf67zJx0= -github.com/mzz2017/shadowsocksR v1.0.0/go.mod h1:5A4hA1y7oP4SoAqcc7gZvjFF63KpKpI5aCUIMrXV1UI= -github.com/nadoo/conflag v0.2.2/go.mod h1:dzFfDUpXdr2uS2oV+udpy5N2vfNOu/bFzjhX1WI52co= github.com/nadoo/conflag v0.2.3 h1:/+rTaN0bHTIiQbPl1WZK78JRoqjlNqJ9Zf05ep0o5jI= github.com/nadoo/conflag v0.2.3/go.mod h1:dzFfDUpXdr2uS2oV+udpy5N2vfNOu/bFzjhX1WI52co= -github.com/nadoo/glider v0.9.2/go.mod h1:S/94KRJFNtgoNlyEm4+33f/DrEsj/uxvismOW4FlIa0= -github.com/nadoo/glider v0.10.0/go.mod h1:q5d4Q5yoGk3nLAhshDJalnl0NXJ8Xh4ODEgp+qbdAAg= -github.com/nadoo/go-shadowsocks2 v0.1.2/go.mod h1:/E2kSkS0mqF/e79wcAA0PezoWXk4CY9HldJlzwWtbwU= github.com/nadoo/ipset v0.3.0 h1:TgULgp4s2PI3ItoCykDzMp8R49fRhMUNoUUEahERr5o= github.com/nadoo/ipset v0.3.0/go.mod h1:ugJe3mH5N1UNQbXayGJnLEMALeiwCJYo49Wg4MnZTHU= -github.com/nadoo/shadowsocksR v0.1.0/go.mod h1:nqcLRU7laARXdLLBrHP8odruT/6GIureicuRTs4R+RY= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -70,25 +56,17 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= github.com/templexxx/cpu v0.0.7 h1:pUEZn8JBy/w5yzdYWgx+0m0xL9uk6j4K91C5kOViAzo= github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= -github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= -github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg= github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo= -github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= -github.com/tjfoc/gmsm v1.3.0/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM= github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/u-root/u-root v7.0.0+incompatible h1:u+KSS04pSxJGI5E7WE4Bs9+Zd75QjFv+REkjy/aoAc8= github.com/u-root/u-root v7.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY= -github.com/xtaci/kcp-go v5.4.11+incompatible h1:tJbtarpmOoOD74cZ41uvvF5Hyt1nvctHQCOxZ6ot5xw= -github.com/xtaci/kcp-go v5.4.11+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= -github.com/xtaci/kcp-go/v5 v5.5.12/go.mod h1:H0T/EJ+lPNytnFYsKLH0JHUtiwZjG3KXlTM6c+Q4YUo= github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI= github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo= github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM= @@ -96,17 +74,10 @@ github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -gitlab.com/yawning/chacha20.git v0.0.0-20190903091407-6d1cb28dc72c h1:yrfrd1u7MWIwWIulet2TZPEkeNQhQ/GcPLdPXgiEEr0= -gitlab.com/yawning/chacha20.git v0.0.0-20190903091407-6d1cb28dc72c/go.mod h1:3x6b94nWCP/a2XB/joOPMiGYUBvqbLfeY/BkHLeDs6s= golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191010185427-af544f31c8ac/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= @@ -121,16 +92,13 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201027133719-8eef5233e2a1 h1:IEhJ99VWSYpHIxjlbu3DQyHegGPnQYAv0IaCX9KHyG0= -golang.org/x/net v0.0.0-20201027133719-8eef5233e2a1/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201029055024-942e2f445f3c h1:rpcgRPA7OvNEOdprt2Wx8/Re2cBTd8NPo/lvo3AyMqk= +golang.org/x/net v0.0.0-20201029055024-942e2f445f3c/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -142,20 +110,14 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191009170203-06d7bd2c5f4f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201027140754-0fcbb8f4928c h1:2+jF2APAgFgXJnYOQGDGGiRvvEo6OhqZGQf46n9xgEw= -golang.org/x/sys v0.0.0-20201027140754-0fcbb8f4928c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM= +golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -163,8 +125,8 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201026223136-e84cfc6dd5ca h1:vL6Mv8VrSxz8azdgLrH/zO/Rd1Bzdk89ZfMVW39gD0Q= -golang.org/x/tools v0.0.0-20201026223136-e84cfc6dd5ca/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201029135353-690a3c245f28 h1:KmEPH4S/AVTMSlbgNWC4xKvfsqvw/dNDUo/bGDqbDdo= +golang.org/x/tools v0.0.0-20201029135353-690a3c245f28/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -173,7 +135,6 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/proxy/obfs/tls.go b/proxy/obfs/tls.go index f25ff50..d443ae6 100644 --- a/proxy/obfs/tls.go +++ b/proxy/obfs/tls.go @@ -2,7 +2,6 @@ // https://golang.org/src/crypto/tls/handshake_messages.go // NOTE: -// https://github.com/shadowsocks/simple-obfs/blob/master/src/obfs_tls.c // The official obfs-server only checks 6 static bytes of client hello packet, // so if we send a malformed packet, e.g: set a wrong length number of extensions, // obfs-server will treat it as a correct packet, but in wireshak, it's malformed. diff --git a/proxy/redir/redir_linux.go b/proxy/redir/redir_linux.go index acf365b..e583710 100644 --- a/proxy/redir/redir_linux.go +++ b/proxy/redir/redir_linux.go @@ -1,6 +1,3 @@ -// getOrigDst: -// https://github.com/shadowsocks/go-shadowsocks2/blob/master/tcp_linux.go#L30 - package redir import ( diff --git a/proxy/ssr/internal/cipher.go b/proxy/ssr/internal/cipher/cipher.go similarity index 99% rename from proxy/ssr/internal/cipher.go rename to proxy/ssr/internal/cipher/cipher.go index 961214c..eab92f0 100644 --- a/proxy/ssr/internal/cipher.go +++ b/proxy/ssr/internal/cipher/cipher.go @@ -1,4 +1,4 @@ -package internal +package cipher import ( "crypto/aes" @@ -14,12 +14,12 @@ import ( "github.com/dgryski/go-camellia" "github.com/dgryski/go-idea" "github.com/dgryski/go-rc2" - "github.com/mzz2017/shadowsocksR/tools" "golang.org/x/crypto/blowfish" "golang.org/x/crypto/cast5" "golang.org/x/crypto/salsa20/salsa" "github.com/nadoo/glider/pool" + "github.com/nadoo/glider/proxy/ssr/internal/tools" ) var errEmptyPassword = errors.New("empty key") diff --git a/proxy/ssr/internal/client.go b/proxy/ssr/internal/client.go index 670a648..1ac0580 100644 --- a/proxy/ssr/internal/client.go +++ b/proxy/ssr/internal/client.go @@ -1,6 +1,6 @@ -// source from https://github.com/v2rayA/shadowsocksR -// just copy here to use the builtin buffer pool. -// as this protocol hasn't been maintained since 2017, it doesn't deserve our research to rewrite it. +// source code from https://github.com/v2rayA/shadowsocksR +// Just copy here to use glider's builtin buffer pool. +// As this protocol hasn't been maintained since 2017, it doesn't deserve our research to rewrite it. package internal @@ -12,13 +12,14 @@ import ( "net" "time" - "github.com/mzz2017/shadowsocksR/obfs" - "github.com/mzz2017/shadowsocksR/protocol" - "github.com/nadoo/glider/pool" + "github.com/nadoo/glider/proxy" + "github.com/nadoo/glider/proxy/ssr/internal/cipher" + "github.com/nadoo/glider/proxy/ssr/internal/obfs" + "github.com/nadoo/glider/proxy/ssr/internal/protocol" ) -const bufSize = 16 << 10 +const bufSize = proxy.TCPBufSize func init() { rand.Seed(time.Now().UnixNano()) @@ -27,7 +28,7 @@ func init() { // SSTCPConn the struct that override the net.Conn methods type SSTCPConn struct { net.Conn - *StreamCipher + *cipher.StreamCipher IObfs obfs.IObfs IProtocol protocol.IProtocol readBuf []byte @@ -38,7 +39,7 @@ type SSTCPConn struct { lastReadError error } -func NewSSTCPConn(c net.Conn, cipher *StreamCipher) *SSTCPConn { +func NewSSTCPConn(c net.Conn, cipher *cipher.StreamCipher) *SSTCPConn { return &SSTCPConn{ Conn: c, StreamCipher: cipher, @@ -108,36 +109,33 @@ func (c *SSTCPConn) doRead(b []byte) (n int, err error) { if c.decryptedBuf.Len() > 0 { return c.decryptedBuf.Read(b) } + n, err = c.Conn.Read(c.readBuf) if n == 0 || err != nil { return n, err } + decodedData, needSendBack, err := c.IObfs.Decode(c.readBuf[:n]) if err != nil { - //log.Println(c.Conn.LocalAddr().String(), c.IObfs.(*obfs.tls12TicketAuth).handshakeStatus, err) return 0, err } //do send back if needSendBack { c.Write(nil) - //log.Println("sendBack") return 0, nil } - //log.Println(len(decodedData), needSendBack, err, n) - if len(decodedData) == 0 { - //log.Println(string(c.readBuf[:200])) - } + decodedDataLen := len(decodedData) if decodedDataLen == 0 { return 0, nil } if !c.DecryptInited() { - if len(decodedData) < c.InfoIVLen() { return 0, errors.New(fmt.Sprintf("invalid ivLen:%v, actual length:%v", c.InfoIVLen(), len(decodedData))) } + iv := decodedData[0:c.InfoIVLen()] if err = c.InitDecrypt(iv); err != nil { return 0, err @@ -146,36 +144,25 @@ func (c *SSTCPConn) doRead(b []byte) (n int, err error) { if len(c.IV()) == 0 { c.SetIV(iv) } + decodedDataLen -= c.InfoIVLen() if decodedDataLen <= 0 { return 0, nil } + decodedData = decodedData[c.InfoIVLen():] } - // nadoo: comment out codes here to use pool buffer - // modify start --> - // buf := make([]byte, decodedDataLen) - // // decrypt decodedData and save it to buf - // c.Decrypt(buf, decodedData) - // // append buf to c.underPostdecryptBuf - // c.underPostdecryptBuf.Write(buf) - // // and read it to buf immediately - // buf = c.underPostdecryptBuf.Bytes() - buf1 := pool.GetBuffer(decodedDataLen) defer pool.PutBuffer(buf1) c.Decrypt(buf1, decodedData) c.underPostdecryptBuf.Write(buf1) buf := c.underPostdecryptBuf.Bytes() - // --> modify end postDecryptedData, length, err := c.IProtocol.PostDecrypt(buf) if err != nil { c.underPostdecryptBuf.Reset() - //log.Println(string(decodebytes)) - //log.Println("err", err) return 0, err } if length == 0 { @@ -187,12 +174,15 @@ func (c *SSTCPConn) doRead(b []byte) (n int, err error) { postDecryptedLength := len(postDecryptedData) blength := len(b) + if blength >= postDecryptedLength { copy(b, postDecryptedData) return postDecryptedLength, nil } + copy(b, postDecryptedData[:blength]) c.decryptedBuf.Write(postDecryptedData[blength:]) + return blength, nil } @@ -200,6 +190,7 @@ func (c *SSTCPConn) preWrite(b []byte) (outData []byte, err error) { if b == nil { b = make([]byte, 0) } + var iv []byte if iv, err = c.initEncryptor(b); err != nil { return @@ -225,6 +216,7 @@ func (c *SSTCPConn) preWrite(b []byte) (outData []byte, err error) { // Put initialization vector in buffer before be encoded copy(cipherData, iv) } + c.Encrypt(cipherData[len(iv):], preEncryptedData) return c.IObfs.Encode(cipherData) } diff --git a/proxy/ssr/internal/obfs/base.go b/proxy/ssr/internal/obfs/base.go new file mode 100644 index 0000000..7f91aaa --- /dev/null +++ b/proxy/ssr/internal/obfs/base.go @@ -0,0 +1,36 @@ +package obfs + +import ( + "strings" + + "github.com/nadoo/glider/proxy/ssr/internal/ssr" +) + +type creator func() IObfs + +var ( + creatorMap = make(map[string]creator) +) + +type IObfs interface { + SetServerInfo(s *ssr.ServerInfo) + GetServerInfo() (s *ssr.ServerInfo) + Encode(data []byte) (encodedData []byte, err error) + Decode(data []byte) (decodedData []byte, needSendBack bool, err error) + SetData(data interface{}) + GetData() interface{} + GetOverhead() int +} + +func register(name string, c creator) { + creatorMap[name] = c +} + +// NewObfs create an obfs object by name and return as an IObfs interface +func NewObfs(name string) IObfs { + c, ok := creatorMap[strings.ToLower(name)] + if ok { + return c() + } + return nil +} diff --git a/proxy/ssr/internal/obfs/http_post.go b/proxy/ssr/internal/obfs/http_post.go new file mode 100644 index 0000000..9a148e5 --- /dev/null +++ b/proxy/ssr/internal/obfs/http_post.go @@ -0,0 +1,20 @@ +package obfs + +import ( + "math/rand" +) + +func init() { + register("http_post", newHttpPost) +} + +// newHttpPost create a http_post object +func newHttpPost() IObfs { + // newHttpSimple create a http_simple object + + t := &httpSimplePost{ + userAgentIndex: rand.Intn(len(requestUserAgent)), + methodGet: false, + } + return t +} diff --git a/proxy/ssr/internal/obfs/http_simple.go b/proxy/ssr/internal/obfs/http_simple.go new file mode 100644 index 0000000..c1607af --- /dev/null +++ b/proxy/ssr/internal/obfs/http_simple.go @@ -0,0 +1,185 @@ +package obfs + +import ( + "bytes" + "encoding/hex" + "fmt" + "math/rand" + "strings" + + "github.com/nadoo/glider/proxy/ssr/internal/ssr" +) + +var ( + requestPath = []string{ + "", "", + "login.php?redir=", "", + "register.php?code=", "", + "?keyword=", "", + "search?src=typd&q=", "&lang=en", + "s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&ch=&bar=&wd=", "&rn=", + "post.php?id=", "&goto=view.php", + } + requestUserAgent = []string{ + "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0", + "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/44.0", + "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/27.0.1453.93 Chrome/27.0.1453.93 Safari/537.36", + "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0", + "Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)", + "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27", + "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)", + "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko", + "Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36", + "Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", + "Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3", + } +) + +// HttpSimple http_simple obfs encapsulate +type httpSimplePost struct { + ssr.ServerInfo + rawTransSent bool + rawTransReceived bool + userAgentIndex int + methodGet bool // true for get, false for post +} + +func init() { + register("http_simple", newHttpSimple) +} + +// newHttpSimple create a http_simple object +func newHttpSimple() IObfs { + + t := &httpSimplePost{ + rawTransSent: false, + rawTransReceived: false, + userAgentIndex: rand.Intn(len(requestUserAgent)), + methodGet: true, + } + return t +} + +func (t *httpSimplePost) SetServerInfo(s *ssr.ServerInfo) { + t.ServerInfo = *s +} + +func (t *httpSimplePost) GetServerInfo() (s *ssr.ServerInfo) { + return &t.ServerInfo +} + +func (t *httpSimplePost) SetData(data interface{}) { + +} + +func (t *httpSimplePost) GetData() interface{} { + return nil +} + +func (t *httpSimplePost) boundary() (ret string) { + + set := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + for i := 0; i < 32; i++ { + ret = fmt.Sprintf("%s%c", ret, set[rand.Intn(len(set))]) + } + return +} + +func (t *httpSimplePost) data2URLEncode(data []byte) (ret string) { + for i := 0; i < len(data); i++ { + ret = fmt.Sprintf("%s%%%s", ret, hex.EncodeToString([]byte{data[i]})) + } + return +} + +func (t *httpSimplePost) Encode(data []byte) (encodedData []byte, err error) { + if t.rawTransSent { + return data, nil + } + + dataLength := len(data) + var headData []byte + if headSize := t.IVLen + t.HeadLen; dataLength-headSize > 64 { + headData = make([]byte, headSize+rand.Intn(64)) + } else { + headData = make([]byte, dataLength) + } + copy(headData, data[0:len(headData)]) + requestPathIndex := rand.Intn(len(requestPath)/2) * 2 + host := t.Host + var customHead string + + if len(t.Param) > 0 { + customHeads := strings.Split(t.Param, "#") + if len(customHeads) > 2 { + customHeads = customHeads[0:2] + } + param := t.Param + if len(customHeads) > 1 { + customHead = customHeads[1] + param = customHeads[0] + } + hosts := strings.Split(param, ",") + if len(hosts) > 0 { + host = strings.TrimSpace(hosts[rand.Intn(len(hosts))]) + } + } + method := "GET /" + if !t.methodGet { + method = "POST /" + } + httpBuf := fmt.Sprintf("%s%s%s%s HTTP/1.1\r\nHost: %s:%d\r\n", + method, + requestPath[requestPathIndex], + t.data2URLEncode(headData), + requestPath[requestPathIndex+1], + host, + t.Port) + if len(customHead) > 0 { + httpBuf = httpBuf + strings.Replace(customHead, "\\n", "\r\n", -1) + "\r\n\r\n" + } else { + var contentType string + if !t.methodGet { + contentType = "Content-Type: multipart/form-data; boundary=" + t.boundary() + "\r\n" + } + httpBuf = httpBuf + + "User-Agent: " + requestUserAgent[t.userAgentIndex] + "\r\n" + + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + + "Accept-Language: en-US,en;q=0.8\r\n" + + "Accept-Encoding: gzip, deflate\r\n" + + contentType + + "DNT: 1\r\n" + + "Connection: keep-alive\r\n" + + "\r\n" + } + + if len(headData) < dataLength { + encodedData = make([]byte, len(httpBuf)+(dataLength-len(headData))) + copy(encodedData, []byte(httpBuf)) + copy(encodedData[len(httpBuf):], data[len(headData):]) + } else { + encodedData = []byte(httpBuf) + } + t.rawTransSent = true + + return +} + +func (t *httpSimplePost) Decode(data []byte) (decodedData []byte, needSendBack bool, err error) { + if t.rawTransReceived { + return data, false, nil + } + + pos := bytes.Index(data, []byte("\r\n\r\n")) + if pos > 0 { + decodedData = make([]byte, len(data)-pos-4) + copy(decodedData, data[pos+4:]) + t.rawTransReceived = true + } + return decodedData, false, nil +} + +func (t *httpSimplePost) GetOverhead() int { + return 0 +} diff --git a/proxy/ssr/internal/obfs/plain.go b/proxy/ssr/internal/obfs/plain.go new file mode 100644 index 0000000..813bcd1 --- /dev/null +++ b/proxy/ssr/internal/obfs/plain.go @@ -0,0 +1,46 @@ +package obfs + +import ( + "github.com/nadoo/glider/proxy/ssr/internal/ssr" +) + +func init() { + register("plain", newPlainObfs) +} + +type plain struct { + ssr.ServerInfo +} + +func newPlainObfs() IObfs { + p := &plain{} + return p +} + +func (p *plain) SetServerInfo(s *ssr.ServerInfo) { + p.ServerInfo = *s +} + +func (p *plain) GetServerInfo() (s *ssr.ServerInfo) { + return &p.ServerInfo +} + +func (p *plain) Encode(data []byte) (encodedData []byte, err error) { + return data, nil +} + +func (p *plain) Decode(data []byte) (decodedData []byte, needSendBack bool, err error) { + return data, false, nil +} + +func (p *plain) SetData(data interface{}) { + +} + +func (p *plain) GetData() interface{} { + return nil +} + +func (p *plain) GetOverhead() int { + return 0 +} diff --git a/proxy/ssr/internal/obfs/random_head.go b/proxy/ssr/internal/obfs/random_head.go new file mode 100644 index 0000000..8da6167 --- /dev/null +++ b/proxy/ssr/internal/obfs/random_head.go @@ -0,0 +1,83 @@ +package obfs + +import ( + "math/rand" + + "github.com/nadoo/glider/proxy/ssr/internal/ssr" +) + +type randomHead struct { + ssr.ServerInfo + rawTransSent bool + rawTransReceived bool + hasSentHeader bool + dataBuffer []byte +} + +func init() { + register("random_head", newRandomHead) +} + +func newRandomHead() IObfs { + p := &randomHead{} + return p +} + +func (r *randomHead) SetServerInfo(s *ssr.ServerInfo) { + r.ServerInfo = *s +} + +func (r *randomHead) GetServerInfo() (s *ssr.ServerInfo) { + return &r.ServerInfo +} + +func (r *randomHead) SetData(data interface{}) { + +} + +func (r *randomHead) GetData() interface{} { + return nil +} + +func (r *randomHead) Encode(data []byte) (encodedData []byte, err error) { + if r.rawTransSent { + return data, nil + } + + dataLength := len(data) + if r.hasSentHeader { + if dataLength > 0 { + d := make([]byte, len(r.dataBuffer)+dataLength) + copy(d, r.dataBuffer) + copy(d[len(r.dataBuffer):], data) + r.dataBuffer = d + } else { + encodedData = r.dataBuffer + r.dataBuffer = nil + r.rawTransSent = true + } + } else { + size := rand.Intn(96) + 8 + encodedData = make([]byte, size) + rand.Read(encodedData) + ssr.SetCRC32(encodedData, size) + + d := make([]byte, dataLength) + copy(d, data) + r.dataBuffer = d + } + r.hasSentHeader = true + return +} + +func (r *randomHead) Decode(data []byte) (decodedData []byte, needSendBack bool, err error) { + if r.rawTransReceived { + return data, false, nil + } + r.rawTransReceived = true + return data, true, nil +} + +func (r *randomHead) GetOverhead() int { + return 0 +} \ No newline at end of file diff --git a/proxy/ssr/internal/obfs/tls12_ticket_auth.go b/proxy/ssr/internal/obfs/tls12_ticket_auth.go new file mode 100644 index 0000000..99545d0 --- /dev/null +++ b/proxy/ssr/internal/obfs/tls12_ticket_auth.go @@ -0,0 +1,313 @@ +package obfs + +import ( + "bytes" + "crypto/hmac" + "encoding/binary" + "fmt" + "log" + "math/rand" + "strings" + "time" + + "github.com/nadoo/glider/proxy/ssr/internal/ssr" + "github.com/nadoo/glider/proxy/ssr/internal/tools" +) + +func init() { + register("tls1.2_ticket_auth", newTLS12TicketAuth) + register("tls1.2_ticket_fastauth", newTLS12TicketFastAuth) +} + +type tlsAuthData struct { + localClientID [32]byte +} + +// tls12TicketAuth tls1.2_ticket_auth obfs encapsulate +type tls12TicketAuth struct { + ssr.ServerInfo + data *tlsAuthData + handshakeStatus int + sendSaver []byte + recvBuffer bytes.Buffer + fastAuth bool +} + +// newTLS12TicketAuth create a tlv1.2_ticket_auth object +func newTLS12TicketAuth() IObfs { + return &tls12TicketAuth{} +} + +// newTLS12TicketFastAuth create a tlv1.2_ticket_fastauth object +func newTLS12TicketFastAuth() IObfs { + return &tls12TicketAuth{ + fastAuth: true, + } +} + +func (t *tls12TicketAuth) SetServerInfo(s *ssr.ServerInfo) { + t.ServerInfo = *s +} + +func (t *tls12TicketAuth) GetServerInfo() (s *ssr.ServerInfo) { + return &t.ServerInfo +} + +func (t *tls12TicketAuth) SetData(data interface{}) { + if auth, ok := data.(*tlsAuthData); ok { + t.data = auth + } +} + +func (t *tls12TicketAuth) GetData() interface{} { + if t.data == nil { + t.data = &tlsAuthData{} + b := make([]byte, 32) + + rand.Read(b) + copy(t.data.localClientID[:], b) + } + return t.data +} + +func (t *tls12TicketAuth) getHost() string { + host := t.Host + if len(t.Param) > 0 { + hosts := strings.Split(t.Param, ",") + if len(hosts) > 0 { + + host = hosts[rand.Intn(len(hosts))] + host = strings.TrimSpace(host) + } + } + if len(host) > 0 && host[len(host)-1] >= byte('0') && host[len(host)-1] <= byte('9') && len(t.Param) == 0 { + host = "" + } + return host +} + +func packData(prefixData []byte, suffixData []byte) (outData []byte) { + d := []byte{0x17, 0x3, 0x3, 0, 0} + binary.BigEndian.PutUint16(d[3:5], uint16(len(suffixData)&0xFFFF)) + outData = append(prefixData, d...) + outData = append(outData, suffixData...) + return +} + +func (t *tls12TicketAuth) Encode(data []byte) (encodedData []byte, err error) { + encodedData = make([]byte, 0) + rand.Seed(time.Now().UnixNano()) + switch t.handshakeStatus { + case 8: + if len(data) < 1024 { + d := []byte{0x17, 0x3, 0x3, 0, 0} + binary.BigEndian.PutUint16(d[3:5], uint16(len(data)&0xFFFF)) + encodedData = append(d, data...) + return + } else { + start := 0 + var l int + for len(data)-start > 2048 { + l = rand.Intn(4096) + 100 + if l > len(data)-start { + l = len(data) - start + } + encodedData = packData(encodedData, data[start:start+l]) + start += l + } + if len(data)-start > 0 { + l = len(data) - start + encodedData = packData(encodedData, data[start:start+l]) + } + return + } + case 1: + if len(data) > 0 { + if len(data) < 1024 { + t.sendSaver = packData(t.sendSaver, data) + } else { + start := 0 + var l int + for len(data)-start > 2048 { + l = rand.Intn(4096) + 100 + if l > len(data)-start { + l = len(data) - start + } + encodedData = packData(encodedData, data[start:start+l]) + start += l + } + if len(data)-start > 0 { + l = len(data) - start + encodedData = packData(encodedData, data[start:start+l]) + } + t.sendSaver = append(t.sendSaver, encodedData...) + encodedData = encodedData[:0] + } + return []byte{}, nil + } + hmacData := make([]byte, 43) + handshakeFinish := []byte("\x14\x03\x03\x00\x01\x01\x16\x03\x03\x00\x20") + copy(hmacData, handshakeFinish) + rand.Read(hmacData[11:33]) + h := t.hmacSHA1(hmacData[:33]) + copy(hmacData[33:], h) + encodedData = append(hmacData, t.sendSaver...) + t.sendSaver = t.sendSaver[:0] + t.handshakeStatus = 8 + case 0: + tlsData0 := []byte("\x00\x1c\xc0\x2b\xc0\x2f\xcc\xa9\xcc\xa8\xcc\x14\xcc\x13\xc0\x0a\xc0\x14\xc0\x09\xc0\x13\x00\x9c\x00\x35\x00\x2f\x00\x0a\x01\x00") + tlsData1 := []byte("\xff\x01\x00\x01\x00") + tlsData2 := []byte("\x00\x17\x00\x00\x00\x23\x00\xd0") + tlsData3 := []byte("\x00\x0d\x00\x16\x00\x14\x06\x01\x06\x03\x05\x01\x05\x03\x04\x01\x04\x03\x03\x01\x03\x03\x02\x01\x02\x03\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x12\x00\x00\x75\x50\x00\x00\x00\x0b\x00\x02\x01\x00\x00\x0a\x00\x06\x00\x04\x00\x17\x00\x18\x00\x15\x00\x66\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") + + var tlsData [2048]byte + tlsDataLen := 0 + copy(tlsData[0:], tlsData1) + tlsDataLen += len(tlsData1) + sni := t.sni(t.getHost()) + copy(tlsData[tlsDataLen:], sni) + tlsDataLen += len(sni) + copy(tlsData[tlsDataLen:], tlsData2) + tlsDataLen += len(tlsData2) + ticketLen := rand.Intn(164)*2 + 64 + tlsData[tlsDataLen-1] = uint8(ticketLen & 0xff) + tlsData[tlsDataLen-2] = uint8(ticketLen >> 8) + //ticketLen := 208 + rand.Read(tlsData[tlsDataLen : tlsDataLen+ticketLen]) + tlsDataLen += ticketLen + copy(tlsData[tlsDataLen:], tlsData3) + tlsDataLen += len(tlsData3) + + length := 11 + 32 + 1 + 32 + len(tlsData0) + 2 + tlsDataLen + encodedData = make([]byte, length) + pdata := length - tlsDataLen + l := tlsDataLen + copy(encodedData[pdata:], tlsData[:tlsDataLen]) + encodedData[pdata-1] = uint8(tlsDataLen) + encodedData[pdata-2] = uint8(tlsDataLen >> 8) + pdata -= 2 + l += 2 + copy(encodedData[pdata-len(tlsData0):], tlsData0) + pdata -= len(tlsData0) + l += len(tlsData0) + copy(encodedData[pdata-32:], t.data.localClientID[:]) + pdata -= 32 + l += 32 + encodedData[pdata-1] = 0x20 + pdata -= 1 + l += 1 + copy(encodedData[pdata-32:], t.packAuthData()) + pdata -= 32 + l += 32 + encodedData[pdata-1] = 0x3 + encodedData[pdata-2] = 0x3 // tls version + pdata -= 2 + l += 2 + encodedData[pdata-1] = uint8(l) + encodedData[pdata-2] = uint8(l >> 8) + encodedData[pdata-3] = 0 + encodedData[pdata-4] = 1 + pdata -= 4 + l += 4 + encodedData[pdata-1] = uint8(l) + encodedData[pdata-2] = uint8(l >> 8) + pdata -= 2 + l += 2 + encodedData[pdata-1] = 0x1 + encodedData[pdata-2] = 0x3 // tls version + pdata -= 2 + l += 2 + encodedData[pdata-1] = 0x16 // tls handshake + pdata -= 1 + l += 1 + + t.sendSaver = packData(t.sendSaver, data) + t.handshakeStatus = 1 + default: + //log.Println(fmt.Errorf("unexpected handshake status: %d", t.handshakeStatus)) + return nil, fmt.Errorf("unexpected handshake status: %d", t.handshakeStatus) + } + return +} + +func (t *tls12TicketAuth) Decode(data []byte) (decodedData []byte, needSendBack bool, err error) { + if t.handshakeStatus == -1 { + return data, false, nil + } + + if t.handshakeStatus == 8 { + t.recvBuffer.Write(data) + for t.recvBuffer.Len() > 5 { + var h [5]byte + _, _ = t.recvBuffer.Read(h[:]) + if !bytes.Equal(h[0:3], []byte{0x17, 0x3, 0x3}) { + log.Println("incorrect magic number", h[0:3], ", 0x170303 is expected") + return nil, false, ssr.ErrTLS12TicketAuthIncorrectMagicNumber + } + size := int(binary.BigEndian.Uint16(h[3:5])) + if t.recvBuffer.Len() < size { + unread := t.recvBuffer.Bytes() + t.recvBuffer.Reset() + t.recvBuffer.Write(h[:]) + t.recvBuffer.Write(unread) + break + } + d := make([]byte, size) + _, _ = t.recvBuffer.Read(d) + decodedData = append(decodedData, d...) + } + return decodedData, false, nil + } + + if len(data) < 11+32+1+32 { + return nil, false, ssr.ErrTLS12TicketAuthTooShortData + } + + hash := t.hmacSHA1(data[11 : 11+22]) + + if !hmac.Equal(data[33:33+ssr.ObfsHMACSHA1Len], hash) { + return nil, false, ssr.ErrTLS12TicketAuthHMACError + } + return nil, true, nil +} + +func (t *tls12TicketAuth) packAuthData() (outData []byte) { + outSize := 32 + outData = make([]byte, outSize) + + now := time.Now().Unix() + binary.BigEndian.PutUint32(outData[0:4], uint32(now)) + + rand.Read(outData[4 : 4+18]) + + hash := t.hmacSHA1(outData[:outSize-ssr.ObfsHMACSHA1Len]) + copy(outData[outSize-ssr.ObfsHMACSHA1Len:], hash) + + return +} + +func (t *tls12TicketAuth) hmacSHA1(data []byte) []byte { + key := make([]byte, t.KeyLen+32) + copy(key, t.Key) + copy(key[t.KeyLen:], t.data.localClientID[:]) + + sha1Data := tools.HmacSHA1(key, data) + return sha1Data[:ssr.ObfsHMACSHA1Len] +} + +func (t *tls12TicketAuth) sni(u string) []byte { + bURL := []byte(u) + length := len(bURL) + ret := make([]byte, length+9) + copy(ret[9:9+length], bURL) + binary.BigEndian.PutUint16(ret[7:], uint16(length&0xFFFF)) + length += 3 + binary.BigEndian.PutUint16(ret[4:], uint16(length&0xFFFF)) + length += 2 + binary.BigEndian.PutUint16(ret[2:], uint16(length&0xFFFF)) + return ret +} + +func (t *tls12TicketAuth) GetOverhead() int { + return 5 +} diff --git a/proxy/ssr/internal/protocol/auth_aes128_md5.go b/proxy/ssr/internal/protocol/auth_aes128_md5.go new file mode 100644 index 0000000..70f5120 --- /dev/null +++ b/proxy/ssr/internal/protocol/auth_aes128_md5.go @@ -0,0 +1,280 @@ +package protocol + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "encoding/binary" + "math/rand" + "strconv" + "strings" + "time" + + "github.com/nadoo/glider/proxy/ssr/internal/ssr" + "github.com/nadoo/glider/proxy/ssr/internal/tools" +) + +func init() { + register("auth_aes128_md5", NewAuthAES128MD5) +} + +func NewAuthAES128MD5() IProtocol { + a := &authAES128{ + salt: "auth_aes128_md5", + hmac: tools.HmacMD5, + hashDigest: tools.MD5Sum, + packID: 1, + recvInfo: recvInfo{ + recvID: 1, + buffer: bytes.NewBuffer(nil), + }, + } + return a +} + +type recvInfo struct { + recvID uint32 + buffer *bytes.Buffer +} + +type authAES128 struct { + ssr.ServerInfo + recvInfo + data *AuthData + hasSentHeader bool + packID uint32 + userKey []byte + salt string + hmac hmacMethod + hashDigest hashDigestMethod +} + +func (a *authAES128) SetServerInfo(s *ssr.ServerInfo) { + a.ServerInfo = *s +} + +func (a *authAES128) GetServerInfo() (s *ssr.ServerInfo) { + return &a.ServerInfo +} + +func (a *authAES128) SetData(data interface{}) { + if auth, ok := data.(*AuthData); ok { + a.data = auth + } +} + +func (a *authAES128) GetData() interface{} { + if a.data == nil { + a.data = &AuthData{} + } + return a.data +} + +func (a *authAES128) packData(data []byte) (outData []byte) { + dataLength := len(data) + randLength := 1 + rand.Seed(time.Now().UnixNano()) + if dataLength <= 1200 { + if a.packID > 4 { + randLength += rand.Intn(32) + } else { + if dataLength > 900 { + randLength += rand.Intn(128) + } else { + randLength += rand.Intn(512) + } + } + } + + outLength := randLength + dataLength + 8 + outData = make([]byte, outLength) + // 0~1, out length + binary.LittleEndian.PutUint16(outData[0:], uint16(outLength&0xFFFF)) + // 2~3, hmac + key := make([]byte, len(a.userKey)+4) + copy(key, a.userKey) + binary.LittleEndian.PutUint32(key[len(key)-4:], a.packID) + h := a.hmac(key, outData[0:2]) + copy(outData[2:4], h[:2]) + // 4~rand length+4, rand number + rand.Read(outData[4 : 4+randLength]) + // 4, rand length + if randLength < 128 { + outData[4] = byte(randLength & 0xFF) + } else { + // 4, magic number 0xFF + outData[4] = 0xFF + // 5~6, rand length + binary.LittleEndian.PutUint16(outData[5:], uint16(randLength&0xFFFF)) + } + // rand length+4~out length-4, data + if dataLength > 0 { + copy(outData[randLength+4:], data) + } + a.packID++ + h = a.hmac(key, outData[:outLength-4]) + copy(outData[outLength-4:], h[:4]) + return +} + +func (a *authAES128) packAuthData(data []byte) (outData []byte) { + dataLength := len(data) + var randLength int + rand.Seed(time.Now().UnixNano()) + if dataLength > 400 { + randLength = rand.Intn(512) + } else { + randLength = rand.Intn(1024) + } + + dataOffset := randLength + 16 + 4 + 4 + 7 + outLength := dataOffset + dataLength + 4 + outData = make([]byte, outLength) + encrypt := make([]byte, 24) + key := make([]byte, a.IVLen+a.KeyLen) + copy(key, a.IV) + copy(key[a.IVLen:], a.Key) + + rand.Read(outData[dataOffset-randLength:]) + a.data.mutex.Lock() + a.data.connectionID++ + if a.data.connectionID > 0xFF000000 { + a.data.clientID = nil + } + if len(a.data.clientID) == 0 { + a.data.clientID = make([]byte, 8) + rand.Read(a.data.clientID) + b := make([]byte, 4) + rand.Read(b) + a.data.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF + } + copy(encrypt[4:], a.data.clientID) + binary.LittleEndian.PutUint32(encrypt[8:], a.data.connectionID) + a.data.mutex.Unlock() + + now := time.Now().Unix() + binary.LittleEndian.PutUint32(encrypt[0:4], uint32(now)) + + binary.LittleEndian.PutUint16(encrypt[12:], uint16(outLength&0xFFFF)) + binary.LittleEndian.PutUint16(encrypt[14:], uint16(randLength&0xFFFF)) + + params := strings.Split(a.Param, ":") + uid := make([]byte, 4) + if len(params) >= 2 { + if userID, err := strconv.ParseUint(params[0], 10, 32); err != nil { + rand.Read(uid) + } else { + binary.LittleEndian.PutUint32(uid, uint32(userID)) + a.userKey = a.hashDigest([]byte(params[1])) + } + } else { + rand.Read(uid) + } + + if a.userKey == nil { + a.userKey = make([]byte, a.KeyLen) + copy(a.userKey, a.Key) + } + + encryptKey := make([]byte, len(a.userKey)) + copy(encryptKey, a.userKey) + + aesCipherKey := tools.EVPBytesToKey(base64.StdEncoding.EncodeToString(encryptKey)+a.salt, 16) + block, err := aes.NewCipher(aesCipherKey) + if err != nil { + return nil + } + + encryptData := make([]byte, 16) + iv := make([]byte, aes.BlockSize) + cbc := cipher.NewCBCEncrypter(block, iv) + cbc.CryptBlocks(encryptData, encrypt[0:16]) + copy(encrypt[4:4+16], encryptData) + copy(encrypt[0:4], uid) + + h := a.hmac(key, encrypt[0:20]) + copy(encrypt[20:], h[:4]) + + rand.Read(outData[0:1]) + h = a.hmac(key, outData[0:1]) + copy(outData[1:], h[0:7-1]) + + copy(outData[7:], encrypt) + copy(outData[dataOffset:], data) + + h = a.hmac(a.userKey, outData[0:outLength-4]) + copy(outData[outLength-4:], h[:4]) + + //log.Println("clientID:", a.data.clientID, "connectionID:", a.data.connectionID) + return +} + +func (a *authAES128) PreEncrypt(plainData []byte) (outData []byte, err error) { + dataLength := len(plainData) + offset := 0 + if dataLength > 0 && !a.hasSentHeader { + authLength := dataLength + if authLength > 1200 { + authLength = 1200 + } + packData := a.packAuthData(plainData[:authLength]) + a.hasSentHeader = true + outData = append(outData, packData...) + dataLength -= authLength + offset += authLength + } + const blockSize = 4096 + for dataLength > blockSize { + packData := a.packData(plainData[offset : offset+blockSize]) + outData = append(outData, packData...) + dataLength -= blockSize + offset += blockSize + } + if dataLength > 0 { + packData := a.packData(plainData[offset:]) + outData = append(outData, packData...) + } + + return +} + +func (a *authAES128) PostDecrypt(plainData []byte) ([]byte, int, error) { + a.buffer.Reset() + plainLength := len(plainData) + readlenth := 0 + key := make([]byte, len(a.userKey)+4) + copy(key, a.userKey) + for plainLength > 4 { + binary.LittleEndian.PutUint32(key[len(key)-4:], a.recvID) + + h := a.hmac(key, plainData[0:2]) + if h[0] != plainData[2] || h[1] != plainData[3] { + return nil, 0, ssr.ErrAuthAES128IncorrectHMAC + } + length := int(binary.LittleEndian.Uint16(plainData[0:2])) + if length >= 8192 || length < 8 { + return nil, 0, ssr.ErrAuthAES128DataLengthError + } + if length > plainLength { + break + } + a.recvID++ + pos := int(plainData[4]) + if pos < 255 { + pos += 4 + } else { + pos = int(binary.LittleEndian.Uint16(plainData[5:7])) + 4 + } + + a.buffer.Write(plainData[pos : length-4]) + plainData = plainData[length:] + plainLength -= length + readlenth += length + } + return a.buffer.Bytes(), readlenth, nil +} + +func (a *authAES128) GetOverhead() int { + return 9 +} diff --git a/proxy/ssr/internal/protocol/auth_aes128_sha1.go b/proxy/ssr/internal/protocol/auth_aes128_sha1.go new file mode 100644 index 0000000..298c468 --- /dev/null +++ b/proxy/ssr/internal/protocol/auth_aes128_sha1.go @@ -0,0 +1,25 @@ +package protocol + +import ( + "bytes" + + "github.com/nadoo/glider/proxy/ssr/internal/tools" +) + +func init() { + register("auth_aes128_sha1", NewAuthAES128SHA1) +} + +func NewAuthAES128SHA1() IProtocol { + a := &authAES128{ + salt: "auth_aes128_sha1", + hmac: tools.HmacSHA1, + hashDigest: tools.SHA1Sum, + packID: 1, + recvInfo: recvInfo{ + recvID: 1, + buffer: bytes.NewBuffer(nil), + }, + } + return a +} diff --git a/proxy/ssr/internal/protocol/auth_chain_a.go b/proxy/ssr/internal/protocol/auth_chain_a.go new file mode 100644 index 0000000..bf2b145 --- /dev/null +++ b/proxy/ssr/internal/protocol/auth_chain_a.go @@ -0,0 +1,320 @@ +// https://github.com/shadowsocksr-backup/shadowsocks-rss/blob/master/doc/auth_chain_a.md + +package protocol + +import ( + "bytes" + "crypto/aes" + stdCipher "crypto/cipher" + "encoding/base64" + "encoding/binary" + "math/rand" + "strconv" + "strings" + "time" + + "github.com/nadoo/glider/proxy/ssr/internal/cipher" + "github.com/nadoo/glider/proxy/ssr/internal/ssr" + "github.com/nadoo/glider/proxy/ssr/internal/tools" +) + +func init() { + register("auth_chain_a", NewAuthChainA) +} + +type authChainA struct { + ssr.ServerInfo + randomClient tools.Shift128plusContext + randomServer tools.Shift128plusContext + recvInfo + cipher *cipher.StreamCipher + hasSentHeader bool + lastClientHash []byte + lastServerHash []byte + userKey []byte + uid [4]byte + salt string + data *AuthData + hmac hmacMethod + hashDigest hashDigestMethod + rnd rndMethod + dataSizeList []int + dataSizeList2 []int + chunkID uint32 +} + +func NewAuthChainA() IProtocol { + a := &authChainA{ + salt: "auth_chain_a", + hmac: tools.HmacMD5, + hashDigest: tools.SHA1Sum, + rnd: authChainAGetRandLen, + recvInfo: recvInfo{ + recvID: 1, + buffer: new(bytes.Buffer), + }, + } + return a +} + +func (a *authChainA) SetServerInfo(s *ssr.ServerInfo) { + a.ServerInfo = *s + if a.salt == "auth_chain_b" { + a.authChainBInitDataSize() + } +} + +func (a *authChainA) GetServerInfo() (s *ssr.ServerInfo) { + return &a.ServerInfo +} + +func (a *authChainA) SetData(data interface{}) { + if auth, ok := data.(*AuthData); ok { + a.data = auth + } +} + +func (a *authChainA) GetData() interface{} { + if a.data == nil { + a.data = &AuthData{} + } + return a.data +} + +func authChainAGetRandLen(dataLength int, random *tools.Shift128plusContext, lastHash []byte, dataSizeList, dataSizeList2 []int, overhead int) int { + if dataLength > 1440 { + return 0 + } + random.InitFromBinDatalen(lastHash[:16], dataLength) + if dataLength > 1300 { + return int(random.Next() % 31) + } + if dataLength > 900 { + return int(random.Next() % 127) + } + if dataLength > 400 { + return int(random.Next() % 521) + } + return int(random.Next() % 1021) +} + +func getRandStartPos(random *tools.Shift128plusContext, randLength int) int { + if randLength > 0 { + return int(random.Next() % 8589934609 % uint64(randLength)) + } + return 0 +} + +func (a *authChainA) getClientRandLen(dataLength int, overhead int) int { + return a.rnd(dataLength, &a.randomClient, a.lastClientHash, a.dataSizeList, a.dataSizeList2, overhead) +} + +func (a *authChainA) getServerRandLen(dataLength int, overhead int) int { + return a.rnd(dataLength, &a.randomServer, a.lastServerHash, a.dataSizeList, a.dataSizeList2, overhead) +} + +func (a *authChainA) packedDataLen(data []byte) (chunkLength, randLength int) { + dataLength := len(data) + randLength = a.getClientRandLen(dataLength, a.Overhead) + chunkLength = randLength + dataLength + 2 + 2 + return +} + +func (a *authChainA) packData(outData []byte, data []byte, randLength int) { + dataLength := len(data) + outLength := randLength + dataLength + 2 + outData[0] = byte(dataLength) ^ a.lastClientHash[14] + outData[1] = byte(dataLength>>8) ^ a.lastClientHash[15] + + { + if dataLength > 0 { + randPart1Length := getRandStartPos(&a.randomClient, randLength) + rand.Read(outData[2 : 2+randPart1Length]) + a.cipher.Encrypt(outData[2+randPart1Length:], data) + rand.Read(outData[2+randPart1Length+dataLength : outLength]) + } else { + rand.Read(outData[2 : 2+randLength]) + } + } + + userKeyLen := uint8(len(a.userKey)) + key := make([]byte, userKeyLen+4) + copy(key, a.userKey) + a.chunkID++ + binary.LittleEndian.PutUint32(key[userKeyLen:], a.chunkID) + a.lastClientHash = a.hmac(key, outData[:outLength]) + copy(outData[outLength:], a.lastClientHash[:2]) + return +} + +const authheadLength = 4 + 8 + 4 + 16 + 4 + +func (a *authChainA) packAuthData(data []byte) (outData []byte) { + outData = make([]byte, authheadLength, authheadLength+1500) + a.data.connectionID++ + if a.data.connectionID > 0xFF000000 { + rand.Read(a.data.clientID) + b := make([]byte, 4) + rand.Read(b) + a.data.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF + } + var key = make([]byte, a.IVLen+a.KeyLen) + copy(key, a.IV) + copy(key[a.IVLen:], a.Key) + + encrypt := make([]byte, 20) + t := time.Now().Unix() + binary.LittleEndian.PutUint32(encrypt[:4], uint32(t)) + copy(encrypt[4:8], a.data.clientID) + binary.LittleEndian.PutUint32(encrypt[8:], a.data.connectionID) + binary.LittleEndian.PutUint16(encrypt[12:], uint16(a.Overhead)) + //binary.LittleEndian.PutUint16(encrypt[14:], 0) + + // first 12 bytes + { + rand.Read(outData[:4]) + a.lastClientHash = a.hmac(key, outData[:4]) + copy(outData[4:], a.lastClientHash[:8]) + } + var base64UserKey string + // uid & 16 bytes auth data + { + uid := make([]byte, 4) + if a.userKey == nil { + params := strings.Split(a.ServerInfo.Param, ":") + if len(params) >= 2 { + if userID, err := strconv.ParseUint(params[0], 10, 32); err == nil { + binary.LittleEndian.PutUint32(a.uid[:], uint32(userID)) + a.userKey = a.hashDigest([]byte(params[1])) + } + } + if a.userKey == nil { + rand.Read(a.uid[:]) + a.userKey = make([]byte, a.KeyLen) + copy(a.userKey, a.Key) + } + } + for i := 0; i < 4; i++ { + uid[i] = a.uid[i] ^ a.lastClientHash[8+i] + } + base64UserKey = base64.StdEncoding.EncodeToString(a.userKey) + aesCipherKey := tools.EVPBytesToKey(base64UserKey+a.salt, 16) + block, err := aes.NewCipher(aesCipherKey) + if err != nil { + return + } + encryptData := make([]byte, 16) + iv := make([]byte, aes.BlockSize) + cbc := stdCipher.NewCBCEncrypter(block, iv) + cbc.CryptBlocks(encryptData, encrypt[:16]) + copy(encrypt[:4], uid[:]) + copy(encrypt[4:4+16], encryptData) + } + // final HMAC + { + a.lastServerHash = a.hmac(a.userKey, encrypt[0:20]) + + copy(outData[12:], encrypt) + copy(outData[12+20:], a.lastServerHash[:4]) + } + + // init cipher + password := make([]byte, len(base64UserKey)+base64.StdEncoding.EncodedLen(16)) + copy(password, base64UserKey) + base64.StdEncoding.Encode(password[len(base64UserKey):], a.lastClientHash[:16]) + a.cipher, _ = cipher.NewStreamCipher("rc4", string(password)) + _, _ = a.cipher.InitEncrypt() + _ = a.cipher.InitDecrypt(nil) + + // data + chunkLength, randLength := a.packedDataLen(data) + if chunkLength <= 1500 { + outData = outData[:authheadLength+chunkLength] + } else { + newOutData := make([]byte, authheadLength+chunkLength) + copy(newOutData, outData[:authheadLength]) + outData = newOutData + } + a.packData(outData[authheadLength:], data, randLength) + return +} + +func (a *authChainA) PreEncrypt(plainData []byte) (outData []byte, err error) { + a.buffer.Reset() + dataLength := len(plainData) + length := dataLength + offset := 0 + if length > 0 && !a.hasSentHeader { + headSize := 1200 + if headSize > dataLength { + headSize = dataLength + } + a.buffer.Write(a.packAuthData(plainData[:headSize])) + offset += headSize + dataLength -= headSize + a.hasSentHeader = true + } + var unitSize = a.TcpMss - a.Overhead + for dataLength > unitSize { + dataLen, randLength := a.packedDataLen(plainData[offset : offset+unitSize]) + b := make([]byte, dataLen) + a.packData(b, plainData[offset:offset+unitSize], randLength) + a.buffer.Write(b) + dataLength -= unitSize + offset += unitSize + } + if dataLength > 0 { + dataLen, randLength := a.packedDataLen(plainData[offset:]) + b := make([]byte, dataLen) + a.packData(b, plainData[offset:], randLength) + a.buffer.Write(b) + } + return a.buffer.Bytes(), nil +} + +func (a *authChainA) PostDecrypt(plainData []byte) (outData []byte, n int, err error) { + a.buffer.Reset() + key := make([]byte, len(a.userKey)+4) + readlenth := 0 + copy(key, a.userKey) + for len(plainData) > 4 { + binary.LittleEndian.PutUint32(key[len(a.userKey):], a.recvID) + dataLen := (int)((uint(plainData[1]^a.lastServerHash[15]) << 8) + uint(plainData[0]^a.lastServerHash[14])) + randLen := a.getServerRandLen(dataLen, a.Overhead) + length := randLen + dataLen + if length >= 4096 { + return nil, 0, ssr.ErrAuthChainDataLengthError + } + length += 4 + if length > len(plainData) { + break + } + + hash := a.hmac(key, plainData[:length-2]) + if !bytes.Equal(hash[:2], plainData[length-2:length]) { + return nil, 0, ssr.ErrAuthChainIncorrectHMAC + } + var dataPos int + if dataLen > 0 && randLen > 0 { + dataPos = 2 + getRandStartPos(&a.randomServer, randLen) + } else { + dataPos = 2 + } + b := make([]byte, dataLen) + a.cipher.Decrypt(b, plainData[dataPos:dataPos+dataLen]) + a.buffer.Write(b) + if a.recvID == 1 { + a.TcpMss = int(binary.LittleEndian.Uint16(a.buffer.Next(2))) + } + a.lastServerHash = hash + a.recvID++ + plainData = plainData[length:] + readlenth += length + + } + return a.buffer.Bytes(), readlenth, nil +} + +func (a *authChainA) GetOverhead() int { + return 4 +} diff --git a/proxy/ssr/internal/protocol/auth_chain_b.go b/proxy/ssr/internal/protocol/auth_chain_b.go new file mode 100644 index 0000000..90659e7 --- /dev/null +++ b/proxy/ssr/internal/protocol/auth_chain_b.go @@ -0,0 +1,81 @@ +package protocol + +import ( + "bytes" + "sort" + + "github.com/nadoo/glider/proxy/ssr/internal/tools" +) + +func init() { + register("auth_chain_b", NewAuthChainB) +} + +func NewAuthChainB() IProtocol { + a := &authChainA{ + salt: "auth_chain_b", + hmac: tools.HmacMD5, + hashDigest: tools.SHA1Sum, + rnd: authChainBGetRandLen, + recvInfo: recvInfo{ + recvID: 1, + buffer: new(bytes.Buffer), + }, + } + return a +} + +func (a *authChainA) authChainBInitDataSize() { + if len(a.Key) == 0 { + return + } + // libev version + random := &a.randomServer + random.InitFromBin(a.Key) + length := random.Next()%8 + 4 + a.dataSizeList = make([]int, length) + for i := 0; i < int(length); i++ { + a.dataSizeList[i] = int(random.Next() % 2340 % 2040 % 1440) + } + sort.Ints(a.dataSizeList) + + length = random.Next()%16 + 8 + a.dataSizeList2 = make([]int, length) + for i := 0; i < int(length); i++ { + a.dataSizeList2[i] = int(random.Next() % 2340 % 2040 % 1440) + } + sort.Ints(a.dataSizeList2) +} + +func authChainBGetRandLen(dataLength int, random *tools.Shift128plusContext, lastHash []byte, dataSizeList, dataSizeList2 []int, overhead int) int { + if dataLength > 1440 { + return 0 + } + random.InitFromBinDatalen(lastHash[:16], dataLength) + // libev version, upper_bound + pos := sort.Search(len(dataSizeList), func(i int) bool { return dataSizeList[i] > dataLength+overhead }) + finalPos := uint64(pos) + random.Next()%uint64(len(dataSizeList)) + if finalPos < uint64(len(dataSizeList)) { + return dataSizeList[finalPos] - dataLength - overhead + } + // libev version, upper_bound + pos = sort.Search(len(dataSizeList2), func(i int) bool { return dataSizeList2[i] > dataLength+overhead }) + finalPos = uint64(pos) + random.Next()%uint64(len(dataSizeList2)) + if finalPos < uint64(len(dataSizeList2)) { + return dataSizeList2[finalPos] - dataLength - overhead + } + if finalPos < uint64(pos+len(dataSizeList2)-1) { + return 0 + } + + if dataLength > 1300 { + return int(random.Next() % 31) + } + if dataLength > 900 { + return int(random.Next() % 127) + } + if dataLength > 400 { + return int(random.Next() % 521) + } + return int(random.Next() % 1021) +} diff --git a/proxy/ssr/internal/protocol/auth_sha1_v4.go b/proxy/ssr/internal/protocol/auth_sha1_v4.go new file mode 100644 index 0000000..eb519cc --- /dev/null +++ b/proxy/ssr/internal/protocol/auth_sha1_v4.go @@ -0,0 +1,226 @@ +package protocol + +import ( + "bytes" + "encoding/binary" + "math/rand" + "time" + + "github.com/nadoo/glider/proxy/ssr/internal/ssr" + "github.com/nadoo/glider/proxy/ssr/internal/tools" +) + +func init() { + register("auth_sha1_v4", NewAuthSHA1v4) +} + +type authSHA1v4 struct { + ssr.ServerInfo + data *AuthData + hasSentHeader bool + buffer bytes.Buffer +} + +func NewAuthSHA1v4() IProtocol { + a := &authSHA1v4{} + return a +} + +func (a *authSHA1v4) SetServerInfo(s *ssr.ServerInfo) { + a.ServerInfo = *s +} + +func (a *authSHA1v4) GetServerInfo() (s *ssr.ServerInfo) { + return &a.ServerInfo +} + +func (a *authSHA1v4) SetData(data interface{}) { + if auth, ok := data.(*AuthData); ok { + a.data = auth + } +} + +func (a *authSHA1v4) GetData() interface{} { + if a.data == nil { + a.data = &AuthData{} + } + return a.data +} + +func (a *authSHA1v4) packData(data []byte) (outData []byte) { + dataLength := len(data) + randLength := 1 + + if dataLength <= 1300 { + if dataLength > 400 { + randLength += rand.Intn(128) + } else { + randLength += rand.Intn(1024) + } + } + + outLength := randLength + dataLength + 8 + outData = make([]byte, outLength) + // 0~1, out length + binary.BigEndian.PutUint16(outData[0:2], uint16(outLength&0xFFFF)) + // 2~3, crc of out length + crc32 := ssr.CalcCRC32(outData, 2, 0xFFFFFFFF) + binary.LittleEndian.PutUint16(outData[2:4], uint16(crc32&0xFFFF)) + // 4, rand length + if randLength < 128 { + outData[4] = uint8(randLength & 0xFF) + } else { + outData[4] = uint8(0xFF) + binary.BigEndian.PutUint16(outData[5:7], uint16(randLength&0xFFFF)) + } + // rand length+4~out length-4, data + if dataLength > 0 { + copy(outData[randLength+4:], data) + } + // out length-4~end, adler32 of full data + adler := ssr.CalcAdler32(outData[:outLength-4]) + binary.LittleEndian.PutUint32(outData[outLength-4:], adler) + + return outData +} + +func (a *authSHA1v4) packAuthData(data []byte) (outData []byte) { + + dataLength := len(data) + randLength := 1 + if dataLength <= 1300 { + if dataLength > 400 { + randLength += rand.Intn(128) + } else { + randLength += rand.Intn(1024) + } + } + dataOffset := randLength + 4 + 2 + outLength := dataOffset + dataLength + 12 + ssr.ObfsHMACSHA1Len + outData = make([]byte, outLength) + a.data.connectionID++ + if a.data.connectionID > 0xFF000000 { + a.data.clientID = nil + } + if len(a.data.clientID) == 0 { + a.data.clientID = make([]byte, 8) + rand.Read(a.data.clientID) + b := make([]byte, 4) + rand.Read(b) + a.data.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF + } + // 0-1, out length + binary.BigEndian.PutUint16(outData[0:2], uint16(outLength&0xFFFF)) + + // 2~6, crc of out length+salt+key + salt := []byte("auth_sha1_v4") + crcData := make([]byte, len(salt)+a.KeyLen+2) + copy(crcData[0:2], outData[0:2]) + copy(crcData[2:], salt) + copy(crcData[2+len(salt):], a.Key) + crc32 := ssr.CalcCRC32(crcData, len(crcData), 0xFFFFFFFF) + // 2~6, crc of out length+salt+key + binary.LittleEndian.PutUint32(outData[2:], crc32) + // 6~rand length+6, rand numbers + rand.Read(outData[dataOffset-randLength : dataOffset]) + // 6, rand length + if randLength < 128 { + outData[6] = byte(randLength & 0xFF) + } else { + // 6, magic number 0xFF + outData[6] = 0xFF + // 7-8, rand length + binary.BigEndian.PutUint16(outData[7:9], uint16(randLength&0xFFFF)) + } + // rand length+6~rand length+10, time stamp + now := time.Now().Unix() + binary.LittleEndian.PutUint32(outData[dataOffset:dataOffset+4], uint32(now)) + // rand length+10~rand length+14, client ID + copy(outData[dataOffset+4:dataOffset+4+4], a.data.clientID[0:4]) + // rand length+14~rand length+18, connection ID + binary.LittleEndian.PutUint32(outData[dataOffset+8:dataOffset+8+4], a.data.connectionID) + // rand length+18~rand length+18+data length, data + copy(outData[dataOffset+12:], data) + + key := make([]byte, a.IVLen+a.KeyLen) + copy(key, a.IV) + copy(key[a.IVLen:], a.Key) + + h := tools.HmacSHA1(key, outData[:outLength-ssr.ObfsHMACSHA1Len]) + // out length-10~out length/rand length+18+data length~end, hmac + copy(outData[outLength-ssr.ObfsHMACSHA1Len:], h[0:ssr.ObfsHMACSHA1Len]) + return outData +} + +func (a *authSHA1v4) PreEncrypt(plainData []byte) (outData []byte, err error) { + a.buffer.Reset() + dataLength := len(plainData) + offset := 0 + if !a.hasSentHeader && dataLength > 0 { + headSize := ssr.GetHeadSize(plainData, 30) + if headSize > dataLength { + headSize = dataLength + } + a.buffer.Write(a.packAuthData(plainData[:headSize])) + offset += headSize + dataLength -= headSize + a.hasSentHeader = true + } + const blockSize = 4096 + for dataLength > blockSize { + a.buffer.Write(a.packData(plainData[offset : offset+blockSize])) + offset += blockSize + dataLength -= blockSize + } + if dataLength > 0 { + a.buffer.Write(a.packData(plainData[offset:])) + } + + return a.buffer.Bytes(), nil +} + +func (a *authSHA1v4) PostDecrypt(plainData []byte) (outData []byte, n int, err error) { + a.buffer.Reset() + dataLength := len(plainData) + plainLength := dataLength + for dataLength > 4 { + crc32 := ssr.CalcCRC32(plainData, 2, 0xFFFFFFFF) + if binary.LittleEndian.Uint16(plainData[2:4]) != uint16(crc32&0xFFFF) { + //common.Error("auth_sha1_v4 post decrypt data crc32 error") + return nil, 0, ssr.ErrAuthSHA1v4CRC32Error + } + length := int(binary.BigEndian.Uint16(plainData[0:2])) + if length >= 8192 || length < 8 { + //common.Error("auth_sha1_v4 post decrypt data length error") + dataLength = 0 + plainData = nil + return nil, 0, ssr.ErrAuthSHA1v4DataLengthError + } + if length > dataLength { + break + } + + if ssr.CheckAdler32(plainData, length) { + pos := int(plainData[4]) + if pos != 0xFF { + pos += 4 + } else { + pos = int(binary.BigEndian.Uint16(plainData[5:5+2])) + 4 + } + outLength := length - pos - 4 + a.buffer.Write(plainData[pos : pos+outLength]) + dataLength -= length + plainData = plainData[length:] + } else { + //common.Error("auth_sha1_v4 post decrypt incorrect checksum") + dataLength = 0 + plainData = nil + return nil, 0, ssr.ErrAuthSHA1v4IncorrectChecksum + } + } + return a.buffer.Bytes(), plainLength - dataLength, nil +} + +func (a *authSHA1v4) GetOverhead() int { + return 7 +} diff --git a/proxy/ssr/internal/protocol/base.go b/proxy/ssr/internal/protocol/base.go new file mode 100644 index 0000000..57d1af9 --- /dev/null +++ b/proxy/ssr/internal/protocol/base.go @@ -0,0 +1,47 @@ +package protocol + +import ( + "strings" + "sync" + + "github.com/nadoo/glider/proxy/ssr/internal/ssr" + "github.com/nadoo/glider/proxy/ssr/internal/tools" +) + +type creator func() IProtocol + +var ( + creatorMap = make(map[string]creator) +) + +type hmacMethod func(key []byte, data []byte) []byte +type hashDigestMethod func(data []byte) []byte +type rndMethod func(dataLength int, random *tools.Shift128plusContext, lastHash []byte, dataSizeList, dataSizeList2 []int, overhead int) int + +type IProtocol interface { + SetServerInfo(s *ssr.ServerInfo) + GetServerInfo() *ssr.ServerInfo + PreEncrypt(data []byte) ([]byte, error) + PostDecrypt(data []byte) ([]byte, int, error) + SetData(data interface{}) + GetData() interface{} + GetOverhead() int +} + +type AuthData struct { + clientID []byte + connectionID uint32 + mutex sync.Mutex +} + +func register(name string, c creator) { + creatorMap[name] = c +} + +func NewProtocol(name string) IProtocol { + c, ok := creatorMap[strings.ToLower(name)] + if ok { + return c() + } + return nil +} diff --git a/proxy/ssr/internal/protocol/origin.go b/proxy/ssr/internal/protocol/origin.go new file mode 100644 index 0000000..2ea515c --- /dev/null +++ b/proxy/ssr/internal/protocol/origin.go @@ -0,0 +1,46 @@ +package protocol + +import ( + "github.com/nadoo/glider/proxy/ssr/internal/ssr" +) + +func init() { + register("origin", NewOrigin) +} + +type origin struct { + ssr.ServerInfo +} + +func NewOrigin() IProtocol { + a := &origin{} + return a +} + +func (o *origin) SetServerInfo(s *ssr.ServerInfo) { + o.ServerInfo = *s +} + +func (o *origin) GetServerInfo() (s *ssr.ServerInfo) { + return &o.ServerInfo +} + +func (o *origin) PreEncrypt(data []byte) (encryptedData []byte, err error) { + return data, nil +} + +func (o *origin) PostDecrypt(data []byte) ([]byte, int, error) { + return data, len(data), nil +} + +func (o *origin) SetData(data interface{}) { + +} + +func (o *origin) GetData() interface{} { + return nil +} + +func (o *origin) GetOverhead() int { + return 0 +} diff --git a/proxy/ssr/internal/protocol/verify_sha1.go b/proxy/ssr/internal/protocol/verify_sha1.go new file mode 100644 index 0000000..2a6d63c --- /dev/null +++ b/proxy/ssr/internal/protocol/verify_sha1.go @@ -0,0 +1,105 @@ +package protocol + +import ( + "bytes" + "encoding/binary" + + "github.com/nadoo/glider/proxy/ssr/internal/ssr" + "github.com/nadoo/glider/proxy/ssr/internal/tools" +) + +func init() { + register("verify_sha1", NewVerifySHA1) + register("ota", NewVerifySHA1) +} + +type verifySHA1 struct { + ssr.ServerInfo + hasSentHeader bool + buffer bytes.Buffer + chunkId uint32 +} + +const ( + oneTimeAuthMask byte = 0x10 +) + +func NewVerifySHA1() IProtocol { + a := &verifySHA1{} + return a +} + +func (v *verifySHA1) otaConnectAuth(data []byte) []byte { + return append(data, tools.HmacSHA1(append(v.IV, v.Key...), data)...) +} + +func (v *verifySHA1) otaReqChunkAuth(chunkId uint32, data []byte) []byte { + nb := make([]byte, 2) + binary.BigEndian.PutUint16(nb, uint16(len(data))) + chunkIdBytes := make([]byte, 4) + binary.BigEndian.PutUint32(chunkIdBytes, chunkId) + header := append(nb, tools.HmacSHA1(append(v.IV, chunkIdBytes...), data)...) + return append(header, data...) +} + +func (v *verifySHA1) otaVerifyAuth(iv []byte, chunkId uint32, data []byte, expectedHmacSha1 []byte) bool { + chunkIdBytes := make([]byte, 4) + binary.BigEndian.PutUint32(chunkIdBytes, chunkId) + actualHmacSha1 := tools.HmacSHA1(append(iv, chunkIdBytes...), data) + return bytes.Equal(expectedHmacSha1, actualHmacSha1) +} + +func (v *verifySHA1) getAndIncreaseChunkId() (chunkId uint32) { + chunkId = v.chunkId + v.chunkId += 1 + return +} + +func (v *verifySHA1) SetServerInfo(s *ssr.ServerInfo) { + v.ServerInfo = *s +} + +func (v *verifySHA1) GetServerInfo() (s *ssr.ServerInfo) { + return &v.ServerInfo +} + +func (v *verifySHA1) SetData(data interface{}) { + +} + +func (v *verifySHA1) GetData() interface{} { + return nil +} + +func (v *verifySHA1) PreEncrypt(data []byte) (encryptedData []byte, err error) { + v.buffer.Reset() + dataLength := len(data) + offset := 0 + if !v.hasSentHeader { + data[0] |= oneTimeAuthMask + v.buffer.Write(v.otaConnectAuth(data[:v.HeadLen])) + v.hasSentHeader = true + dataLength -= v.HeadLen + offset += v.HeadLen + } + const blockSize = 4096 + for dataLength > blockSize { + chunkId := v.getAndIncreaseChunkId() + v.buffer.Write(v.otaReqChunkAuth(chunkId, data[offset:offset+blockSize])) + dataLength -= blockSize + offset += blockSize + } + if dataLength > 0 { + chunkId := v.getAndIncreaseChunkId() + v.buffer.Write(v.otaReqChunkAuth(chunkId, data[offset:])) + } + return v.buffer.Bytes(), nil +} + +func (v *verifySHA1) PostDecrypt(data []byte) ([]byte, int, error) { + return data, len(data), nil +} + +func (v *verifySHA1) GetOverhead() int { + return 0 +} diff --git a/proxy/ssr/internal/ssr/adler32.go b/proxy/ssr/internal/ssr/adler32.go new file mode 100644 index 0000000..6bda937 --- /dev/null +++ b/proxy/ssr/internal/ssr/adler32.go @@ -0,0 +1,31 @@ +package ssr + +import "encoding/binary" + +func calcShortAdler32(input []byte, a, b uint32) (uint32, uint32) { + for _, i := range input { + a += uint32(i) + b += a + } + a %= 65521 + b %= 65521 + return a, b +} + +func CalcAdler32(input []byte) uint32 { + var a uint32 = 1 + var b uint32 = 0 + const nMax = 5552 + for length := len(input); length > nMax; length -= nMax { + a, b = calcShortAdler32(input[:nMax], a, b) + input = input[nMax:] + } + a, b = calcShortAdler32(input, a, b) + return (b << 16) + a +} + +func CheckAdler32(input []byte, l int) bool { + adler32 := CalcAdler32(input[:l-4]) + checksum := binary.LittleEndian.Uint32(input[l-4:]) + return adler32 == checksum +} diff --git a/proxy/ssr/internal/ssr/crc32.go b/proxy/ssr/internal/ssr/crc32.go new file mode 100644 index 0000000..9cf6cc6 --- /dev/null +++ b/proxy/ssr/internal/ssr/crc32.go @@ -0,0 +1,52 @@ +package ssr + +import "encoding/binary" + +var ( + crc32Table = make([]uint32, 256) +) + +func init() { + createCRC32Table() +} + +func createCRC32Table() { + for i := 0; i < 256; i++ { + crc := uint32(i) + for j := 8; j > 0; j-- { + if crc&1 == 1 { + crc = (crc >> 1) ^ 0xEDB88320 + } else { + crc >>= 1 + } + } + crc32Table[i] = crc + } +} + +func CalcCRC32(input []byte, length int, value uint32) uint32 { + value = 0xFFFFFFFF + return DoCalcCRC32(input, 0, length, value) +} + +func DoCalcCRC32(input []byte, index int, length int, value uint32) uint32 { + buffer := input + for i := index; i < length; i++ { + value = (value >> 8) ^ crc32Table[byte(value&0xFF)^buffer[i]] + } + return value ^ 0xFFFFFFFF +} + +func DoSetCRC32(buffer []byte, index int, length int) { + crc := CalcCRC32(buffer[:length-4], length-4, 0xFFFFFFFF) + binary.LittleEndian.PutUint32(buffer[length-4:], crc^0xFFFFFFFF) +} + +func SetCRC32(buffer []byte, length int) { + DoSetCRC32(buffer, 0, length) +} + +func CheckCRC32(buffer []byte, length int) bool { + crc := CalcCRC32(buffer, length, 0xFFFFFFFF) + return crc == 0xFFFFFFFF +} diff --git a/proxy/ssr/internal/ssr/obfs.go b/proxy/ssr/internal/ssr/obfs.go new file mode 100644 index 0000000..e7258fa --- /dev/null +++ b/proxy/ssr/internal/ssr/obfs.go @@ -0,0 +1,59 @@ +package ssr + +import "errors" + +const ObfsHMACSHA1Len = 10 + +var ( + ErrAuthSHA1v4CRC32Error = errors.New("auth_sha1_v4 post decrypt data crc32 error") + ErrAuthSHA1v4DataLengthError = errors.New("auth_sha1_v4 post decrypt data length error") + ErrAuthSHA1v4IncorrectChecksum = errors.New("auth_sha1_v4 post decrypt incorrect checksum") + ErrAuthAES128IncorrectHMAC = errors.New("auth_aes128_* post decrypt incorrect hmac") + ErrAuthAES128DataLengthError = errors.New("auth_aes128_* post decrypt length mismatch") + ErrAuthChainDataLengthError = errors.New("auth_chain_* post decrypt length mismatch") + ErrAuthChainIncorrectHMAC = errors.New("auth_chain_* post decrypt incorrect hmac") + ErrAuthAES128IncorrectChecksum = errors.New("auth_aes128_* post decrypt incorrect checksum") + ErrAuthAES128PosOutOfRange = errors.New("auth_aes128_* post decrypt pos out of range") + ErrTLS12TicketAuthTooShortData = errors.New("tls1.2_ticket_auth too short data") + ErrTLS12TicketAuthHMACError = errors.New("tls1.2_ticket_auth hmac verifying failed") + ErrTLS12TicketAuthIncorrectMagicNumber = errors.New("tls1.2_ticket_auth incorrect magic number") +) + +type ServerInfo struct { + Host string + Port uint16 + Param string + IV []byte + IVLen int + RecvIV []byte + RecvIVLen int + Key []byte + KeyLen int + HeadLen int + TcpMss int + Overhead int +} + +func GetHeadSize(data []byte, defaultValue int) int { + if data == nil || len(data) < 2 { + return defaultValue + } + headType := data[0] & 0x07 + switch headType { + case 1: + // IPv4 1+4+2 + return 7 + case 4: + // IPv6 1+16+2 + return 19 + case 3: + // domain name, variant length + return 4 + int(data[1]) + } + + return defaultValue +} + +func (s *ServerInfo) SetHeadLen(data []byte, defaultValue int) { + s.HeadLen = GetHeadSize(data, defaultValue) +} diff --git a/proxy/ssr/internal/tools/encrypt.go b/proxy/ssr/internal/tools/encrypt.go new file mode 100644 index 0000000..bb81b07 --- /dev/null +++ b/proxy/ssr/internal/tools/encrypt.go @@ -0,0 +1,51 @@ +package tools + +import ( + "crypto/hmac" + "crypto/md5" + "crypto/sha1" +) + +func HmacMD5(key []byte, data []byte) []byte { + hmacMD5 := hmac.New(md5.New, key) + hmacMD5.Write(data) + return hmacMD5.Sum(nil)[:16] +} + +func HmacSHA1(key []byte, data []byte) []byte { + hmacSHA1 := hmac.New(sha1.New, key) + hmacSHA1.Write(data) + return hmacSHA1.Sum(nil)[:20] +} + +func MD5Sum(d []byte) []byte { + h := md5.New() + h.Write(d) + return h.Sum(nil) +} + +func SHA1Sum(d []byte) []byte { + h := sha1.New() + h.Write(d) + return h.Sum(nil) +} + +func EVPBytesToKey(password string, keyLen int) (key []byte) { + const md5Len = 16 + + cnt := (keyLen-1)/md5Len + 1 + m := make([]byte, cnt*md5Len) + copy(m, MD5Sum([]byte(password))) + + // Repeatedly call md5 until bytes generated is enough. + // Each call to md5 uses data: prev md5 sum + password. + d := make([]byte, md5Len+len(password)) + start := 0 + for i := 1; i < cnt; i++ { + start += md5Len + copy(d, m[start-md5Len:start]) + copy(d[md5Len:], password) + copy(m[start:], MD5Sum(d)) + } + return m[:keyLen] +} diff --git a/proxy/ssr/internal/tools/obfsutil.go b/proxy/ssr/internal/tools/obfsutil.go new file mode 100644 index 0000000..473bc22 --- /dev/null +++ b/proxy/ssr/internal/tools/obfsutil.go @@ -0,0 +1,53 @@ +package tools + +import ( + "encoding/binary" + "unsafe" +) + +func IsLittleEndian() bool { + const N int = int(unsafe.Sizeof(0)) + x := 0x1234 + p := unsafe.Pointer(&x) + p2 := (*[N]byte)(p) + if p2[0] == 0 { + return false + } else { + return true + } +} + +type Shift128plusContext struct { + v [2]uint64 +} + +func (ctx *Shift128plusContext) InitFromBin(bin []byte) { + var fillBin [16]byte + copy(fillBin[:], bin) + + ctx.v[0] = binary.LittleEndian.Uint64(fillBin[:8]) + ctx.v[1] = binary.LittleEndian.Uint64(fillBin[8:]) +} + +func (ctx *Shift128plusContext) InitFromBinDatalen(bin []byte, datalen int) { + var fillBin [16]byte + copy(fillBin[:], bin) + binary.LittleEndian.PutUint16(fillBin[:2], uint16(datalen)) + + ctx.v[0] = binary.LittleEndian.Uint64(fillBin[:8]) + ctx.v[1] = binary.LittleEndian.Uint64(fillBin[8:]) + + for i := 0; i < 4; i++ { + ctx.Next() + } +} + +func (ctx *Shift128plusContext) Next() uint64 { + x := ctx.v[0] + y := ctx.v[1] + ctx.v[0] = y + x ^= x << 23 + x ^= y ^ (x >> 17) ^ (y >> 26) + ctx.v[1] = x + return x + y +} diff --git a/proxy/ssr/ssr.go b/proxy/ssr/ssr.go index 348c12d..30ed906 100644 --- a/proxy/ssr/ssr.go +++ b/proxy/ssr/ssr.go @@ -7,14 +7,14 @@ import ( "strconv" "strings" - "github.com/mzz2017/shadowsocksR/obfs" - "github.com/mzz2017/shadowsocksR/protocol" - ssrinfo "github.com/mzz2017/shadowsocksR/ssr" - "github.com/nadoo/glider/log" "github.com/nadoo/glider/proxy" "github.com/nadoo/glider/proxy/socks" "github.com/nadoo/glider/proxy/ssr/internal" + "github.com/nadoo/glider/proxy/ssr/internal/cipher" + "github.com/nadoo/glider/proxy/ssr/internal/obfs" + "github.com/nadoo/glider/proxy/ssr/internal/protocol" + ssrinfo "github.com/nadoo/glider/proxy/ssr/internal/ssr" ) func init() { @@ -40,7 +40,7 @@ type SSR struct { func NewSSR(s string, d proxy.Dialer) (*SSR, error) { u, err := url.Parse(s) if err != nil { - log.F("parse err: %s", err) + log.F("[ssr] parse err: %s", err) return nil, err } @@ -86,7 +86,7 @@ func (s *SSR) Dial(network, addr string) (net.Conn, error) { return nil, errors.New("[ssr] unable to parse address: " + addr) } - cipher, err := internal.NewStreamCipher(s.EncryptMethod, s.EncryptPassword) + cipher, err := cipher.NewStreamCipher(s.EncryptMethod, s.EncryptPassword) if err != nil { return nil, err }