mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 17:35:40 +08:00
ws: support websocket server mode
This commit is contained in:
parent
13babd9305
commit
beec9d205f
11
README.md
11
README.md
@ -59,7 +59,7 @@ we can set up local listeners as proxy servers, and forward requests to internet
|
|||||||
|tls |√| |√| |transport client & server
|
|tls |√| |√| |transport client & server
|
||||||
|kcp | |√|√| |transport client & server
|
|kcp | |√|√| |transport client & server
|
||||||
|unix |√| |√| |transport client & server
|
|unix |√| |√| |transport client & server
|
||||||
|websocket | | |√| |transport client only
|
|websocket |√| |√| |transport client only
|
||||||
|simple-obfs | | |√| |transport client only
|
|simple-obfs | | |√| |transport client only
|
||||||
|tcptun |√| | | |transport server only
|
|tcptun |√| | | |transport server only
|
||||||
|udptun | |√| | |transport server only
|
|udptun | |√| | |transport server only
|
||||||
@ -88,7 +88,7 @@ glider -h
|
|||||||
<summary>click to see details</summary>
|
<summary>click to see details</summary>
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
glider 0.12.0 usage:
|
glider 0.12.1 usage:
|
||||||
-checkdisabledonly
|
-checkdisabledonly
|
||||||
check disabled fowarders only
|
check disabled fowarders only
|
||||||
-checkinterval int
|
-checkinterval int
|
||||||
@ -143,7 +143,7 @@ glider 0.12.0 usage:
|
|||||||
verbose mode
|
verbose mode
|
||||||
|
|
||||||
Available schemes:
|
Available schemes:
|
||||||
listen: mixed ss socks5 http vless trojan trojanc redir redir6 tcptun udptun tls unix kcp
|
listen: mixed ss socks5 http vless trojan trojanc redir redir6 tcptun udptun tls ws unix kcp
|
||||||
forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tls ws unix kcp simple-obfs
|
forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tls ws unix kcp simple-obfs
|
||||||
|
|
||||||
Socks5 scheme:
|
Socks5 scheme:
|
||||||
@ -202,9 +202,12 @@ Proxy over tls server:
|
|||||||
tls://host:port?cert=PATH&key=PATH,socks5://
|
tls://host:port?cert=PATH&key=PATH,socks5://
|
||||||
tls://host:port?cert=PATH&key=PATH,ss://method:pass@
|
tls://host:port?cert=PATH&key=PATH,ss://method:pass@
|
||||||
|
|
||||||
Websocket scheme:
|
Websocket client scheme:
|
||||||
ws://host:port[/path][?host=HOST]
|
ws://host:port[/path][?host=HOST]
|
||||||
|
|
||||||
|
Websocket server scheme:
|
||||||
|
ws://:port[/path]?host=HOST
|
||||||
|
|
||||||
Websocket with a specified proxy protocol:
|
Websocket with a specified proxy protocol:
|
||||||
ws://host:port[/path][?host=HOST],scheme://
|
ws://host:port[/path][?host=HOST],scheme://
|
||||||
ws://host:port[/path][?host=HOST],http://[user:pass@]
|
ws://host:port[/path][?host=HOST],http://[user:pass@]
|
||||||
|
@ -131,7 +131,7 @@ func usage() {
|
|||||||
fmt.Fprintf(w, "\n")
|
fmt.Fprintf(w, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(w, "Available schemes:\n")
|
fmt.Fprintf(w, "Available schemes:\n")
|
||||||
fmt.Fprintf(w, " listen: mixed ss socks5 http vless trojan trojanc redir redir6 tcptun udptun tls unix kcp\n")
|
fmt.Fprintf(w, " listen: mixed ss socks5 http vless trojan trojanc redir redir6 tcptun udptun tls ws unix kcp\n")
|
||||||
fmt.Fprintf(w, " forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tls ws unix kcp simple-obfs\n")
|
fmt.Fprintf(w, " forward: reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tls ws unix kcp simple-obfs\n")
|
||||||
fmt.Fprintf(w, "\n")
|
fmt.Fprintf(w, "\n")
|
||||||
|
|
||||||
@ -205,10 +205,14 @@ func usage() {
|
|||||||
fmt.Fprintf(w, " tls://host:port?cert=PATH&key=PATH,ss://method:pass@\n")
|
fmt.Fprintf(w, " tls://host:port?cert=PATH&key=PATH,ss://method:pass@\n")
|
||||||
fmt.Fprintf(w, "\n")
|
fmt.Fprintf(w, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(w, "Websocket scheme:\n")
|
fmt.Fprintf(w, "Websocket client scheme:\n")
|
||||||
fmt.Fprintf(w, " ws://host:port[/path][?host=HOST]\n")
|
fmt.Fprintf(w, " ws://host:port[/path][?host=HOST]\n")
|
||||||
fmt.Fprintf(w, "\n")
|
fmt.Fprintf(w, "\n")
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "Websocket server scheme:\n")
|
||||||
|
fmt.Fprintf(w, " ws://:port[/path]?host=HOST\n")
|
||||||
|
fmt.Fprintf(w, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(w, "Websocket with a specified proxy protocol:\n")
|
fmt.Fprintf(w, "Websocket with a specified proxy protocol:\n")
|
||||||
fmt.Fprintf(w, " ws://host:port[/path][?host=HOST],scheme://\n")
|
fmt.Fprintf(w, " ws://host:port[/path][?host=HOST],scheme://\n")
|
||||||
fmt.Fprintf(w, " ws://host:port[/path][?host=HOST],http://[user:pass@]\n")
|
fmt.Fprintf(w, " ws://host:port[/path][?host=HOST],http://[user:pass@]\n")
|
||||||
|
@ -75,6 +75,9 @@ listen=socks5://:1080
|
|||||||
# vless over tls server
|
# vless over tls server
|
||||||
# listen=tls://:1234?cert=/path/to/cert&key=/path/to/key,vless://UUID@?fallback=127.0.0.1:80
|
# listen=tls://:1234?cert=/path/to/cert&key=/path/to/key,vless://UUID@?fallback=127.0.0.1:80
|
||||||
|
|
||||||
|
# vless over ws
|
||||||
|
# listen=ws://:1234/path?host=domain.com,vless://707f20ea-d4b8-4d1d-8e2e-2c86cb2ed97a@?fallback=127.0.0.1:80
|
||||||
|
|
||||||
# trojan server
|
# trojan server
|
||||||
# listen=trojan://PASSWORD:1234?cert=/path/to/cert&key=/path/to/key&fallback=127.0.0.1
|
# listen=trojan://PASSWORD:1234?cert=/path/to/cert&key=/path/to/key&fallback=127.0.0.1
|
||||||
|
|
||||||
|
8
go.mod
8
go.mod
@ -10,10 +10,10 @@ require (
|
|||||||
github.com/nadoo/ipset v0.3.0
|
github.com/nadoo/ipset v0.3.0
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||||
github.com/xtaci/kcp-go/v5 v5.6.1
|
github.com/xtaci/kcp-go/v5 v5.6.1
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb // indirect
|
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 // indirect
|
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 // indirect
|
||||||
golang.org/x/tools v0.0.0-20201014231627-1610a49f37af // indirect
|
golang.org/x/tools v0.0.0-20201017001424-6003fad69a88 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
16
go.sum
16
go.sum
@ -118,8 +118,8 @@ golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/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 h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI=
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||||
@ -141,8 +141,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgN
|
|||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY=
|
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0 h1:5kGOVHlq0euqwzgTC9Vu15p6fV1Wi0ArVi8da2urnVg=
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/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-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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -167,8 +167,8 @@ golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/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 h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88=
|
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 h1:5jaG59Zhd+8ZXe8C+lgiAGqkOaZBruqrWclLkgAww34=
|
||||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@ -177,8 +177,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
|||||||
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU=
|
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU=
|
||||||
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
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-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20201014231627-1610a49f37af h1:VIUWFyOgzG3c0t9KYop5Ybp4m56LupfOnFYX7Ipnz+I=
|
golang.org/x/tools v0.0.0-20201017001424-6003fad69a88 h1:ZB1XYzdDo7c/O48jzjMkvIjnC120Z9/CwgDWhePjQdQ=
|
||||||
golang.org/x/tools v0.0.0-20201014231627-1610a49f37af/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
golang.org/x/tools v0.0.0-20201017001424-6003fad69a88/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
2
main.go
2
main.go
@ -18,7 +18,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "0.12.0"
|
version = "0.12.1"
|
||||||
config = parseConfig()
|
config = parseConfig()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,50 +2,62 @@ package ws
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/nadoo/glider/pool"
|
"github.com/nadoo/glider/pool"
|
||||||
|
"github.com/nadoo/glider/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
func init() {
|
||||||
|
proxy.RegisterDialer("ws", NewWSDialer)
|
||||||
// Client is ws client struct.
|
|
||||||
type Client struct {
|
|
||||||
host string
|
|
||||||
path string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conn is a connection to ws server.
|
// NewWSDialer returns a ws proxy dialer.
|
||||||
type Conn struct {
|
func NewWSDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
|
||||||
|
return NewWS(s, d, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr returns forwarder's address.
|
||||||
|
func (s *WS) Addr() string {
|
||||||
|
if s.addr == "" {
|
||||||
|
return s.dialer.Addr()
|
||||||
|
}
|
||||||
|
return s.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial connects to the address addr on the network net via the proxy.
|
||||||
|
func (s *WS) Dial(network, addr string) (net.Conn, error) {
|
||||||
|
rc, err := s.dialer.Dial("tcp", s.addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.NewClientConn(rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialUDP connects to the given address via the proxy.
|
||||||
|
func (s *WS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||||
|
return nil, nil, errors.New("[ws] ws client does not support udp now")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientConn is a connection to ws server.
|
||||||
|
type ClientConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
writer io.Writer
|
writer io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates a new ws client.
|
// NewClientConn creates a new ws client connection.
|
||||||
func NewClient(host, path string) (*Client, error) {
|
func (s *WS) NewClientConn(rc net.Conn) (*ClientConn, error) {
|
||||||
if path == "" {
|
conn := &ClientConn{Conn: rc}
|
||||||
path = "/"
|
return conn, conn.Handshake(s.host, s.path)
|
||||||
}
|
|
||||||
c := &Client{host: host, path: path}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewConn creates a new ws client connection.
|
|
||||||
func (c *Client) NewConn(rc net.Conn, target string) (*Conn, error) {
|
|
||||||
conn := &Conn{Conn: rc}
|
|
||||||
return conn, conn.Handshake(c.host, c.path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handshake handshakes with the server using HTTP to request a protocol upgrade.
|
// Handshake handshakes with the server using HTTP to request a protocol upgrade.
|
||||||
func (c *Conn) Handshake(host, path string) error {
|
func (c *ClientConn) Handshake(host, path string) error {
|
||||||
clientKey := generateClientKey()
|
clientKey := generateClientKey()
|
||||||
|
|
||||||
buf := pool.GetWriteBuffer()
|
buf := pool.GetWriteBuffer()
|
||||||
@ -89,42 +101,16 @@ func (c *Conn) Handshake(host, path string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) Write(b []byte) (n int, err error) {
|
func (c *ClientConn) Write(b []byte) (n int, err error) {
|
||||||
if c.writer == nil {
|
if c.writer == nil {
|
||||||
c.writer = FrameWriter(c.Conn)
|
c.writer = FrameWriter(c.Conn, true)
|
||||||
}
|
}
|
||||||
return c.writer.Write(b)
|
return c.writer.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) Read(b []byte) (n int, err error) {
|
func (c *ClientConn) Read(b []byte) (n int, err error) {
|
||||||
if c.reader == nil {
|
if c.reader == nil {
|
||||||
c.reader = FrameReader(c.Conn)
|
c.reader = FrameReader(c.Conn, true)
|
||||||
}
|
}
|
||||||
return c.reader.Read(b)
|
return c.reader.Read(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseFirstLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts.
|
|
||||||
// TODO: move to separate http lib package for reuse(also for http proxy module)
|
|
||||||
func parseFirstLine(line string) (r1, r2, r3 string, ok bool) {
|
|
||||||
s1 := strings.Index(line, " ")
|
|
||||||
s2 := strings.Index(line[s1+1:], " ")
|
|
||||||
if s1 < 0 || s2 < 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s2 += s1 + 1
|
|
||||||
return line[:s1], line[s1+1 : s2], line[s2+1:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateClientKey() string {
|
|
||||||
p := pool.GetBuffer(16)
|
|
||||||
defer pool.PutBuffer(p)
|
|
||||||
rand.Read(p)
|
|
||||||
return base64.StdEncoding.EncodeToString(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func computeServerKey(clientKey string) string {
|
|
||||||
h := sha1.New()
|
|
||||||
h.Write([]byte(clientKey))
|
|
||||||
h.Write(keyGUID)
|
|
||||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
|
||||||
}
|
|
||||||
|
@ -30,28 +30,32 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultFrameSize = 4096
|
defaultFrameSize = 4096
|
||||||
maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask
|
maxHeaderSize = 2 + 8 + 4 // Fixed header + length + mask
|
||||||
maskKeyLen = 4
|
|
||||||
|
|
||||||
|
// byte 0
|
||||||
finalBit byte = 1 << 7
|
finalBit byte = 1 << 7
|
||||||
maskBit byte = 1 << 7
|
|
||||||
opCodeBinary byte = 2
|
opCodeBinary byte = 2
|
||||||
|
|
||||||
|
// byte 1
|
||||||
|
maskBit byte = 1 << 7
|
||||||
)
|
)
|
||||||
|
|
||||||
type frameWriter struct {
|
type frameWriter struct {
|
||||||
io.Writer
|
io.Writer
|
||||||
buf []byte
|
buf []byte
|
||||||
maskKey [maskKeyLen]byte
|
client bool
|
||||||
|
maskKey [4]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// FrameWriter returns a frame writer.
|
// FrameWriter returns a frame writer.
|
||||||
func FrameWriter(w io.Writer) io.Writer {
|
func FrameWriter(w io.Writer, client bool) io.Writer {
|
||||||
n := rand.Uint32()
|
n := rand.Uint32()
|
||||||
return &frameWriter{
|
return &frameWriter{
|
||||||
Writer: w,
|
Writer: w,
|
||||||
buf: make([]byte, maxFrameHeaderSize+defaultFrameSize),
|
buf: make([]byte, maxHeaderSize+defaultFrameSize),
|
||||||
maskKey: [...]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)},
|
client: client,
|
||||||
|
maskKey: [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,13 +67,18 @@ func (w *frameWriter) Write(b []byte) (int, error) {
|
|||||||
func (w *frameWriter) ReadFrom(r io.Reader) (n int64, err error) {
|
func (w *frameWriter) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
for {
|
for {
|
||||||
buf := w.buf
|
buf := w.buf
|
||||||
payloadBuf := buf[maxFrameHeaderSize:]
|
payloadBuf := buf[maxHeaderSize:]
|
||||||
|
|
||||||
nr, er := r.Read(payloadBuf)
|
nr, er := r.Read(payloadBuf)
|
||||||
|
|
||||||
if nr > 0 {
|
if nr > 0 {
|
||||||
n += int64(nr)
|
n += int64(nr)
|
||||||
buf[0] = finalBit | opCodeBinary
|
buf[0] = opCodeBinary
|
||||||
buf[1] = maskBit
|
buf[1] = 0
|
||||||
|
if w.client {
|
||||||
|
buf[0] |= finalBit
|
||||||
|
buf[1] = maskBit
|
||||||
|
}
|
||||||
|
|
||||||
lengthFieldLen := 0
|
lengthFieldLen := 0
|
||||||
switch {
|
switch {
|
||||||
@ -92,17 +101,20 @@ func (w *frameWriter) ReadFrom(r io.Reader) (n int64, err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// maskkey
|
|
||||||
_, ew = w.Writer.Write(w.maskKey[:])
|
|
||||||
if ew != nil {
|
|
||||||
err = ew
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// payload
|
|
||||||
payloadBuf = payloadBuf[:nr]
|
payloadBuf = payloadBuf[:nr]
|
||||||
for i := range payloadBuf {
|
|
||||||
payloadBuf[i] = payloadBuf[i] ^ w.maskKey[i%4]
|
if w.client {
|
||||||
|
// maskkey
|
||||||
|
_, ew = w.Writer.Write(w.maskKey[:])
|
||||||
|
if ew != nil {
|
||||||
|
err = ew
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// payload mask
|
||||||
|
for i := range payloadBuf {
|
||||||
|
payloadBuf[i] = payloadBuf[i] ^ w.maskKey[i%4]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ew = w.Writer.Write(payloadBuf)
|
_, ew = w.Writer.Write(payloadBuf)
|
||||||
@ -126,17 +138,21 @@ func (w *frameWriter) ReadFrom(r io.Reader) (n int64, err error) {
|
|||||||
|
|
||||||
type frameReader struct {
|
type frameReader struct {
|
||||||
io.Reader
|
io.Reader
|
||||||
buf [8]byte
|
buf [8]byte
|
||||||
left int64
|
left int64
|
||||||
|
server bool
|
||||||
|
maskKey [4]byte
|
||||||
|
maskOffset int
|
||||||
}
|
}
|
||||||
|
|
||||||
// FrameReader returns a chunked reader.
|
// FrameReader returns a chunked reader.
|
||||||
func FrameReader(r io.Reader) io.Reader {
|
func FrameReader(r io.Reader, client bool) io.Reader {
|
||||||
return &frameReader{Reader: r}
|
return &frameReader{Reader: r, server: !client}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *frameReader) Read(b []byte) (int, error) {
|
func (r *frameReader) Read(b []byte) (int, error) {
|
||||||
if r.left == 0 {
|
if r.left == 0 {
|
||||||
|
|
||||||
// get msg header
|
// get msg header
|
||||||
_, err := io.ReadFull(r.Reader, r.buf[:2])
|
_, err := io.ReadFull(r.Reader, r.buf[:2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -145,7 +161,8 @@ func (r *frameReader) Read(b []byte) (int, error) {
|
|||||||
|
|
||||||
// final := r.buf[0]&finalBit != 0
|
// final := r.buf[0]&finalBit != 0
|
||||||
// frameType := int(r.buf[0] & 0xf)
|
// frameType := int(r.buf[0] & 0xf)
|
||||||
// mask := r.buf[1]&maskBit != 0
|
// r.mask = r.buf[1]&maskBit != 0
|
||||||
|
|
||||||
r.left = int64(r.buf[1] & 0x7f)
|
r.left = int64(r.buf[1] & 0x7f)
|
||||||
switch r.left {
|
switch r.left {
|
||||||
case 126:
|
case 126:
|
||||||
@ -161,6 +178,14 @@ func (r *frameReader) Read(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
r.left = int64(binary.BigEndian.Uint64(r.buf[:8]))
|
r.left = int64(binary.BigEndian.Uint64(r.buf[:8]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.server {
|
||||||
|
_, err := io.ReadFull(r.Reader, r.maskKey[:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
r.maskOffset = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readLen := int64(len(b))
|
readLen := int64(len(b))
|
||||||
@ -173,6 +198,13 @@ func (r *frameReader) Read(b []byte) (int, error) {
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.server {
|
||||||
|
for i := range b[:m] {
|
||||||
|
b[i] = b[i] ^ r.maskKey[(i+r.maskOffset)%4]
|
||||||
|
}
|
||||||
|
r.maskOffset = (m + r.maskOffset) % 4
|
||||||
|
}
|
||||||
|
|
||||||
r.left -= int64(m)
|
r.left -= int64(m)
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
|
144
proxy/ws/server.go
Normal file
144
proxy/ws/server.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
package ws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/textproto"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/nadoo/glider/log"
|
||||||
|
"github.com/nadoo/glider/pool"
|
||||||
|
"github.com/nadoo/glider/proxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proxy.RegisterServer("ws", NewWSServer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWSServer returns a ws transport server.
|
||||||
|
func NewWSServer(s string, p proxy.Proxy) (proxy.Server, error) {
|
||||||
|
transport := strings.Split(s, ",")
|
||||||
|
|
||||||
|
// prepare transport listener
|
||||||
|
// TODO: check here
|
||||||
|
if len(transport) < 2 {
|
||||||
|
return nil, errors.New("[tls] malformd listener:" + s)
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := NewWS(transport[0], nil, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.server, err = proxy.ServerFromURL(transport[1], p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServe listens on server's addr and serves connections.
|
||||||
|
func (s *WS) ListenAndServe() {
|
||||||
|
l, err := net.Listen("tcp", s.addr)
|
||||||
|
if err != nil {
|
||||||
|
log.F("[ws] failed to listen on %s: %v", s.addr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
log.F("[ws] listening TCP on %s", s.addr)
|
||||||
|
|
||||||
|
for {
|
||||||
|
c, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.F("[ws] failed to accept: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
go s.Serve(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve serves a connection.
|
||||||
|
func (s *WS) Serve(c net.Conn) {
|
||||||
|
// we know the internal server will close the connection after serve
|
||||||
|
// defer c.Close()
|
||||||
|
|
||||||
|
if s.server != nil {
|
||||||
|
sc, err := s.NewServerConn(c)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.server.Serve(sc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerConn is a connection to ws client.
|
||||||
|
type ServerConn struct {
|
||||||
|
net.Conn
|
||||||
|
reader io.Reader
|
||||||
|
writer io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerConn creates a new ws server connection.
|
||||||
|
func (s *WS) NewServerConn(rc net.Conn) (*ServerConn, error) {
|
||||||
|
sc := &ServerConn{Conn: rc}
|
||||||
|
err := sc.Handshake(s.host, s.path)
|
||||||
|
return sc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handshake handshakes with the client.
|
||||||
|
func (c *ServerConn) Handshake(host, path string) error {
|
||||||
|
tpr := textproto.NewReader(bufio.NewReader(c.Conn))
|
||||||
|
line, err := tpr.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, path, _, ok := parseFirstLine(line)
|
||||||
|
if !ok || path != path {
|
||||||
|
return errors.New("[ws] error in ws handshake parseFirstLine")
|
||||||
|
}
|
||||||
|
|
||||||
|
reqHeader, err := tpr.ReadMIMEHeader()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqHeader.Get("Host") != host {
|
||||||
|
return errors.New("[ws] got wrong host")
|
||||||
|
}
|
||||||
|
|
||||||
|
clientKey := reqHeader.Get("Sec-WebSocket-Key")
|
||||||
|
serverKey := computeServerKey(clientKey)
|
||||||
|
|
||||||
|
buf := pool.GetWriteBuffer()
|
||||||
|
defer pool.PutWriteBuffer(buf)
|
||||||
|
|
||||||
|
buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
|
||||||
|
buf.WriteString("Upgrade: websocket\r\n")
|
||||||
|
buf.WriteString("Connection: Upgrade\r\n")
|
||||||
|
buf.WriteString("Sec-WebSocket-Accept: " + serverKey + "\r\n")
|
||||||
|
buf.WriteString("Sec-WebSocket-Protocol: binary\r\n")
|
||||||
|
buf.WriteString(("\r\n"))
|
||||||
|
|
||||||
|
_, err = c.Conn.Write(buf.Bytes())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) Write(b []byte) (n int, err error) {
|
||||||
|
if c.writer == nil {
|
||||||
|
c.writer = FrameWriter(c.Conn, false)
|
||||||
|
}
|
||||||
|
return c.writer.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) Read(b []byte) (n int, err error) {
|
||||||
|
if c.reader == nil {
|
||||||
|
c.reader = FrameReader(c.Conn, false)
|
||||||
|
}
|
||||||
|
return c.reader.Read(b)
|
||||||
|
}
|
@ -2,29 +2,33 @@
|
|||||||
package ws
|
package ws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"crypto/rand"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/nadoo/glider/log"
|
"github.com/nadoo/glider/log"
|
||||||
|
"github.com/nadoo/glider/pool"
|
||||||
"github.com/nadoo/glider/proxy"
|
"github.com/nadoo/glider/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||||
|
|
||||||
// WS is the base ws proxy struct.
|
// WS is the base ws proxy struct.
|
||||||
type WS struct {
|
type WS struct {
|
||||||
dialer proxy.Dialer
|
dialer proxy.Dialer
|
||||||
|
proxy proxy.Proxy
|
||||||
addr string
|
addr string
|
||||||
host string
|
host string
|
||||||
|
path string
|
||||||
|
|
||||||
client *Client
|
server proxy.Server
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proxy.RegisterDialer("ws", NewWSDialer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWS returns a websocket proxy.
|
// NewWS returns a websocket proxy.
|
||||||
func NewWS(s string, d proxy.Dialer) (*WS, error) {
|
func NewWS(s string, d proxy.Dialer, p proxy.Proxy) (*WS, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[ws] parse url err: %s", err)
|
log.F("[ws] parse url err: %s", err)
|
||||||
@ -38,49 +42,47 @@ func NewWS(s string, d proxy.Dialer) (*WS, error) {
|
|||||||
addr = d.Addr()
|
addr = d.Addr()
|
||||||
}
|
}
|
||||||
|
|
||||||
p := &WS{
|
w := &WS{
|
||||||
dialer: d,
|
dialer: d,
|
||||||
|
proxy: p,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
host: u.Query().Get("host"),
|
host: u.Query().Get("host"),
|
||||||
|
path: u.Path,
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.host == "" {
|
if w.host == "" {
|
||||||
p.host, _, _ = net.SplitHostPort(addr)
|
w.host, _, _ = net.SplitHostPort(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.client, err = NewClient(p.host, u.Path)
|
if w.path == "" {
|
||||||
if err != nil {
|
w.path = "/"
|
||||||
log.F("[ws] create ws client error: %s", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return p, nil
|
return w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWSDialer returns a ws proxy dialer.
|
// parseFirstLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts.
|
||||||
func NewWSDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
|
// TODO: move to separate http lib package for reuse(also for http proxy module)
|
||||||
return NewWS(s, d)
|
func parseFirstLine(line string) (r1, r2, r3 string, ok bool) {
|
||||||
}
|
s1 := strings.Index(line, " ")
|
||||||
|
s2 := strings.Index(line[s1+1:], " ")
|
||||||
// Addr returns forwarder's address.
|
if s1 < 0 || s2 < 0 {
|
||||||
func (s *WS) Addr() string {
|
return
|
||||||
if s.addr == "" {
|
|
||||||
return s.dialer.Addr()
|
|
||||||
}
|
}
|
||||||
return s.addr
|
s2 += s1 + 1
|
||||||
|
return line[:s1], line[s1+1 : s2], line[s2+1:], true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dial connects to the address addr on the network net via the proxy.
|
func generateClientKey() string {
|
||||||
func (s *WS) Dial(network, addr string) (net.Conn, error) {
|
p := pool.GetBuffer(16)
|
||||||
rc, err := s.dialer.Dial("tcp", s.addr)
|
defer pool.PutBuffer(p)
|
||||||
if err != nil {
|
rand.Read(p)
|
||||||
return nil, err
|
return base64.StdEncoding.EncodeToString(p)
|
||||||
}
|
|
||||||
|
|
||||||
return s.client.NewConn(rc, addr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialUDP connects to the given address via the proxy.
|
func computeServerKey(clientKey string) string {
|
||||||
func (s *WS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
h := sha1.New()
|
||||||
return nil, nil, errors.New("[ws] ws client does not support udp now")
|
h.Write([]byte(clientKey))
|
||||||
|
h.Write(keyGUID)
|
||||||
|
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||||
}
|
}
|
||||||
|
@ -236,7 +236,7 @@ func checkWebSite(fwdr *Forwarder, website string, timeout time.Duration, buf []
|
|||||||
rc.SetDeadline(time.Now().Add(timeout))
|
rc.SetDeadline(time.Now().Add(timeout))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = io.WriteString(rc, "GET / HTTP/1.1\r\n\r\n")
|
_, err = io.WriteString(rc, "GET / HTTP/1.1\r\nHost:"+website+"\r\nConnection: close"+"\r\n\r\n")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[check] %s(%d) -> %s, FAILED. error in write: %s", fwdr.Addr(), fwdr.Priority(), website, err)
|
log.F("[check] %s(%d) -> %s, FAILED. error in write: %s", fwdr.Addr(), fwdr.Priority(), website, err)
|
||||||
fwdr.Disable()
|
fwdr.Disable()
|
||||||
|
Loading…
Reference in New Issue
Block a user