mirror of
https://github.com/net-byte/vtun
synced 2024-03-14 10:50:03 +08:00
optimise code.
This commit is contained in:
parent
b9f0c3fc25
commit
414869fd96
@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"hash/fnv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type XCrypto struct {
|
type XCrypto struct {
|
||||||
@ -25,10 +26,21 @@ func (x *XCrypto) LoadKey(key string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (x *XCrypto) LoadNonce(key string) {
|
func (x *XCrypto) LoadNonce(key string) {
|
||||||
|
n := 2 * 2 * 3
|
||||||
h := sha1.New()
|
h := sha1.New()
|
||||||
h.Write([]byte(key))
|
h.Write([]byte(key))
|
||||||
b := h.Sum(nil)
|
b := h.Sum(nil)
|
||||||
x.Nonce = b[:12]
|
ia := int(String2Int64(key) % int64(len(b)-n))
|
||||||
|
x.Nonce = b[ia : ia+n]
|
||||||
|
}
|
||||||
|
|
||||||
|
func String2Int64(s string) int64 {
|
||||||
|
h := fnv.New32a()
|
||||||
|
_, err := h.Write([]byte(s))
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int64(h.Sum32())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XCrypto) Init(key string) error {
|
func (x *XCrypto) Init(key string) error {
|
||||||
|
11
h1/conn.go
11
h1/conn.go
@ -3,7 +3,6 @@ package h1
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -117,13 +116,3 @@ func mkConnAddr(p1 net.Conn, address string) net.Conn {
|
|||||||
}
|
}
|
||||||
return p1
|
return p1
|
||||||
}
|
}
|
||||||
|
|
||||||
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/-_"
|
|
||||||
|
|
||||||
func randStringBytes(n int) string {
|
|
||||||
b := make([]byte, n)
|
|
||||||
for i := range b {
|
|
||||||
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
|
||||||
}
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
@ -22,6 +22,10 @@ import (
|
|||||||
func StartClient(iFace *water.Interface, config config.Config) {
|
func StartClient(iFace *water.Interface, config config.Config) {
|
||||||
log.Println("vtun h1 client started")
|
log.Println("vtun h1 client started")
|
||||||
var cl *Client
|
var cl *Client
|
||||||
|
tcA := RandomStringByStringNonce(16, config.Key, 123)
|
||||||
|
tcB := RandomStringByStringNonce(32, config.Key, 456)
|
||||||
|
tcC := RandomStringByStringNonce(64, config.Key, 789)
|
||||||
|
ua := RandomUserAgent(config.Key)
|
||||||
go tunToH1(config, iFace)
|
go tunToH1(config, iFace)
|
||||||
for {
|
for {
|
||||||
if config.Protocol == "https" {
|
if config.Protocol == "https" {
|
||||||
@ -29,6 +33,12 @@ func StartClient(iFace *water.Interface, config config.Config) {
|
|||||||
} else {
|
} else {
|
||||||
cl = NewClient(config.ServerAddr)
|
cl = NewClient(config.ServerAddr)
|
||||||
}
|
}
|
||||||
|
cl.TokenCookieA = tcA
|
||||||
|
cl.TokenCookieB = tcB
|
||||||
|
cl.TokenCookieC = tcC
|
||||||
|
cl.Url = "/" + RandomStringByInt64(64, time.Now().UnixMilli())
|
||||||
|
cl.Host = "www.microsoft.com"
|
||||||
|
cl.UserAgent = ua
|
||||||
conn, err := cl.Dial()
|
conn, err := cl.Dial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
|
@ -23,13 +23,16 @@ import (
|
|||||||
func StartServer(iFace *water.Interface, config config.Config) {
|
func StartServer(iFace *water.Interface, config config.Config) {
|
||||||
log.Printf("vtun h1 server started on %v", config.LocalAddr)
|
log.Printf("vtun h1 server started on %v", config.LocalAddr)
|
||||||
webSrv := NewHandle(netutil.GetDefaultHttpHandleFunc())
|
webSrv := NewHandle(netutil.GetDefaultHttpHandleFunc())
|
||||||
|
webSrv.TokenCookieA = RandomStringByStringNonce(16, config.Key, 123)
|
||||||
|
webSrv.TokenCookieB = RandomStringByStringNonce(32, config.Key, 456)
|
||||||
|
webSrv.TokenCookieC = RandomStringByStringNonce(64, config.Key, 789)
|
||||||
http.Handle("/", webSrv)
|
http.Handle("/", webSrv)
|
||||||
srv := &http.Server{Addr: config.LocalAddr, Handler: nil}
|
srv := &http.Server{Addr: config.LocalAddr, Handler: nil}
|
||||||
go func(srv *http.Server) {
|
go func(srv *http.Server) {
|
||||||
var err error
|
var err error
|
||||||
if config.Protocol == "https" {
|
if config.Protocol == "https" {
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS13,
|
||||||
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
||||||
CipherSuites: []uint16{
|
CipherSuites: []uint16{
|
||||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||||
@ -45,7 +48,7 @@ func StartServer(iFace *water.Interface, config config.Config) {
|
|||||||
} else {
|
} else {
|
||||||
err = srv.ListenAndServe()
|
err = srv.ListenAndServe()
|
||||||
}
|
}
|
||||||
if err != ErrServerClose {
|
if err != http.ErrServerClosed {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}(srv)
|
}(srv)
|
||||||
|
@ -38,6 +38,16 @@ func NewTLSClient(config config.Config) *Client {
|
|||||||
cl := NewClient(config.ServerAddr)
|
cl := NewClient(config.ServerAddr)
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS13,
|
||||||
|
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
||||||
|
CipherSuites: []uint16{
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
},
|
||||||
InsecureSkipVerify: config.TLSInsecureSkipVerify,
|
InsecureSkipVerify: config.TLSInsecureSkipVerify,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
47
h1/httpd.go
47
h1/httpd.go
@ -80,9 +80,7 @@ func NewHandle(handler http.Handler) *Server {
|
|||||||
HttpHandler: handler,
|
HttpHandler: handler,
|
||||||
TokenTTL: tokenTTL,
|
TokenTTL: tokenTTL,
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.startTokenCleaner()
|
srv.startTokenCleaner()
|
||||||
|
|
||||||
return srv
|
return srv
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,9 +95,7 @@ func (srv *Server) StartServer() {
|
|||||||
if srv.lis == nil {
|
if srv.lis == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.startTokenCleaner()
|
srv.startTokenCleaner()
|
||||||
|
|
||||||
http.HandleFunc("/", srv.ServeHTTP)
|
http.HandleFunc("/", srv.ServeHTTP)
|
||||||
go http.Serve(srv.lis, nil)
|
go http.Serve(srv.lis, nil)
|
||||||
}
|
}
|
||||||
@ -122,7 +118,6 @@ func (srv *Server) Addr() net.Addr {
|
|||||||
|
|
||||||
func (srv *Server) Close() error {
|
func (srv *Server) Close() error {
|
||||||
srv.dieLock.Lock()
|
srv.dieLock.Lock()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-srv.die:
|
case <-srv.die:
|
||||||
srv.dieLock.Unlock()
|
srv.dieLock.Unlock()
|
||||||
@ -137,38 +132,28 @@ func (srv *Server) Close() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set cookie: TokenCookieA = XXXX
|
|
||||||
// try get cookie: TokenCookieB = XXXX
|
|
||||||
func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
var cc *state
|
var cc *state
|
||||||
var ok bool
|
var ok bool
|
||||||
var err error
|
var err error
|
||||||
var c, ct *http.Cookie
|
var c, ct *http.Cookie
|
||||||
|
c, err = r.Cookie(srv.TokenCookieB)
|
||||||
c, err = r.Cookie(srv.TokenCookieB) // token
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
goto FILE
|
goto FILE
|
||||||
}
|
}
|
||||||
|
ct, err = r.Cookie(srv.TokenCookieC)
|
||||||
ct, err = r.Cookie(srv.TokenCookieC) // flag
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
goto FILE
|
goto FILE
|
||||||
}
|
}
|
||||||
|
|
||||||
cc, ok = srv.checkToken(c.Value)
|
cc, ok = srv.checkToken(c.Value)
|
||||||
if ok {
|
if ok {
|
||||||
if r.Method == srv.RxMethod || r.Method == srv.TxMethod {
|
if r.Method == srv.RxMethod || r.Method == srv.TxMethod {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
goto FILE
|
goto FILE
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.handleNonWs(w, r, c.Value, ct.Value, cc)
|
srv.handleNonWs(w, r, c.Value, ct.Value, cc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE:
|
FILE:
|
||||||
srv.handleBase(w, r)
|
srv.handleBase(w, r)
|
||||||
}
|
}
|
||||||
@ -176,42 +161,33 @@ FILE:
|
|||||||
func (srv *Server) handleBase(w http.ResponseWriter, r *http.Request) {
|
func (srv *Server) handleBase(w http.ResponseWriter, r *http.Request) {
|
||||||
header := w.Header()
|
header := w.Header()
|
||||||
header.Set("Server", srv.HeaderServer)
|
header.Set("Server", srv.HeaderServer)
|
||||||
token := randStringBytes(16)
|
token := RandomString(16)
|
||||||
expiration := time.Now().AddDate(0, 0, 3)
|
expiration := time.Now().AddDate(0, 0, 3)
|
||||||
cookie := http.Cookie{Name: srv.TokenCookieA, Value: token, Expires: expiration}
|
cookie := http.Cookie{Name: srv.TokenCookieA, Value: token, Expires: expiration}
|
||||||
http.SetCookie(w, &cookie)
|
http.SetCookie(w, &cookie)
|
||||||
srv.regToken(token)
|
srv.regToken(token)
|
||||||
|
|
||||||
srv.HttpHandler.ServeHTTP(w, r)
|
srv.HttpHandler.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) handleWs(w http.ResponseWriter, r *http.Request, token string, flag string, cc *state) {
|
func (srv *Server) handleWs(w http.ResponseWriter, r *http.Request, token string, flag string, cc *state) {
|
||||||
|
|
||||||
ip := r.Header.Get("Cf-Connecting-Ip")
|
ip := r.Header.Get("Cf-Connecting-Ip")
|
||||||
|
|
||||||
hj, ok := w.(http.Hijacker)
|
hj, ok := w.(http.Hijacker)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, bufRW, err := hj.Hijack()
|
conn, bufRW, err := hj.Hijack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bufRW.Flush()
|
bufRW.Flush()
|
||||||
|
|
||||||
conn.Write([]byte("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " + token + "\r\n\r\n"))
|
conn.Write([]byte("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " + token + "\r\n\r\n"))
|
||||||
|
|
||||||
cc.mx.Lock()
|
cc.mx.Lock()
|
||||||
defer cc.mx.Unlock()
|
defer cc.mx.Unlock()
|
||||||
if r.Method == srv.RxMethod && flag == srv.RxFlag {
|
if r.Method == srv.RxMethod && flag == srv.RxFlag {
|
||||||
|
|
||||||
srv.rmToken(token)
|
srv.rmToken(token)
|
||||||
srv.accepts <- mkConnAddr(conn, ip)
|
srv.accepts <- mkConnAddr(conn, ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) handleNonWs(w http.ResponseWriter, r *http.Request, token string, flag string, cc *state) {
|
func (srv *Server) handleNonWs(w http.ResponseWriter, r *http.Request, token string, flag string, cc *state) {
|
||||||
@ -224,19 +200,15 @@ func (srv *Server) handleNonWs(w http.ResponseWriter, r *http.Request, token str
|
|||||||
header.Set("Cache-Control", "private, no-store, no-cache, max-age=0")
|
header.Set("Cache-Control", "private, no-store, no-cache, max-age=0")
|
||||||
header.Set("Content-Encoding", "gzip")
|
header.Set("Content-Encoding", "gzip")
|
||||||
flusher.Flush()
|
flusher.Flush()
|
||||||
|
|
||||||
hj, ok := w.(http.Hijacker)
|
hj, ok := w.(http.Hijacker)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, bufRW, err := hj.Hijack()
|
conn, bufRW, err := hj.Hijack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bufRW.Flush()
|
bufRW.Flush()
|
||||||
|
|
||||||
cc.mx.Lock()
|
cc.mx.Lock()
|
||||||
defer cc.mx.Unlock()
|
defer cc.mx.Unlock()
|
||||||
if r.Method == srv.RxMethod && flag == srv.RxFlag {
|
if r.Method == srv.RxMethod && flag == srv.RxFlag {
|
||||||
@ -254,25 +226,22 @@ func (srv *Server) handleNonWs(w http.ResponseWriter, r *http.Request, token str
|
|||||||
cc.bufR.Reader.Read(buf[:n])
|
cc.bufR.Reader.Read(buf[:n])
|
||||||
srv.accepts <- mkConn(cc.connR, cc.connW, buf[:n])
|
srv.accepts <- mkConn(cc.connR, cc.connW, buf[:n])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) regToken(token string) {
|
func (srv *Server) regToken(token string) {
|
||||||
srv.mx.Lock()
|
srv.mx.Lock()
|
||||||
defer srv.mx.Unlock()
|
defer srv.mx.Unlock()
|
||||||
|
|
||||||
_, ok := srv.states[token]
|
_, ok := srv.states[token]
|
||||||
if ok {
|
if ok {
|
||||||
|
|
||||||
}
|
}
|
||||||
srv.states[token] = &state{
|
srv.states[token] = &state{
|
||||||
ttl: time.Now().Add(srv.TokenTTL),
|
ttl: time.Now().Add(srv.TokenTTL),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) checkToken(token string) (*state, bool) {
|
func (srv *Server) checkToken(token string) (*state, bool) {
|
||||||
srv.mx.Lock()
|
srv.mx.Lock()
|
||||||
defer srv.mx.Unlock()
|
defer srv.mx.Unlock()
|
||||||
|
|
||||||
c, ok := srv.states[token]
|
c, ok := srv.states[token]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
@ -283,17 +252,15 @@ func (srv *Server) checkToken(token string) (*state, bool) {
|
|||||||
}
|
}
|
||||||
return c, true
|
return c, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) rmToken(token string) {
|
func (srv *Server) rmToken(token string) {
|
||||||
srv.mx.Lock()
|
srv.mx.Lock()
|
||||||
defer srv.mx.Unlock()
|
defer srv.mx.Unlock()
|
||||||
|
|
||||||
_, ok := srv.states[token]
|
_, ok := srv.states[token]
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(srv.states, token)
|
delete(srv.states, token)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,9 +273,7 @@ func (srv *Server) tokenCleaner() {
|
|||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
}
|
}
|
||||||
|
|
||||||
list := make([]*state, 0)
|
list := make([]*state, 0)
|
||||||
|
|
||||||
srv.mx.Lock()
|
srv.mx.Lock()
|
||||||
for idx, c := range srv.states {
|
for idx, c := range srv.states {
|
||||||
if time.Now().After(c.ttl) {
|
if time.Now().After(c.ttl) {
|
||||||
@ -318,8 +283,6 @@ func (srv *Server) tokenCleaner() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
srv.mx.Unlock()
|
srv.mx.Unlock()
|
||||||
|
|
||||||
// check and close half open connection
|
|
||||||
for _, cc := range list {
|
for _, cc := range list {
|
||||||
cc.mx.Lock()
|
cc.mx.Lock()
|
||||||
if cc.connR == nil && cc.connW != nil {
|
if cc.connR == nil && cc.connW != nil {
|
||||||
|
83
h1/utils.go
Normal file
83
h1/utils.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package h1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hash/fnv"
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
var letter = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||||
|
|
||||||
|
func RandomString(n int) string {
|
||||||
|
b := make([]rune, n)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = letter[rand.Intn(len(letter))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandomStringByInt64(n int, seed int64) string {
|
||||||
|
r := rand.New(rand.NewSource(seed))
|
||||||
|
b := make([]rune, n)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = letter[r.Intn(len(letter))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func String2Int64(s string) int64 {
|
||||||
|
h := fnv.New32a()
|
||||||
|
_, err := h.Write([]byte(s))
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int64(h.Sum32())
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandomStringByStringNonce(n int, seed string, nonce int64) string {
|
||||||
|
return RandomStringByInt64(n, String2Int64(seed)*nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandomStringByString(n int, seed string) string {
|
||||||
|
return RandomStringByInt64(n, String2Int64(seed))
|
||||||
|
}
|
||||||
|
|
||||||
|
var userAgentList = []string{
|
||||||
|
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;",
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)",
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv,2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||||
|
"Mozilla/5.0 (Windows NT 6.1; rv,2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||||
|
"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11",
|
||||||
|
"Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X…TML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
|
||||||
|
"Mozilla/5.0 (Macintosh; U; Intel Mac OS….50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
|
||||||
|
"Mozilla/5.0 (Windows; U; Windows NT 6.1….50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)",
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)",
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)",
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)",
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 7.0; Win…etaSr 1.0; .NET CLR 2.0.50727; SE 2.X MetaSr 1.0)",
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)",
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Avant Browser)",
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)",
|
||||||
|
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 4… Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
|
||||||
|
"Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3… Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
|
||||||
|
"Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like… Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 2.3.7; …HTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||||
|
"MQQBrowser/26 Mozilla/5.0 (Linux; U; A…HTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||||
|
"Opera/9.80 (Android 2.3.4; Linux; Oper…107180945; U; en-GB) Presto/2.8.149 Version/11.10",
|
||||||
|
"Mozilla/5.0 (Linux; U; Android 3.0; en….13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
|
||||||
|
"Mozilla/5.0 (BlackBerry; U; BlackBerry…ike Gecko) Version/6.0.0.337 Mobile Safari/534.1+",
|
||||||
|
"Mozilla/5.0 (hp-tablet; Linux; hpwOS/3…ecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0",
|
||||||
|
"Mozilla/5.0 (SymbianOS/9.4; Series60/5…ebKit/525 (KHTML, like Gecko) BrowserNG/7.1.18124",
|
||||||
|
"Mozilla/5.0 (compatible; MSIE 9.0; Win…ne OS 7.5; Trident/5.0; IEMobile/9.0; HTC; Titan)",
|
||||||
|
"UCWEB7.0.2.37/28/999",
|
||||||
|
"NOKIA5700/ UCWEB7.0.2.37/28/999",
|
||||||
|
"Openwave/ UCWEB7.0.2.37/28/999",
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 6.0; ) Opera/UCWEB7.0.2.37/28/999",
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandomUserAgent(seed string) string {
|
||||||
|
return userAgentList[rand.New(rand.NewSource(String2Int64(seed))).Intn(len(userAgentList))]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user