[ Cmii ] [ Octopus ] - add all image tag back up; add socks5 proxy
This commit is contained in:
@@ -1,28 +1,5 @@
|
||||
package cmii_operator
|
||||
|
||||
var CmiiFrontendAppName = []string{
|
||||
"cmii-uav-platform",
|
||||
"cmii-uav-platform-oms",
|
||||
"cmii-uav-platform-mws",
|
||||
"cmii-uav-platform-cms-portal",
|
||||
"cmii-uav-platform-armypeople",
|
||||
"cmii-uav-platform-share",
|
||||
"cmii-suav-platform-supervisionh5",
|
||||
"cmii-suav-platform-supervision",
|
||||
"cmii-uav-platform-ai-brain",
|
||||
"cmii-uav-platform-base",
|
||||
"cmii-uav-platform-detection",
|
||||
"cmii-uav-platform-emergency-rescue",
|
||||
"cmii-uav-platform-logistics",
|
||||
"cmii-uav-platform-media",
|
||||
"cmii-uav-platform-open",
|
||||
"cmii-uav-platform-security",
|
||||
"cmii-uav-platform-securityh5",
|
||||
"cmii-uav-platform-seniclive",
|
||||
"cmii-uav-platform-threedsimulation",
|
||||
"cmii-uav-platform-visualization",
|
||||
}
|
||||
|
||||
var CmiiBackendAppMap = map[string]string{
|
||||
"cmii-admin-data": "5.2.0",
|
||||
"cmii-admin-gateway": "5.2.0",
|
||||
@@ -43,10 +20,6 @@ var CmiiBackendAppMap = map[string]string{
|
||||
"cmii-uav-device": "5.2.0",
|
||||
"cmii-uav-emergency": "5.2.0",
|
||||
"cmii-uav-gateway": "5.2.0",
|
||||
"cmii-uav-gis-server": "5.2.0",
|
||||
"cmii-uav-grid-datasource": "5.2.0-24810",
|
||||
"cmii-uav-grid-engine": "5.1.0",
|
||||
"cmii-uav-grid-manage": "5.1.0",
|
||||
"cmii-uav-industrial-portfolio": "5.2.0-25268-10",
|
||||
"cmii-uav-integration": "5.2.0-25447",
|
||||
"cmii-uav-kpi-monitor": "5.2.0",
|
||||
@@ -101,56 +74,17 @@ var CmiiMiddlewareNameMap = map[string]string{
|
||||
"helm-rabbitmq": "single",
|
||||
}
|
||||
|
||||
var CmiiBackendAppName = []string{
|
||||
"cmii-uav-gateway",
|
||||
"cmii-uav-oauth",
|
||||
"cmii-uav-user",
|
||||
"cmii-uav-material-warehouse",
|
||||
"cmii-uav-device",
|
||||
"cmii-uav-mission",
|
||||
"cmii-uav-mqtthandler",
|
||||
"cmii-uav-surveillance",
|
||||
"cmii-uav-waypoint",
|
||||
"cmii-uav-airspace",
|
||||
"cmii-uav-industrial-portfolio",
|
||||
"cmii-uav-integration",
|
||||
"cmii-admin-data",
|
||||
"cmii-admin-gateway",
|
||||
"cmii-admin-user",
|
||||
"cmii-uav-cloud-live",
|
||||
"cmii-uav-emergency",
|
||||
"cmii-uav-cms",
|
||||
"cmii-open-gateway",
|
||||
"cmii-uav-cloud-live",
|
||||
"cmii-uav-alarm",
|
||||
"cmii-uav-brain",
|
||||
"cmii-app-release",
|
||||
"cmii-uav-notice",
|
||||
"cmii-suav-supervision",
|
||||
"cmii-uav-autowaypoint",
|
||||
"cmii-uav-data-post-process",
|
||||
"cmii-uav-depotautoreturn",
|
||||
"cmii-uav-developer",
|
||||
"cmii-uav-kpi-monitor",
|
||||
"cmii-uav-logger",
|
||||
"cmii-uav-process",
|
||||
"cmii-uav-threedsimulation",
|
||||
"cmii-uav-tower",
|
||||
var CmiiSrsAppMap = map[string]string{
|
||||
"helm-live-op-v2": "deployment",
|
||||
"helm-live-rtsp-op": "4.1.6",
|
||||
"helm-live-srs-rtc": "statefulset",
|
||||
}
|
||||
|
||||
var CmiiStreamAppName = []string{
|
||||
"cmii-uav-cloud-live",
|
||||
"helm-live-op-v2",
|
||||
"helm-live-rtsp-op",
|
||||
"helm-live-rtsp-zlm",
|
||||
"helm-vms-deploy",
|
||||
}
|
||||
|
||||
var CmiiGISAppName = []string{
|
||||
"cmii-uav-gis-server",
|
||||
"cmii-uav-grid-datasource",
|
||||
"cmii-uav-grid-engine",
|
||||
"cmii-uav-grid-manage",
|
||||
var CmiiGISAppMap = map[string]string{
|
||||
"cmii-uav-gis-server": "5.4.0",
|
||||
"cmii-uav-grid-datasource": "5.4.0",
|
||||
"cmii-uav-grid-engine": "5.4.0",
|
||||
"cmii-uav-grid-manage": "5.4.0",
|
||||
}
|
||||
var CmiiDevK8sConfig = `apiVersion: v1
|
||||
clusters:
|
||||
|
||||
@@ -426,6 +426,74 @@ func BackupAllCmiiDeploymentToMap(cmiiEnv string) (backendMap, frontendMap map[s
|
||||
return backendMap, frontendMap
|
||||
}
|
||||
|
||||
func BackUpAllCmiiAppImageNameFromEnv(cmiiEnv string) {
|
||||
|
||||
CmiiOperator.changeOperatorEnv(cmiiEnv)
|
||||
filePath := "C:\\Users\\wddsh\\Documents\\IdeaProjects\\ProjectOctopus\\cmii_operator\\log\\images-" + CmiiOperator.CurrentNamespace + "-" + utils.TimeSplitFormatString() + ".txt"
|
||||
|
||||
only := make(map[string]string, 150)
|
||||
// front
|
||||
executor.BasicAppendContentToFile("---\n", filePath)
|
||||
for key, value := range CmiiFrontendAppMap {
|
||||
_, ok := only[key]
|
||||
if !ok {
|
||||
deploy := CmiiOperator.DeploymentOneInterface(cmiiEnv, key)
|
||||
if deploy != nil {
|
||||
only[key] = value
|
||||
executor.BasicAppendContentToFile(deploy.Image+"\n", filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
executor.BasicAppendContentToFile("---\n", filePath)
|
||||
for key, value := range CmiiBackendAppMap {
|
||||
_, ok := only[key]
|
||||
if !ok {
|
||||
deploy := CmiiOperator.DeploymentOneInterface(cmiiEnv, key)
|
||||
if deploy != nil {
|
||||
only[key] = value
|
||||
executor.BasicAppendContentToFile(deploy.Image+"\n", filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
// backend
|
||||
executor.BasicAppendContentToFile("---\n", filePath)
|
||||
// gis server
|
||||
for key, value := range CmiiGISAppMap {
|
||||
_, ok := only[key]
|
||||
if !ok {
|
||||
deploy := CmiiOperator.DeploymentOneInterface(cmiiEnv, key)
|
||||
if deploy != nil {
|
||||
only[key] = value
|
||||
executor.BasicAppendContentToFile(deploy.Image+"\n", filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
// srs
|
||||
executor.BasicAppendContentToFile("---\n", filePath)
|
||||
for key, value := range CmiiSrsAppMap {
|
||||
_, ok := only[key]
|
||||
if !ok {
|
||||
var app *CmiiDeploymentInterface
|
||||
if strings.Contains(value, "deployment") {
|
||||
app = CmiiOperator.DeploymentOneInterface(cmiiEnv, key)
|
||||
if app != nil {
|
||||
only[key] = value
|
||||
executor.BasicAppendContentToFile(app.Image+"\n", filePath)
|
||||
}
|
||||
} else if strings.Contains(value, "state") {
|
||||
app = CmiiOperator.StatefulSetOneInterface(cmiiEnv, key)
|
||||
if app != nil {
|
||||
only[key] = value
|
||||
for _, image := range app.ContainerImageMap {
|
||||
executor.BasicAppendContentToFile(image+"\n", filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
executor.BasicAppendContentToFile("---\n", filePath)
|
||||
}
|
||||
|
||||
func FilterAllCmiiAppStrict(source []CmiiDeploymentInterface) (result []CmiiDeploymentInterface) {
|
||||
|
||||
for _, c := range source {
|
||||
|
||||
@@ -50,12 +50,6 @@ func TestFindCmiiMiddlewarePodInterface(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackupAllDeploymentFromEnv(t *testing.T) {
|
||||
|
||||
BackupAllDeploymentFromEnv(devFlight)
|
||||
|
||||
}
|
||||
|
||||
func TestBackupAllCmiiDeploymentToMap(t *testing.T) {
|
||||
backendMap, frontendMap := BackupAllCmiiDeploymentToMap(demo)
|
||||
|
||||
@@ -226,11 +220,21 @@ func TestUpdateCmiiImageTagFromNameTagMap(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestBackupAllDeploymentFromEnv(t *testing.T) {
|
||||
|
||||
BackupAllDeploymentFromEnv(demo)
|
||||
|
||||
}
|
||||
|
||||
func TestBackUpAllCmiiAppImageNameFromEnv(t *testing.T) {
|
||||
BackUpAllCmiiAppImageNameFromEnv(demo)
|
||||
}
|
||||
|
||||
func TestUpdateCmiiDeploymentImageTag(t *testing.T) {
|
||||
|
||||
cmiiEnv := devFlight
|
||||
cmiiEnv := demo
|
||||
appName := "cmii-uav-device"
|
||||
newTag := "5.4.0-26906-01"
|
||||
newTag := "5.4.0-26905"
|
||||
|
||||
tag := UpdateCmiiDeploymentImageTag(cmiiEnv, appName, newTag)
|
||||
assert.Equal(t, tag, true, "update image tag failed !")
|
||||
|
||||
@@ -99,6 +99,40 @@ func (deploy CmiiDeploymentInterface) Convert(deployment v1.Deployment) CmiiDepl
|
||||
return deploy
|
||||
}
|
||||
|
||||
func (deploy CmiiDeploymentInterface) ConvertFromStatefulSet(statefulSet v1.StatefulSet) CmiiDeploymentInterface {
|
||||
containers := statefulSet.Spec.Template.Spec.Containers
|
||||
|
||||
containerImageMap := make(map[string]string, len(containers))
|
||||
if len(containers) > 1 {
|
||||
log.WarnF("[CmiiDeploymentInterface ConvertFromStatefulSet] - statefulSet [%s] [%s] container greater than one !", statefulSet.Namespace, statefulSet.Name)
|
||||
}
|
||||
for _, container := range containers {
|
||||
containerImageMap[container.Name] = container.Image
|
||||
deploy.Image = container.Image
|
||||
deploy.ContainerName = container.Name
|
||||
deploy.ImageTag = strings.Split(container.Image, ":")[1]
|
||||
|
||||
for _, envVar := range container.Env {
|
||||
if strings.HasPrefix(envVar.Name, "GIT_BRANCH") {
|
||||
deploy.GitBranch = envVar.Value
|
||||
}
|
||||
|
||||
if strings.HasPrefix(envVar.Name, "GIT_COMMIT") {
|
||||
deploy.GitCommit = envVar.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deploy.Name = statefulSet.Name
|
||||
deploy.Namespace = statefulSet.Namespace
|
||||
deploy.AvailableReplicas = statefulSet.Status.AvailableReplicas
|
||||
deploy.Replicas = *statefulSet.Spec.Replicas
|
||||
deploy.ContainerImageMap = containerImageMap
|
||||
deploy.StatusOk = statefulSet.Status.AvailableReplicas == *statefulSet.Spec.Replicas
|
||||
|
||||
return deploy
|
||||
}
|
||||
|
||||
func (pod CmiiPodInterface) Convert(podDetail corev1.Pod) CmiiPodInterface {
|
||||
|
||||
containers := podDetail.Spec.Containers
|
||||
|
||||
@@ -330,6 +330,23 @@ func (op *CmiiK8sOperator) DeploymentOneInterface(cmiiEnv, appName string) (depl
|
||||
return &convert
|
||||
}
|
||||
|
||||
func (op *CmiiK8sOperator) StatefulSetOneInterface(cmiiEnv, appName string) (deploy *CmiiDeploymentInterface) {
|
||||
|
||||
op.changeOperatorEnv(cmiiEnv)
|
||||
client := op.CurrentClient
|
||||
deploymentInterface := CmiiDeploymentInterface{}
|
||||
|
||||
statefulSet, err := client.AppsV1().StatefulSets(op.CurrentNamespace).Get(context.TODO(), appName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.ErrorF("[StatefulSetOneInterface] - stateful set [%s] [%s] not exists ! %s", cmiiEnv, appName, err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
convert := deploymentInterface.ConvertFromStatefulSet(*statefulSet)
|
||||
|
||||
return &convert
|
||||
}
|
||||
|
||||
func (op *CmiiK8sOperator) DeploymentScale(cmiiEnv, appName string, scaleCount int32) bool {
|
||||
|
||||
deployment := op.DeploymentOneInterface(cmiiEnv, appName)
|
||||
|
||||
@@ -27,3 +27,4 @@
|
||||
2024-02-23-09-31-21 uavcloud-demo cmii-uav-device 5.4.0 5.4.0-26906
|
||||
2024-02-23-10-55-14 uavcloud-demo cmii-uav-device 5.4.0-26906 5.4.0-26906-01
|
||||
2024-02-23-14-32-05 uavcloud-devflight cmii-uav-device 5.2.0-validation 5.4.0-26906-01
|
||||
2024-02-28-17-09-55 uavcloud-demo cmii-uav-device 5.4.0 5.4.0-26905
|
||||
|
||||
28
socks5_txthinking/.gitignore
vendored
Normal file
28
socks5_txthinking/.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# IDEs
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
21
socks5_txthinking/LICENSE
Normal file
21
socks5_txthinking/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015-present Cloud <cloud@txthinking.com> https://www.txthinking.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
103
socks5_txthinking/README.md
Normal file
103
socks5_txthinking/README.md
Normal file
@@ -0,0 +1,103 @@
|
||||
## socks5
|
||||
|
||||
[中文](README_ZH.md)
|
||||
|
||||
[](https://goreportcard.com/report/github.com/txthinking/socks5)
|
||||
[](https://godoc.org/github.com/txthinking/socks5)
|
||||
|
||||
[🗣 News](https://t.me/s/txthinking_news)
|
||||
[🩸 Youtube](https://www.youtube.com/txthinking)
|
||||
|
||||
SOCKS Protocol Version 5 Library.
|
||||
|
||||
Full TCP/UDP and IPv4/IPv6 support.
|
||||
Goals: KISS, less is more, small API, code is like the original protocol.
|
||||
|
||||
❤️ A project by [txthinking.com](https://www.txthinking.com)
|
||||
|
||||
### Install
|
||||
|
||||
```
|
||||
$ go get github.com/txthinking/socks5
|
||||
```
|
||||
|
||||
### Struct is like concept in protocol
|
||||
|
||||
- Negotiation:
|
||||
- `type NegotiationRequest struct`
|
||||
- `func NewNegotiationRequest(methods []byte)`, in client
|
||||
- `func (r *NegotiationRequest) WriteTo(w io.Writer)`, client writes to server
|
||||
- `func NewNegotiationRequestFrom(r io.Reader)`, server reads from client
|
||||
- `type NegotiationReply struct`
|
||||
- `func NewNegotiationReply(method byte)`, in server
|
||||
- `func (r *NegotiationReply) WriteTo(w io.Writer)`, server writes to client
|
||||
- `func NewNegotiationReplyFrom(r io.Reader)`, client reads from server
|
||||
- User and password negotiation:
|
||||
- `type UserPassNegotiationRequest struct`
|
||||
- `func NewUserPassNegotiationRequest(username []byte, password []byte)`, in client
|
||||
- `func (r *UserPassNegotiationRequest) WriteTo(w io.Writer)`, client writes to server
|
||||
- `func NewUserPassNegotiationRequestFrom(r io.Reader)`, server reads from client
|
||||
- `type UserPassNegotiationReply struct`
|
||||
- `func NewUserPassNegotiationReply(status byte)`, in server
|
||||
- `func (r *UserPassNegotiationReply) WriteTo(w io.Writer)`, server writes to client
|
||||
- `func NewUserPassNegotiationReplyFrom(r io.Reader)`, client reads from server
|
||||
- Request:
|
||||
- `type Request struct`
|
||||
- `func NewRequest(cmd byte, atyp byte, dstaddr []byte, dstport []byte)`, in client
|
||||
- `func (r *Request) WriteTo(w io.Writer)`, client writes to server
|
||||
- `func NewRequestFrom(r io.Reader)`, server reads from client
|
||||
- After server gets the client's \*Request, processes...
|
||||
- Reply:
|
||||
- `type Reply struct`
|
||||
- `func NewReply(rep byte, atyp byte, bndaddr []byte, bndport []byte)`, in server
|
||||
- `func (r *Reply) WriteTo(w io.Writer)`, server writes to client
|
||||
- `func NewReplyFrom(r io.Reader)`, client reads from server
|
||||
- Datagram:
|
||||
- `type Datagram struct`
|
||||
- `func NewDatagram(atyp byte, dstaddr []byte, dstport []byte, data []byte)`
|
||||
- `func NewDatagramFromBytes(bb []byte)`
|
||||
- `func (d *Datagram) Bytes()`
|
||||
|
||||
### Advanced API
|
||||
|
||||
> This can satisfy the classic scenario, and it is still recommended that you choose the above small API to customize
|
||||
> for special scenarios.
|
||||
|
||||
**Server**: support both TCP and UDP
|
||||
|
||||
- `type Server struct`
|
||||
- `type Handler interface`
|
||||
- `TCPHandle(*Server, *net.TCPConn, *Request) error`
|
||||
- `UDPHandle(*Server, *net.UDPAddr, *Datagram) error`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
server, _ := NewClassicServer(addr, ip, username, password, tcpTimeout, udpTimeout)
|
||||
server.ListenAndServe(Handler)
|
||||
```
|
||||
|
||||
**Client**: support both TCP and UDP and return net.Conn
|
||||
|
||||
- `type Client struct`
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
client, _ := socks5.NewClient(server, username, password, tcpTimeout, udpTimeout)
|
||||
conn, _ := client.Dial(network, addr)
|
||||
```
|
||||
|
||||
### Projects using this library
|
||||
|
||||
- Brook: https://github.com/txthinking/brook
|
||||
- Shiliew: https://www.txthinking.com/shiliew.html
|
||||
- dismap: https://github.com/zhzyker/dismap
|
||||
- emp3r0r: https://github.com/jm33-m0/emp3r0r
|
||||
- hysteria: https://github.com/apernet/hysteria
|
||||
- mtg: https://github.com/9seconds/mtg
|
||||
- trojan-go: https://github.com/p4gefau1t/trojan-go
|
||||
|
||||
## License
|
||||
|
||||
Licensed under The MIT License
|
||||
102
socks5_txthinking/README_ZH.md
Normal file
102
socks5_txthinking/README_ZH.md
Normal file
@@ -0,0 +1,102 @@
|
||||
## socks5
|
||||
|
||||
[English](README.md)
|
||||
|
||||
[](https://goreportcard.com/report/github.com/txthinking/socks5)
|
||||
[](https://godoc.org/github.com/txthinking/socks5)
|
||||
|
||||
[🗣 News](https://t.me/s/txthinking_news)
|
||||
[🩸 Youtube](https://www.youtube.com/txthinking)
|
||||
|
||||
SOCKS Protocol Version 5 Library.
|
||||
|
||||
完整 TCP/UDP 和 IPv4/IPv6 支持.
|
||||
目标: KISS, less is more, small API, code is like the original protocol.
|
||||
|
||||
❤️ A project by [txthinking.com](https://www.txthinking.com)
|
||||
|
||||
### 获取
|
||||
|
||||
```
|
||||
$ go get github.com/txthinking/socks5
|
||||
```
|
||||
|
||||
### Struct的概念 对标 原始协议里的概念
|
||||
|
||||
* Negotiation:
|
||||
* `type NegotiationRequest struct`
|
||||
* `func NewNegotiationRequest(methods []byte)`, in client
|
||||
* `func (r *NegotiationRequest) WriteTo(w io.Writer)`, client writes to server
|
||||
* `func NewNegotiationRequestFrom(r io.Reader)`, server reads from client
|
||||
* `type NegotiationReply struct`
|
||||
* `func NewNegotiationReply(method byte)`, in server
|
||||
* `func (r *NegotiationReply) WriteTo(w io.Writer)`, server writes to client
|
||||
* `func NewNegotiationReplyFrom(r io.Reader)`, client reads from server
|
||||
* User and password negotiation:
|
||||
* `type UserPassNegotiationRequest struct`
|
||||
* `func NewUserPassNegotiationRequest(username []byte, password []byte)`, in client
|
||||
* `func (r *UserPassNegotiationRequest) WriteTo(w io.Writer)`, client writes to server
|
||||
* `func NewUserPassNegotiationRequestFrom(r io.Reader)`, server reads from client
|
||||
* `type UserPassNegotiationReply struct`
|
||||
* `func NewUserPassNegotiationReply(status byte)`, in server
|
||||
* `func (r *UserPassNegotiationReply) WriteTo(w io.Writer)`, server writes to client
|
||||
* `func NewUserPassNegotiationReplyFrom(r io.Reader)`, client reads from server
|
||||
* Request:
|
||||
* `type Request struct`
|
||||
* `func NewRequest(cmd byte, atyp byte, dstaddr []byte, dstport []byte)`, in client
|
||||
* `func (r *Request) WriteTo(w io.Writer)`, client writes to server
|
||||
* `func NewRequestFrom(r io.Reader)`, server reads from client
|
||||
* After server gets the client's *Request, processes...
|
||||
* Reply:
|
||||
* `type Reply struct`
|
||||
* `func NewReply(rep byte, atyp byte, bndaddr []byte, bndport []byte)`, in server
|
||||
* `func (r *Reply) WriteTo(w io.Writer)`, server writes to client
|
||||
* `func NewReplyFrom(r io.Reader)`, client reads from server
|
||||
* Datagram:
|
||||
* `type Datagram struct`
|
||||
* `func NewDatagram(atyp byte, dstaddr []byte, dstport []byte, data []byte)`
|
||||
* `func NewDatagramFromBytes(bb []byte)`
|
||||
* `func (d *Datagram) Bytes()`
|
||||
|
||||
### 高级 API
|
||||
|
||||
> 这可以满足经典场景,特殊场景推荐你选择上面的小API来自定义。
|
||||
|
||||
**Server**: 支持UDP和TCP
|
||||
|
||||
* `type Server struct`
|
||||
* `type Handler interface`
|
||||
* `TCPHandle(*Server, *net.TCPConn, *Request) error`
|
||||
* `UDPHandle(*Server, *net.UDPAddr, *Datagram) error`
|
||||
|
||||
举例:
|
||||
|
||||
```
|
||||
server, _ := NewClassicServer(addr, ip, username, password, tcpTimeout, udpTimeout)
|
||||
server.ListenAndServe(Handler)
|
||||
```
|
||||
|
||||
**Client**: 支持TCP和UDP, 返回net.Conn
|
||||
|
||||
* `type Client struct`
|
||||
|
||||
举例:
|
||||
|
||||
```
|
||||
client, _ := socks5.NewClient(server, username, password, tcpTimeout, udpTimeout)
|
||||
conn, _ := client.Dial(network, addr)
|
||||
```
|
||||
|
||||
### 谁在使用此项目
|
||||
|
||||
- Brook: https://github.com/txthinking/brook
|
||||
- Shiliew: https://www.txthinking.com/shiliew.html
|
||||
- dismap: https://github.com/zhzyker/dismap
|
||||
- emp3r0r: https://github.com/jm33-m0/emp3r0r
|
||||
- hysteria: https://github.com/apernet/hysteria
|
||||
- mtg: https://github.com/9seconds/mtg
|
||||
- trojan-go: https://github.com/p4gefau1t/trojan-go
|
||||
|
||||
## 开源协议
|
||||
|
||||
基于 MIT 协议开源
|
||||
11
socks5_txthinking/bind.go
Normal file
11
socks5_txthinking/bind.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
// TODO
|
||||
func (r *Request) bind(c net.Conn) error {
|
||||
return errors.New("Unsupport BIND now")
|
||||
}
|
||||
254
socks5_txthinking/client.go
Normal file
254
socks5_txthinking/client.go
Normal file
@@ -0,0 +1,254 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Client is socks5 client wrapper
|
||||
type Client struct {
|
||||
Server string
|
||||
UserName string
|
||||
Password string
|
||||
// On cmd UDP, let server control the tcp and udp connection relationship
|
||||
TCPConn net.Conn
|
||||
UDPConn net.Conn
|
||||
RemoteAddress net.Addr
|
||||
TCPTimeout int
|
||||
UDPTimeout int
|
||||
Dst string
|
||||
}
|
||||
|
||||
// This is just create a client, you need to use Dial to create conn
|
||||
func NewClient(addr, username, password string, tcpTimeout, udpTimeout int) (*Client, error) {
|
||||
c := &Client{
|
||||
Server: addr,
|
||||
UserName: username,
|
||||
Password: password,
|
||||
TCPTimeout: tcpTimeout,
|
||||
UDPTimeout: udpTimeout,
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Client) Dial(network, addr string) (net.Conn, error) {
|
||||
return c.DialWithLocalAddr(network, "", addr, nil)
|
||||
}
|
||||
|
||||
// If you want to send address that expects to use to send UDP, just assign it to src, otherwise it will send zero address.
|
||||
// Recommend specifying the src address in a non-NAT environment, and leave it blank in other cases.
|
||||
func (c *Client) DialWithLocalAddr(network, src, dst string, remoteAddr net.Addr) (net.Conn, error) {
|
||||
c = &Client{
|
||||
Server: c.Server,
|
||||
UserName: c.UserName,
|
||||
Password: c.Password,
|
||||
TCPTimeout: c.TCPTimeout,
|
||||
UDPTimeout: c.UDPTimeout,
|
||||
Dst: dst,
|
||||
RemoteAddress: remoteAddr,
|
||||
}
|
||||
var err error
|
||||
if network == "tcp" {
|
||||
var laddr net.Addr
|
||||
if src != "" {
|
||||
laddr, err = net.ResolveTCPAddr("tcp", src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := c.Negotiate(laddr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a, h, p, err := ParseAddress(dst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if a == ATYPDomain {
|
||||
h = h[1:]
|
||||
}
|
||||
if _, err := c.Request(NewRequest(CmdConnect, a, h, p)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
if network == "udp" {
|
||||
var laddr net.Addr
|
||||
if src != "" {
|
||||
laddr, err = net.ResolveTCPAddr("tcp", src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := c.Negotiate(laddr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a, h, p := ATYPIPv4, net.IPv4zero, []byte{0x00, 0x00}
|
||||
if src != "" {
|
||||
a, h, p, err = ParseAddress(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if a == ATYPDomain {
|
||||
h = h[1:]
|
||||
}
|
||||
}
|
||||
rp, err := c.Request(NewRequest(CmdUDP, a, h, p))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.UDPConn, err = DialUDP("udp", src, rp.Address())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.UDPTimeout != 0 {
|
||||
if err := c.UDPConn.SetDeadline(time.Now().Add(time.Duration(c.UDPTimeout) * time.Second)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
return nil, errors.New("unsupport network")
|
||||
}
|
||||
|
||||
func (c *Client) Read(b []byte) (int, error) {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.Read(b)
|
||||
}
|
||||
n, err := c.UDPConn.Read(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d, err := NewDatagramFromBytes(b[0:n])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n = copy(b, d.Data)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *Client) Write(b []byte) (int, error) {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.Write(b)
|
||||
}
|
||||
a, h, p, err := ParseAddress(c.Dst)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if a == ATYPDomain {
|
||||
h = h[1:]
|
||||
}
|
||||
d := NewDatagram(a, h, p, b)
|
||||
b1 := d.Bytes()
|
||||
n, err := c.UDPConn.Write(b1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(b1) != n {
|
||||
return 0, errors.New("not write full")
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.Close()
|
||||
}
|
||||
if c.TCPConn != nil {
|
||||
c.TCPConn.Close()
|
||||
}
|
||||
return c.UDPConn.Close()
|
||||
}
|
||||
|
||||
func (c *Client) LocalAddr() net.Addr {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.LocalAddr()
|
||||
}
|
||||
return c.UDPConn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *Client) RemoteAddr() net.Addr {
|
||||
return c.RemoteAddress
|
||||
}
|
||||
|
||||
func (c *Client) SetDeadline(t time.Time) error {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.SetDeadline(t)
|
||||
}
|
||||
return c.UDPConn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *Client) SetReadDeadline(t time.Time) error {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.SetReadDeadline(t)
|
||||
}
|
||||
return c.UDPConn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *Client) SetWriteDeadline(t time.Time) error {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.SetWriteDeadline(t)
|
||||
}
|
||||
return c.UDPConn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func (c *Client) Negotiate(laddr net.Addr) error {
|
||||
src := ""
|
||||
if laddr != nil {
|
||||
src = laddr.String()
|
||||
}
|
||||
var err error
|
||||
c.TCPConn, err = DialTCP("tcp", src, c.Server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.TCPTimeout != 0 {
|
||||
if err := c.TCPConn.SetDeadline(time.Now().Add(time.Duration(c.TCPTimeout) * time.Second)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
m := MethodNone
|
||||
if c.UserName != "" && c.Password != "" {
|
||||
m = MethodUsernamePassword
|
||||
}
|
||||
rq := NewNegotiationRequest([]byte{m})
|
||||
if _, err := rq.WriteTo(c.TCPConn); err != nil {
|
||||
return err
|
||||
}
|
||||
rp, err := NewNegotiationReplyFrom(c.TCPConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rp.Method != m {
|
||||
return errors.New("Unsupport method")
|
||||
}
|
||||
if m == MethodUsernamePassword {
|
||||
urq := NewUserPassNegotiationRequest([]byte(c.UserName), []byte(c.Password))
|
||||
if _, err := urq.WriteTo(c.TCPConn); err != nil {
|
||||
return err
|
||||
}
|
||||
urp, err := NewUserPassNegotiationReplyFrom(c.TCPConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if urp.Status != UserPassStatusSuccess {
|
||||
return ErrUserPassAuth
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Request(r *Request) (*Reply, error) {
|
||||
if _, err := r.WriteTo(c.TCPConn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rp, err := NewReplyFrom(c.TCPConn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rp.Rep != RepSuccess {
|
||||
return nil, errors.New("Host unreachable")
|
||||
}
|
||||
return rp, nil
|
||||
}
|
||||
172
socks5_txthinking/client_side.go
Normal file
172
socks5_txthinking/client_side.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrBadReply is the error when read reply
|
||||
ErrBadReply = errors.New("Bad Reply")
|
||||
)
|
||||
|
||||
// NewNegotiationRequest return negotiation request packet can be writed into server
|
||||
func NewNegotiationRequest(methods []byte) *NegotiationRequest {
|
||||
return &NegotiationRequest{
|
||||
Ver: Ver,
|
||||
NMethods: byte(len(methods)),
|
||||
Methods: methods,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo write negotiation request packet into server
|
||||
func (r *NegotiationRequest) WriteTo(w io.Writer) (int64, error) {
|
||||
i, err := w.Write(append([]byte{r.Ver, r.NMethods}, r.Methods...))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Sent NegotiationRequest: %#v %#v %#v\n", r.Ver, r.NMethods, r.Methods)
|
||||
}
|
||||
return int64(i), nil
|
||||
}
|
||||
|
||||
// NewNegotiationReplyFrom read negotiation reply packet from server
|
||||
func NewNegotiationReplyFrom(r io.Reader) (*NegotiationReply, error) {
|
||||
bb := make([]byte, 2)
|
||||
if _, err := io.ReadFull(r, bb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bb[0] != Ver {
|
||||
return nil, ErrVersion
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Got NegotiationReply: %#v %#v\n", bb[0], bb[1])
|
||||
}
|
||||
return &NegotiationReply{
|
||||
Ver: bb[0],
|
||||
Method: bb[1],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewUserPassNegotiationRequest return user password negotiation request packet can be writed into server
|
||||
func NewUserPassNegotiationRequest(username []byte, password []byte) *UserPassNegotiationRequest {
|
||||
return &UserPassNegotiationRequest{
|
||||
Ver: UserPassVer,
|
||||
Ulen: byte(len(username)),
|
||||
Uname: username,
|
||||
Plen: byte(len(password)),
|
||||
Passwd: password,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo write user password negotiation request packet into server
|
||||
func (r *UserPassNegotiationRequest) WriteTo(w io.Writer) (int64, error) {
|
||||
i, err := w.Write(append(append(append([]byte{r.Ver, r.Ulen}, r.Uname...), r.Plen), r.Passwd...))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Sent UserNameNegotiationRequest: %#v %#v %#v %#v %#v\n", r.Ver, r.Ulen, r.Uname, r.Plen, r.Passwd)
|
||||
}
|
||||
return int64(i), nil
|
||||
}
|
||||
|
||||
// NewUserPassNegotiationReplyFrom read user password negotiation reply packet from server
|
||||
func NewUserPassNegotiationReplyFrom(r io.Reader) (*UserPassNegotiationReply, error) {
|
||||
bb := make([]byte, 2)
|
||||
if _, err := io.ReadFull(r, bb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bb[0] != UserPassVer {
|
||||
return nil, ErrUserPassVersion
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Got UserPassNegotiationReply: %#v %#v \n", bb[0], bb[1])
|
||||
}
|
||||
return &UserPassNegotiationReply{
|
||||
Ver: bb[0],
|
||||
Status: bb[1],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewRequest return request packet can be writed into server, dstaddr should not have domain length
|
||||
func NewRequest(cmd byte, atyp byte, dstaddr []byte, dstport []byte) *Request {
|
||||
if atyp == ATYPDomain {
|
||||
dstaddr = append([]byte{byte(len(dstaddr))}, dstaddr...)
|
||||
}
|
||||
return &Request{
|
||||
Ver: Ver,
|
||||
Cmd: cmd,
|
||||
Rsv: 0x00,
|
||||
Atyp: atyp,
|
||||
DstAddr: dstaddr,
|
||||
DstPort: dstport,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo write request packet into server
|
||||
func (r *Request) WriteTo(w io.Writer) (int64, error) {
|
||||
i, err := w.Write(append(append([]byte{r.Ver, r.Cmd, r.Rsv, r.Atyp}, r.DstAddr...), r.DstPort...))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Sent Request: %#v %#v %#v %#v %#v %#v\n", r.Ver, r.Cmd, r.Rsv, r.Atyp, r.DstAddr, r.DstPort)
|
||||
}
|
||||
return int64(i), nil
|
||||
}
|
||||
|
||||
// NewReplyFrom read reply packet from server
|
||||
func NewReplyFrom(r io.Reader) (*Reply, error) {
|
||||
bb := make([]byte, 4)
|
||||
if _, err := io.ReadFull(r, bb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bb[0] != Ver {
|
||||
return nil, ErrVersion
|
||||
}
|
||||
var addr []byte
|
||||
if bb[3] == ATYPIPv4 {
|
||||
addr = make([]byte, 4)
|
||||
if _, err := io.ReadFull(r, addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if bb[3] == ATYPIPv6 {
|
||||
addr = make([]byte, 16)
|
||||
if _, err := io.ReadFull(r, addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if bb[3] == ATYPDomain {
|
||||
dal := make([]byte, 1)
|
||||
if _, err := io.ReadFull(r, dal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dal[0] == 0 {
|
||||
return nil, ErrBadReply
|
||||
}
|
||||
addr = make([]byte, int(dal[0]))
|
||||
if _, err := io.ReadFull(r, addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr = append(dal, addr...)
|
||||
} else {
|
||||
return nil, ErrBadReply
|
||||
}
|
||||
port := make([]byte, 2)
|
||||
if _, err := io.ReadFull(r, port); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Got Reply: %#v %#v %#v %#v %#v %#v\n", bb[0], bb[1], bb[2], bb[3], addr, port)
|
||||
}
|
||||
return &Reply{
|
||||
Ver: bb[0],
|
||||
Rep: bb[1],
|
||||
Rsv: bb[2],
|
||||
Atyp: bb[3],
|
||||
BndAddr: addr,
|
||||
BndPort: port,
|
||||
}, nil
|
||||
}
|
||||
53
socks5_txthinking/connect.go
Normal file
53
socks5_txthinking/connect.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Connect remote conn which u want to connect with your dialer
|
||||
// Error or OK both replied.
|
||||
func (r *Request) Connect(w io.Writer) (net.Conn, error) {
|
||||
if Debug {
|
||||
log.Println("Call:", r.Address())
|
||||
}
|
||||
rc, err := DialTCP("tcp", "", r.Address())
|
||||
if err != nil {
|
||||
var p *Reply
|
||||
if r.Atyp == ATYPIPv4 || r.Atyp == ATYPDomain {
|
||||
p = NewReply(RepHostUnreachable, ATYPIPv4, []byte{0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00})
|
||||
} else {
|
||||
p = NewReply(RepHostUnreachable, ATYPIPv6, []byte(net.IPv6zero), []byte{0x00, 0x00})
|
||||
}
|
||||
if _, err := p.WriteTo(w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a, addr, port, err := ParseAddress(rc.LocalAddr().String())
|
||||
if err != nil {
|
||||
rc.Close()
|
||||
var p *Reply
|
||||
if r.Atyp == ATYPIPv4 || r.Atyp == ATYPDomain {
|
||||
p = NewReply(RepHostUnreachable, ATYPIPv4, []byte{0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00})
|
||||
} else {
|
||||
p = NewReply(RepHostUnreachable, ATYPIPv6, []byte(net.IPv6zero), []byte{0x00, 0x00})
|
||||
}
|
||||
if _, err := p.WriteTo(w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if a == ATYPDomain {
|
||||
addr = addr[1:]
|
||||
}
|
||||
p := NewReply(RepSuccess, a, addr, port)
|
||||
if _, err := p.WriteTo(w); err != nil {
|
||||
rc.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rc, nil
|
||||
}
|
||||
87
socks5_txthinking/example_test.go
Normal file
87
socks5_txthinking/example_test.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func ExampleServer() {
|
||||
s, err := NewClassicServer("127.0.0.1:1080", "127.0.0.1", "", "", 0, 60)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
// You can pass in custom Handler
|
||||
s.ListenAndServe(nil)
|
||||
// #Output:
|
||||
}
|
||||
|
||||
func ExampleClient_tcp() {
|
||||
go ExampleServer()
|
||||
c, err := NewClient("127.0.0.1:1080", "", "", 0, 60)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Dial: func(network, addr string) (net.Conn, error) {
|
||||
return c.Dial(network, addr)
|
||||
},
|
||||
},
|
||||
}
|
||||
res, err := client.Get("https://ifconfig.co")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
b, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Println("tcp", string(b))
|
||||
// Output:
|
||||
}
|
||||
|
||||
func ExampleClient_udp() {
|
||||
go ExampleServer()
|
||||
c, err := NewClient("127.0.0.1:1080", "", "", 0, 60)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
conn, err := c.Dial("udp", "8.8.8.8:53")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
b, err := hex.DecodeString("0001010000010000000000000a74787468696e6b696e6703636f6d0000010001")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if _, err := conn.Write(b); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
b = make([]byte, 2048)
|
||||
n, err := conn.Read(b)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
m := &dns.Msg{}
|
||||
if err := m.Unpack(b[0:n]); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
log.Println(m.String())
|
||||
// Output:
|
||||
}
|
||||
8
socks5_txthinking/go.mod
Normal file
8
socks5_txthinking/go.mod
Normal file
@@ -0,0 +1,8 @@
|
||||
module wdd.io/socks5
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/miekg/dns v1.1.51
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
)
|
||||
41
socks5_txthinking/go.sum
Normal file
41
socks5_txthinking/go.sum
Normal file
@@ -0,0 +1,41 @@
|
||||
github.com/miekg/dns v1.1.51 h1:0+Xg7vObnhrz/4ZCZcZh7zPXlmU0aveS2HDBd0m0qSo=
|
||||
github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0=
|
||||
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
|
||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
52
socks5_txthinking/init.go
Normal file
52
socks5_txthinking/init.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
var Debug bool
|
||||
|
||||
func init() {
|
||||
// log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
}
|
||||
|
||||
var Resolve func(network string, addr string) (net.Addr, error) = func(network string, addr string) (net.Addr, error) {
|
||||
if network == "tcp" {
|
||||
return net.ResolveTCPAddr("tcp", addr)
|
||||
}
|
||||
return net.ResolveUDPAddr("udp", addr)
|
||||
}
|
||||
|
||||
var DialTCP func(network string, laddr, raddr string) (net.Conn, error) = func(network string, laddr, raddr string) (net.Conn, error) {
|
||||
var la, ra *net.TCPAddr
|
||||
if laddr != "" {
|
||||
var err error
|
||||
la, err = net.ResolveTCPAddr(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
a, err := Resolve(network, raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ra = a.(*net.TCPAddr)
|
||||
return net.DialTCP(network, la, ra)
|
||||
}
|
||||
|
||||
var DialUDP func(network string, laddr, raddr string) (net.Conn, error) = func(network string, laddr, raddr string) (net.Conn, error) {
|
||||
var la, ra *net.UDPAddr
|
||||
if laddr != "" {
|
||||
var err error
|
||||
la, err = net.ResolveUDPAddr(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
a, err := Resolve(network, raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ra = a.(*net.UDPAddr)
|
||||
return net.DialUDP(network, la, ra)
|
||||
}
|
||||
5
socks5_txthinking/main.go
Normal file
5
socks5_txthinking/main.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package socks5
|
||||
|
||||
func main() {
|
||||
|
||||
}
|
||||
211
socks5_txthinking/old_tcp_tailscale/logger.go
Normal file
211
socks5_txthinking/old_tcp_tailscale/logger.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package old_tcp_tailscale // Copyright (c) Tailscale Inc & AUTHORS
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
)
|
||||
|
||||
// Logf is the basic Tailscale logger type: a printf-like func.
|
||||
// Like log.Printf, the format need not end in a newline.
|
||||
// Logf functions must be safe for concurrent use.
|
||||
type Logf func(format string, args ...any)
|
||||
|
||||
// A Context is a context.Context that should contain a custom log function, obtainable from FromContext.
|
||||
// If no log function is present, FromContext will return log.Printf.
|
||||
// To construct a Context, use Add
|
||||
//
|
||||
// Deprecated: Do not use.
|
||||
type Context context.Context
|
||||
|
||||
// jenc is a json.Encode + bytes.Buffer pair wired up to be reused in a pool.
|
||||
type jenc struct {
|
||||
buf bytes.Buffer
|
||||
enc *json.Encoder
|
||||
}
|
||||
|
||||
var jencPool = &sync.Pool{New: func() any {
|
||||
je := new(jenc)
|
||||
je.enc = json.NewEncoder(&je.buf)
|
||||
return je
|
||||
}}
|
||||
|
||||
// JSON marshals v as JSON and writes it to logf formatted with the annotation to make logtail
|
||||
// treat it as a structured log.
|
||||
//
|
||||
// The recType is the record type and becomes the key of the wrapper
|
||||
// JSON object that is logged. That is, if recType is "foo" and v is
|
||||
// 123, the value logged is {"foo":123}.
|
||||
//
|
||||
// Do not use recType "logtail", "v", "text", or "metrics", with any case.
|
||||
// Those are reserved for the logging system.
|
||||
//
|
||||
// The level can be from 0 to 9. Levels from 1 to 9 are included in
|
||||
// the logged JSON object, like {"foo":123,"v":2}.
|
||||
func (logf Logf) JSON(level int, recType string, v any) {
|
||||
je := jencPool.Get().(*jenc)
|
||||
defer jencPool.Put(je)
|
||||
je.buf.Reset()
|
||||
je.buf.WriteByte('{')
|
||||
je.enc.Encode(recType)
|
||||
je.buf.Truncate(je.buf.Len() - 1) // remove newline from prior Encode
|
||||
je.buf.WriteByte(':')
|
||||
if err := je.enc.Encode(v); err != nil {
|
||||
logf("[unexpected]: failed to encode structured JSON log record of type %q / %T: %v", recType, v, err)
|
||||
return
|
||||
}
|
||||
je.buf.Truncate(je.buf.Len() - 1) // remove newline from prior Encode
|
||||
je.buf.WriteByte('}')
|
||||
// Magic prefix recognized by logtail:
|
||||
logf("[v\x00JSON]%d%s", level%10, je.buf.Bytes())
|
||||
|
||||
}
|
||||
|
||||
// WithPrefix wraps f, prefixing each format with the provided prefix.
|
||||
func WithPrefix(f Logf, prefix string) Logf {
|
||||
return func(format string, args ...any) {
|
||||
f(prefix+format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// FuncWriter returns an io.Writer that writes to f.
|
||||
func FuncWriter(f Logf) io.Writer {
|
||||
return funcWriter{f}
|
||||
}
|
||||
|
||||
// StdLogger returns a standard library logger from a Logf.
|
||||
func StdLogger(f Logf) *log.Logger {
|
||||
return log.New(FuncWriter(f), "", 0)
|
||||
}
|
||||
|
||||
type funcWriter struct{ f Logf }
|
||||
|
||||
func (w funcWriter) Write(p []byte) (int, error) {
|
||||
w.f("%s", p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Discard is a Logf that throws away the logs given to it.
|
||||
func Discard(string, ...any) {}
|
||||
|
||||
// LogOnChange logs a given line only if line != lastLine, or if maxInterval has passed
|
||||
// since the last time this identical line was logged.
|
||||
func LogOnChange(logf Logf, maxInterval time.Duration, timeNow func() time.Time) Logf {
|
||||
var (
|
||||
mu sync.Mutex
|
||||
sLastLogged string
|
||||
tLastLogged = timeNow()
|
||||
)
|
||||
|
||||
return func(format string, args ...any) {
|
||||
s := fmt.Sprintf(format, args...)
|
||||
|
||||
mu.Lock()
|
||||
if s == sLastLogged && timeNow().Sub(tLastLogged) < maxInterval {
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
sLastLogged = s
|
||||
tLastLogged = timeNow()
|
||||
mu.Unlock()
|
||||
|
||||
// Re-stringify it (instead of using "%s", s) so something like "%s"
|
||||
// doesn't end up getting rate-limited. (And can't use 's' as the pattern,
|
||||
// as it might contain formatting directives.)
|
||||
logf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// ArgWriter is a fmt.Formatter that can be passed to any Logf func to
|
||||
// efficiently write to a %v argument without allocations.
|
||||
type ArgWriter func(*bufio.Writer)
|
||||
|
||||
func (fn ArgWriter) Format(f fmt.State, _ rune) {
|
||||
bw := argBufioPool.Get().(*bufio.Writer)
|
||||
bw.Reset(f)
|
||||
fn(bw)
|
||||
bw.Flush()
|
||||
argBufioPool.Put(bw)
|
||||
}
|
||||
|
||||
var argBufioPool = &sync.Pool{New: func() any { return bufio.NewWriterSize(io.Discard, 1024) }}
|
||||
|
||||
// Filtered returns a Logf that silently swallows some log lines.
|
||||
// Each inbound format and args is evaluated and printed to a string s.
|
||||
// The original format and args are passed to logf if and only if allow(s) returns true.
|
||||
func Filtered(logf Logf, allow func(s string) bool) Logf {
|
||||
return func(format string, args ...any) {
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
if !allow(msg) {
|
||||
return
|
||||
}
|
||||
logf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// LogfCloser wraps logf to create a logger that can be closed.
|
||||
// Calling close makes all future calls to newLogf into no-ops.
|
||||
func LogfCloser(logf Logf) (newLogf Logf, close func()) {
|
||||
var (
|
||||
mu sync.Mutex
|
||||
closed bool
|
||||
)
|
||||
close = func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
closed = true
|
||||
}
|
||||
newLogf = func(msg string, args ...any) {
|
||||
mu.Lock()
|
||||
if closed {
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
mu.Unlock()
|
||||
logf(msg, args...)
|
||||
}
|
||||
return newLogf, close
|
||||
}
|
||||
|
||||
// AsJSON returns a formatter that formats v as JSON. The value is suitable to
|
||||
// passing to a regular %v printf argument. (%s is not required)
|
||||
//
|
||||
// If json.Marshal returns an error, the output is "%%!JSON-ERROR:" followed by
|
||||
// the error string.
|
||||
func AsJSON(v any) fmt.Formatter {
|
||||
return asJSONResult{v}
|
||||
}
|
||||
|
||||
type asJSONResult struct{ v any }
|
||||
|
||||
func (a asJSONResult) Format(s fmt.State, verb rune) {
|
||||
v, err := json.Marshal(a.v)
|
||||
if err != nil {
|
||||
fmt.Fprintf(s, "%%!JSON-ERROR:%v", err)
|
||||
return
|
||||
}
|
||||
s.Write(v)
|
||||
}
|
||||
|
||||
// TBLogger is the testing.TB subset needed by TestLogger.
|
||||
type TBLogger interface {
|
||||
Helper()
|
||||
Logf(format string, args ...any)
|
||||
}
|
||||
|
||||
// TestLogger returns a logger that logs to tb.Logf
|
||||
// with a prefix to make it easier to distinguish spam
|
||||
// from explicit test failures.
|
||||
func TestLogger(tb TBLogger) Logf {
|
||||
return func(format string, args ...any) {
|
||||
tb.Helper()
|
||||
tb.Logf(" ... "+format, args...)
|
||||
}
|
||||
}
|
||||
42
socks5_txthinking/old_tcp_tailscale/main.go
Normal file
42
socks5_txthinking/old_tcp_tailscale/main.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Println("start socks5 server error must provide listen port !")
|
||||
return
|
||||
}
|
||||
if len(os.Args) > 3 {
|
||||
fmt.Println("start socks5 server error !")
|
||||
return
|
||||
}
|
||||
username := ""
|
||||
password := ""
|
||||
if len(os.Args) == 4 {
|
||||
username = os.Args[2]
|
||||
password = os.Args[3]
|
||||
}
|
||||
|
||||
port := os.Args[1]
|
||||
|
||||
listener, err := net.Listen("tcp", ":"+port)
|
||||
if err != nil {
|
||||
fmt.Println("start listener error ! => " + err.Error())
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
|
||||
err = server.Serve(listener)
|
||||
if err != nil {
|
||||
fmt.Println("server start to server error ! => " + err.Error())
|
||||
}
|
||||
}
|
||||
417
socks5_txthinking/old_tcp_tailscale/socks5.go
Normal file
417
socks5_txthinking/old_tcp_tailscale/socks5.go
Normal file
@@ -0,0 +1,417 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package socks5 is a SOCKS5 server implementation.
|
||||
//
|
||||
// This is used for userspace networking in Tailscale. Specifically,
|
||||
// this is used for dialing out of the machine to other nodes, without
|
||||
// the host kernel's involvement, so it doesn't proper routing tables,
|
||||
// TUN, IPv6, etc. This package is meant to only handle the SOCKS5 protocol
|
||||
// details and not any integration with Tailscale internals itself.
|
||||
//
|
||||
// The glue between this package and Tailscale is in net/socks5/tssocks.
|
||||
package old_tcp_tailscale
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Authentication METHODs described in RFC 1928, section 3.
|
||||
const (
|
||||
noAuthRequired byte = 0
|
||||
passwordAuth byte = 2
|
||||
noAcceptableAuth byte = 255
|
||||
)
|
||||
|
||||
// passwordAuthVersion is the auth version byte described in RFC 1929.
|
||||
const passwordAuthVersion = 1
|
||||
|
||||
// socks5Version is the byte that represents the SOCKS version
|
||||
// in requests.
|
||||
const socks5Version byte = 5
|
||||
|
||||
// commandType are the bytes sent in SOCKS5 packets
|
||||
// that represent the kind of connection the client needs.
|
||||
type commandType byte
|
||||
|
||||
// The set of valid SOCKS5 commands as described in RFC 1928.
|
||||
const (
|
||||
connect commandType = 1
|
||||
bind commandType = 2
|
||||
udpAssociate commandType = 3
|
||||
)
|
||||
|
||||
// addrType are the bytes sent in SOCKS5 packets
|
||||
// that represent particular address types.
|
||||
type addrType byte
|
||||
|
||||
// The set of valid SOCKS5 address types as defined in RFC 1928.
|
||||
const (
|
||||
ipv4 addrType = 1
|
||||
domainName addrType = 3
|
||||
ipv6 addrType = 4
|
||||
)
|
||||
|
||||
// replyCode are the bytes sent in SOCKS5 packets
|
||||
// that represent replies from the server to a client
|
||||
// request.
|
||||
type replyCode byte
|
||||
|
||||
// The set of valid SOCKS5 reply types as per the RFC 1928.
|
||||
const (
|
||||
success replyCode = 0
|
||||
generalFailure replyCode = 1
|
||||
connectionNotAllowed replyCode = 2
|
||||
networkUnreachable replyCode = 3
|
||||
hostUnreachable replyCode = 4
|
||||
connectionRefused replyCode = 5
|
||||
ttlExpired replyCode = 6
|
||||
commandNotSupported replyCode = 7
|
||||
addrTypeNotSupported replyCode = 8
|
||||
)
|
||||
|
||||
// Server is a SOCKS5 proxy server.
|
||||
type Server struct {
|
||||
// Logf optionally specifies the logger to use.
|
||||
// If nil, the standard logger is used.
|
||||
Logf Logf
|
||||
|
||||
// Dialer optionally specifies the dialer to use for outgoing connections.
|
||||
// If nil, the net package's standard dialer is used.
|
||||
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
// Username and Password, if set, are the credential clients must provide.
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func (s *Server) dial(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dial := s.Dialer
|
||||
if dial == nil {
|
||||
dialer := &net.Dialer{}
|
||||
dial = dialer.DialContext
|
||||
}
|
||||
return dial(ctx, network, addr)
|
||||
}
|
||||
|
||||
func (s *Server) logf(format string, args ...any) {
|
||||
logf := s.Logf
|
||||
if logf == nil {
|
||||
logf = log.Printf
|
||||
}
|
||||
logf(format, args...)
|
||||
}
|
||||
|
||||
// Serve accepts and handles incoming connections on the given listener.
|
||||
func (s *Server) Serve(l net.Listener) error {
|
||||
defer l.Close()
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
defer c.Close()
|
||||
conn := &Conn{clientConn: c, srv: s}
|
||||
err := conn.Run()
|
||||
if err != nil {
|
||||
s.logf("client connection failed: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// Conn is a SOCKS5 connection for client to reach
|
||||
// server.
|
||||
type Conn struct {
|
||||
// The struct is filled by each of the internal
|
||||
// methods in turn as the transaction progresses.
|
||||
|
||||
srv *Server
|
||||
clientConn net.Conn
|
||||
request *request
|
||||
}
|
||||
|
||||
// Run starts the new connection.
|
||||
func (c *Conn) Run() error {
|
||||
needAuth := c.srv.Username != "" || c.srv.Password != ""
|
||||
authMethod := noAuthRequired
|
||||
if needAuth {
|
||||
authMethod = passwordAuth
|
||||
}
|
||||
|
||||
err := parseClientGreeting(c.clientConn, authMethod)
|
||||
if err != nil {
|
||||
c.clientConn.Write([]byte{socks5Version, noAcceptableAuth})
|
||||
return err
|
||||
}
|
||||
c.clientConn.Write([]byte{socks5Version, authMethod})
|
||||
if !needAuth {
|
||||
return c.handleRequest()
|
||||
}
|
||||
|
||||
user, pwd, err := parseClientAuth(c.clientConn)
|
||||
if err != nil || user != c.srv.Username || pwd != c.srv.Password {
|
||||
c.clientConn.Write([]byte{1, 1}) // auth error
|
||||
return err
|
||||
}
|
||||
c.clientConn.Write([]byte{1, 0}) // auth success
|
||||
|
||||
return c.handleRequest()
|
||||
}
|
||||
|
||||
func (c *Conn) handleRequest() error {
|
||||
|
||||
req, err := parseClientRequest(c.clientConn)
|
||||
if err != nil {
|
||||
res := &response{reply: generalFailure}
|
||||
buf, _ := res.marshal()
|
||||
c.clientConn.Write(buf)
|
||||
return err
|
||||
}
|
||||
if req.command != connect {
|
||||
res := &response{reply: commandNotSupported}
|
||||
buf, _ := res.marshal()
|
||||
c.clientConn.Write(buf)
|
||||
return fmt.Errorf("unsupported command %v", req.command)
|
||||
}
|
||||
c.request = req
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
srv, err := c.srv.dial(
|
||||
ctx,
|
||||
"tcp",
|
||||
net.JoinHostPort(c.request.destination, strconv.Itoa(int(c.request.port))),
|
||||
)
|
||||
if err != nil {
|
||||
res := &response{reply: generalFailure}
|
||||
buf, _ := res.marshal()
|
||||
c.clientConn.Write(buf)
|
||||
return err
|
||||
}
|
||||
defer srv.Close()
|
||||
serverAddr, serverPortStr, err := net.SplitHostPort(srv.LocalAddr().String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serverPort, _ := strconv.Atoi(serverPortStr)
|
||||
|
||||
var bindAddrType addrType
|
||||
if ip := net.ParseIP(serverAddr); ip != nil {
|
||||
if ip.To4() != nil {
|
||||
bindAddrType = ipv4
|
||||
} else {
|
||||
bindAddrType = ipv6
|
||||
}
|
||||
} else {
|
||||
bindAddrType = domainName
|
||||
}
|
||||
|
||||
res := &response{
|
||||
reply: success,
|
||||
bindAddrType: bindAddrType,
|
||||
bindAddr: serverAddr,
|
||||
bindPort: uint16(serverPort),
|
||||
}
|
||||
buf, err := res.marshal()
|
||||
if err != nil {
|
||||
res = &response{reply: generalFailure}
|
||||
buf, _ = res.marshal()
|
||||
}
|
||||
c.clientConn.Write(buf)
|
||||
|
||||
errc := make(chan error, 2)
|
||||
go func() {
|
||||
_, err := io.Copy(c.clientConn, srv)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("from backend to client: %w", err)
|
||||
}
|
||||
errc <- err
|
||||
}()
|
||||
go func() {
|
||||
_, err := io.Copy(srv, c.clientConn)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("from client to backend: %w", err)
|
||||
}
|
||||
errc <- err
|
||||
}()
|
||||
return <-errc
|
||||
}
|
||||
|
||||
// parseClientGreeting parses a request initiation packet.
|
||||
func parseClientGreeting(r io.Reader, authMethod byte) error {
|
||||
var hdr [2]byte
|
||||
_, err := io.ReadFull(r, hdr[:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read packet header")
|
||||
}
|
||||
if hdr[0] != socks5Version {
|
||||
return fmt.Errorf("incompatible SOCKS version")
|
||||
}
|
||||
count := int(hdr[1])
|
||||
methods := make([]byte, count)
|
||||
_, err = io.ReadFull(r, methods)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read methods")
|
||||
}
|
||||
for _, m := range methods {
|
||||
if m == authMethod {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("no acceptable auth methods")
|
||||
}
|
||||
|
||||
func parseClientAuth(r io.Reader) (usr, pwd string, err error) {
|
||||
var hdr [2]byte
|
||||
if _, err := io.ReadFull(r, hdr[:]); err != nil {
|
||||
return "", "", fmt.Errorf("could not read auth packet header")
|
||||
}
|
||||
if hdr[0] != passwordAuthVersion {
|
||||
return "", "", fmt.Errorf("bad SOCKS auth version")
|
||||
}
|
||||
usrLen := int(hdr[1])
|
||||
usrBytes := make([]byte, usrLen)
|
||||
if _, err := io.ReadFull(r, usrBytes); err != nil {
|
||||
return "", "", fmt.Errorf("could not read auth packet username")
|
||||
}
|
||||
var hdrPwd [1]byte
|
||||
if _, err := io.ReadFull(r, hdrPwd[:]); err != nil {
|
||||
return "", "", fmt.Errorf("could not read auth packet password length")
|
||||
}
|
||||
pwdLen := int(hdrPwd[0])
|
||||
pwdBytes := make([]byte, pwdLen)
|
||||
if _, err := io.ReadFull(r, pwdBytes); err != nil {
|
||||
return "", "", fmt.Errorf("could not read auth packet password")
|
||||
}
|
||||
return string(usrBytes), string(pwdBytes), nil
|
||||
}
|
||||
|
||||
// request represents data contained within a SOCKS5
|
||||
// connection request packet.
|
||||
type request struct {
|
||||
command commandType
|
||||
destination string
|
||||
port uint16
|
||||
destAddrType addrType
|
||||
}
|
||||
|
||||
// parseClientRequest converts raw packet bytes into a
|
||||
// SOCKS5Request struct.
|
||||
func parseClientRequest(r io.Reader) (*request, error) {
|
||||
var hdr [4]byte
|
||||
_, err := io.ReadFull(r, hdr[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read packet header")
|
||||
}
|
||||
cmd := hdr[1]
|
||||
destAddrType := addrType(hdr[3])
|
||||
|
||||
var destination string
|
||||
var port uint16
|
||||
|
||||
if destAddrType == ipv4 {
|
||||
var ip [4]byte
|
||||
_, err = io.ReadFull(r, ip[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read IPv4 address")
|
||||
}
|
||||
destination = net.IP(ip[:]).String()
|
||||
} else if destAddrType == domainName {
|
||||
var dstSizeByte [1]byte
|
||||
_, err = io.ReadFull(r, dstSizeByte[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read domain name size")
|
||||
}
|
||||
dstSize := int(dstSizeByte[0])
|
||||
domainName := make([]byte, dstSize)
|
||||
_, err = io.ReadFull(r, domainName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read domain name")
|
||||
}
|
||||
destination = string(domainName)
|
||||
} else if destAddrType == ipv6 {
|
||||
var ip [16]byte
|
||||
_, err = io.ReadFull(r, ip[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read IPv6 address")
|
||||
}
|
||||
destination = net.IP(ip[:]).String()
|
||||
} else {
|
||||
return nil, fmt.Errorf("unsupported address type")
|
||||
}
|
||||
var portBytes [2]byte
|
||||
_, err = io.ReadFull(r, portBytes[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read port")
|
||||
}
|
||||
port = binary.BigEndian.Uint16(portBytes[:])
|
||||
|
||||
return &request{
|
||||
command: commandType(cmd),
|
||||
destination: destination,
|
||||
port: port,
|
||||
destAddrType: destAddrType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// response contains the contents of
|
||||
// a response packet sent from the proxy
|
||||
// to the client.
|
||||
type response struct {
|
||||
reply replyCode
|
||||
bindAddrType addrType
|
||||
bindAddr string
|
||||
bindPort uint16
|
||||
}
|
||||
|
||||
// marshal converts a SOCKS5Response struct into
|
||||
// a packet. If res.reply == Success, it may throw an error on
|
||||
// receiving an invalid bind address. Otherwise, it will not throw.
|
||||
func (res *response) marshal() ([]byte, error) {
|
||||
pkt := make([]byte, 4)
|
||||
pkt[0] = socks5Version
|
||||
pkt[1] = byte(res.reply)
|
||||
pkt[2] = 0 // null reserved byte
|
||||
pkt[3] = byte(res.bindAddrType)
|
||||
|
||||
if res.reply != success {
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
var addr []byte
|
||||
switch res.bindAddrType {
|
||||
case ipv4:
|
||||
addr = net.ParseIP(res.bindAddr).To4()
|
||||
if addr == nil {
|
||||
return nil, fmt.Errorf("invalid IPv4 address for binding")
|
||||
}
|
||||
case domainName:
|
||||
if len(res.bindAddr) > 255 {
|
||||
return nil, fmt.Errorf("invalid domain name for binding")
|
||||
}
|
||||
addr = make([]byte, 0, len(res.bindAddr)+1)
|
||||
addr = append(addr, byte(len(res.bindAddr)))
|
||||
addr = append(addr, []byte(res.bindAddr)...)
|
||||
case ipv6:
|
||||
addr = net.ParseIP(res.bindAddr).To16()
|
||||
if addr == nil {
|
||||
return nil, fmt.Errorf("invalid IPv6 address for binding")
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported address type")
|
||||
}
|
||||
|
||||
pkt = append(pkt, addr...)
|
||||
pkt = binary.BigEndian.AppendUint16(pkt, uint16(res.bindPort))
|
||||
|
||||
return pkt, nil
|
||||
}
|
||||
96
socks5_txthinking/runnergrounp.go
Normal file
96
socks5_txthinking/runnergrounp.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RunnerGroup is like sync.WaitGroup,
|
||||
// the diffrence is if one task stops, all will be stopped.
|
||||
type RunnerGroup struct {
|
||||
Runners []*Runner
|
||||
End chan byte
|
||||
}
|
||||
|
||||
type Runner struct {
|
||||
// Start is a blocking function.
|
||||
Start func() error
|
||||
// Stop is not a blocking function, if Stop called, must let Start return.
|
||||
// Notice: Stop maybe called multi times even if before start.
|
||||
Stop func() error
|
||||
lock sync.Mutex
|
||||
status int
|
||||
}
|
||||
|
||||
func NewRunnerGroup() *RunnerGroup {
|
||||
g := &RunnerGroup{}
|
||||
g.Runners = make([]*Runner, 0)
|
||||
g.End = make(chan byte)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *RunnerGroup) Add(r *Runner) {
|
||||
g.Runners = append(g.Runners, r)
|
||||
}
|
||||
|
||||
// Call Wait after all task have been added,
|
||||
// Return the first ended start's result.
|
||||
func (g *RunnerGroup) Wait() error {
|
||||
e := make(chan error)
|
||||
for _, v := range g.Runners {
|
||||
v.status = 1
|
||||
go func(v *Runner) {
|
||||
err := v.Start()
|
||||
v.lock.Lock()
|
||||
v.status = 0
|
||||
v.lock.Unlock()
|
||||
select {
|
||||
case <-g.End:
|
||||
case e <- err:
|
||||
}
|
||||
}(v)
|
||||
}
|
||||
err := <-e
|
||||
for _, v := range g.Runners {
|
||||
for {
|
||||
v.lock.Lock()
|
||||
if v.status == 0 {
|
||||
v.lock.Unlock()
|
||||
break
|
||||
}
|
||||
v.lock.Unlock()
|
||||
_ = v.Stop()
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
close(g.End)
|
||||
return err
|
||||
}
|
||||
|
||||
// Call Done if you want to stop all.
|
||||
// return the stop's return which is not nil, do not guarantee,
|
||||
// because starts may ended caused by itself.
|
||||
func (g *RunnerGroup) Done() error {
|
||||
if len(g.Runners) == 0 {
|
||||
return nil
|
||||
}
|
||||
var e error
|
||||
for _, v := range g.Runners {
|
||||
for {
|
||||
v.lock.Lock()
|
||||
if v.status == 0 {
|
||||
v.lock.Unlock()
|
||||
break
|
||||
}
|
||||
v.lock.Unlock()
|
||||
if err := v.Stop(); err != nil {
|
||||
if e == nil {
|
||||
e = err
|
||||
}
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
<-g.End
|
||||
return e
|
||||
}
|
||||
438
socks5_txthinking/server.go
Normal file
438
socks5_txthinking/server.go
Normal file
@@ -0,0 +1,438 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cache "github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrUnsupportCmd is the error when got unsupport command
|
||||
ErrUnsupportCmd = errors.New("Unsupport Command")
|
||||
// ErrUserPassAuth is the error when got invalid username or password
|
||||
ErrUserPassAuth = errors.New("Invalid Username or Password for Auth")
|
||||
)
|
||||
|
||||
// Server is socks5 server wrapper
|
||||
type Server struct {
|
||||
UserName string
|
||||
Password string
|
||||
Method byte
|
||||
SupportedCommands []byte
|
||||
Addr string
|
||||
ServerAddr net.Addr
|
||||
UDPConn *net.UDPConn
|
||||
UDPExchanges *cache.Cache
|
||||
TCPTimeout int
|
||||
UDPTimeout int
|
||||
Handle Handler
|
||||
AssociatedUDP *cache.Cache
|
||||
UDPSrc *cache.Cache
|
||||
RunnerGroup *RunnerGroup
|
||||
// RFC: [UDP ASSOCIATE] The server MAY use this information to limit access to the association. Default false, no limit.
|
||||
LimitUDP bool
|
||||
}
|
||||
|
||||
// UDPExchange used to store client address and remote connection
|
||||
type UDPExchange struct {
|
||||
ClientAddr *net.UDPAddr
|
||||
RemoteConn net.Conn
|
||||
}
|
||||
|
||||
// NewClassicServer return a server which allow none method
|
||||
func NewClassicServer(addr, ip, username, password string, tcpTimeout, udpTimeout int) (*Server, error) {
|
||||
_, p, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
saddr, err := Resolve("udp", net.JoinHostPort(ip, p))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := MethodNone
|
||||
if username != "" && password != "" {
|
||||
m = MethodUsernamePassword
|
||||
}
|
||||
cs := cache.New(cache.NoExpiration, cache.NoExpiration)
|
||||
cs1 := cache.New(cache.NoExpiration, cache.NoExpiration)
|
||||
cs2 := cache.New(cache.NoExpiration, cache.NoExpiration)
|
||||
s := &Server{
|
||||
Method: m,
|
||||
UserName: username,
|
||||
Password: password,
|
||||
SupportedCommands: []byte{CmdConnect, CmdUDP},
|
||||
Addr: addr,
|
||||
ServerAddr: saddr,
|
||||
UDPExchanges: cs,
|
||||
TCPTimeout: tcpTimeout,
|
||||
UDPTimeout: udpTimeout,
|
||||
AssociatedUDP: cs1,
|
||||
UDPSrc: cs2,
|
||||
RunnerGroup: NewRunnerGroup(),
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Negotiate handle negotiate packet.
|
||||
// This method do not handle gssapi(0x01) method now.
|
||||
// Error or OK both replied.
|
||||
func (s *Server) Negotiate(rw io.ReadWriter) error {
|
||||
rq, err := NewNegotiationRequestFrom(rw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var got bool
|
||||
var m byte
|
||||
for _, m = range rq.Methods {
|
||||
if m == s.Method {
|
||||
got = true
|
||||
}
|
||||
}
|
||||
if !got {
|
||||
rp := NewNegotiationReply(MethodUnsupportAll)
|
||||
if _, err := rp.WriteTo(rw); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
rp := NewNegotiationReply(s.Method)
|
||||
if _, err := rp.WriteTo(rw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.Method == MethodUsernamePassword {
|
||||
urq, err := NewUserPassNegotiationRequestFrom(rw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if string(urq.Uname) != s.UserName || string(urq.Passwd) != s.Password {
|
||||
urp := NewUserPassNegotiationReply(UserPassStatusFailure)
|
||||
if _, err := urp.WriteTo(rw); err != nil {
|
||||
return err
|
||||
}
|
||||
return ErrUserPassAuth
|
||||
}
|
||||
urp := NewUserPassNegotiationReply(UserPassStatusSuccess)
|
||||
if _, err := urp.WriteTo(rw); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRequest get request packet from client, and check command according to SupportedCommands
|
||||
// Error replied.
|
||||
func (s *Server) GetRequest(rw io.ReadWriter) (*Request, error) {
|
||||
r, err := NewRequestFrom(rw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var supported bool
|
||||
for _, c := range s.SupportedCommands {
|
||||
if r.Cmd == c {
|
||||
supported = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !supported {
|
||||
var p *Reply
|
||||
if r.Atyp == ATYPIPv4 || r.Atyp == ATYPDomain {
|
||||
p = NewReply(RepCommandNotSupported, ATYPIPv4, []byte{0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00})
|
||||
} else {
|
||||
p = NewReply(RepCommandNotSupported, ATYPIPv6, []byte(net.IPv6zero), []byte{0x00, 0x00})
|
||||
}
|
||||
if _, err := p.WriteTo(rw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, ErrUnsupportCmd
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Run server
|
||||
func (s *Server) ListenAndServe(h Handler) error {
|
||||
if h == nil {
|
||||
s.Handle = &DefaultHandle{}
|
||||
} else {
|
||||
s.Handle = h
|
||||
}
|
||||
|
||||
addr, err := net.ResolveTCPAddr("tcp", s.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.RunnerGroup.Add(&Runner{
|
||||
Start: func() error {
|
||||
for {
|
||||
c, err := l.AcceptTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func(c *net.TCPConn) {
|
||||
defer c.Close()
|
||||
if err := s.Negotiate(c); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
r, err := s.GetRequest(c)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if err := s.Handle.TCPHandle(s, c, r); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}(c)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Stop: func() error {
|
||||
return l.Close()
|
||||
},
|
||||
})
|
||||
|
||||
addr1, err := net.ResolveUDPAddr("udp", s.Addr)
|
||||
if err != nil {
|
||||
l.Close()
|
||||
return err
|
||||
}
|
||||
s.UDPConn, err = net.ListenUDP("udp", addr1)
|
||||
if err != nil {
|
||||
l.Close()
|
||||
return err
|
||||
}
|
||||
s.RunnerGroup.Add(&Runner{
|
||||
Start: func() error {
|
||||
for {
|
||||
b := make([]byte, 65507)
|
||||
n, addr, err := s.UDPConn.ReadFromUDP(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func(addr *net.UDPAddr, b []byte) {
|
||||
d, err := NewDatagramFromBytes(b)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if d.Frag != 0x00 {
|
||||
log.Println("Ignore frag", d.Frag)
|
||||
return
|
||||
}
|
||||
if err := s.Handle.UDPHandle(s, addr, d); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}(addr, b[0:n])
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Stop: func() error {
|
||||
return s.UDPConn.Close()
|
||||
},
|
||||
})
|
||||
return s.RunnerGroup.Wait()
|
||||
}
|
||||
|
||||
// Stop server
|
||||
func (s *Server) Shutdown() error {
|
||||
return s.RunnerGroup.Done()
|
||||
}
|
||||
|
||||
// Handler handle tcp, udp request
|
||||
type Handler interface {
|
||||
// Request has not been replied yet
|
||||
TCPHandle(*Server, *net.TCPConn, *Request) error
|
||||
UDPHandle(*Server, *net.UDPAddr, *Datagram) error
|
||||
}
|
||||
|
||||
// DefaultHandle implements Handler interface
|
||||
type DefaultHandle struct {
|
||||
}
|
||||
|
||||
// TCPHandle auto handle request. You may prefer to do yourself.
|
||||
func (h *DefaultHandle) TCPHandle(s *Server, c *net.TCPConn, r *Request) error {
|
||||
if r.Cmd == CmdConnect {
|
||||
rc, err := r.Connect(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
go func() {
|
||||
var bf [1024 * 2]byte
|
||||
for {
|
||||
if s.TCPTimeout != 0 {
|
||||
if err := rc.SetDeadline(time.Now().Add(time.Duration(s.TCPTimeout) * time.Second)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
i, err := rc.Read(bf[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if _, err := c.Write(bf[0:i]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
var bf [1024 * 2]byte
|
||||
for {
|
||||
if s.TCPTimeout != 0 {
|
||||
if err := c.SetDeadline(time.Now().Add(time.Duration(s.TCPTimeout) * time.Second)); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
i, err := c.Read(bf[:])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if _, err := rc.Write(bf[0:i]); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if r.Cmd == CmdUDP {
|
||||
caddr, err := r.UDP(c, s.ServerAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ch := make(chan byte)
|
||||
defer close(ch)
|
||||
s.AssociatedUDP.Set(caddr.String(), ch, -1)
|
||||
defer s.AssociatedUDP.Delete(caddr.String())
|
||||
io.Copy(ioutil.Discard, c)
|
||||
if Debug {
|
||||
log.Printf("A tcp connection that udp %#v associated closed\n", caddr.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return ErrUnsupportCmd
|
||||
}
|
||||
|
||||
// UDPHandle auto handle packet. You may prefer to do yourself.
|
||||
func (h *DefaultHandle) UDPHandle(s *Server, addr *net.UDPAddr, d *Datagram) error {
|
||||
src := addr.String()
|
||||
var ch chan byte
|
||||
if s.LimitUDP {
|
||||
any, ok := s.AssociatedUDP.Get(src)
|
||||
if !ok {
|
||||
return fmt.Errorf("This udp address %s is not associated with tcp", src)
|
||||
}
|
||||
ch = any.(chan byte)
|
||||
}
|
||||
send := func(ue *UDPExchange, data []byte) error {
|
||||
select {
|
||||
case <-ch:
|
||||
return fmt.Errorf("This udp address %s is not associated with tcp", src)
|
||||
default:
|
||||
_, err := ue.RemoteConn.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Sent UDP data to remote. client: %#v server: %#v remote: %#v data: %#v\n", ue.ClientAddr.String(), ue.RemoteConn.LocalAddr().String(), ue.RemoteConn.RemoteAddr().String(), data)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
dst := d.Address()
|
||||
var ue *UDPExchange
|
||||
iue, ok := s.UDPExchanges.Get(src + dst)
|
||||
if ok {
|
||||
ue = iue.(*UDPExchange)
|
||||
return send(ue, d.Data)
|
||||
}
|
||||
|
||||
if Debug {
|
||||
log.Printf("Call udp: %#v\n", dst)
|
||||
}
|
||||
var laddr string
|
||||
any, ok := s.UDPSrc.Get(src + dst)
|
||||
if ok {
|
||||
laddr = any.(string)
|
||||
}
|
||||
rc, err := DialUDP("udp", laddr, dst)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "address already in use") && !strings.Contains(err.Error(), "can't assign requested address") {
|
||||
return err
|
||||
}
|
||||
rc, err = DialUDP("udp", "", dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
laddr = ""
|
||||
}
|
||||
if laddr == "" {
|
||||
s.UDPSrc.Set(src+dst, rc.LocalAddr().String(), -1)
|
||||
}
|
||||
ue = &UDPExchange{
|
||||
ClientAddr: addr,
|
||||
RemoteConn: rc,
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Created remote UDP conn for client. client: %#v server: %#v remote: %#v\n", addr.String(), ue.RemoteConn.LocalAddr().String(), d.Address())
|
||||
}
|
||||
if err := send(ue, d.Data); err != nil {
|
||||
ue.RemoteConn.Close()
|
||||
return err
|
||||
}
|
||||
s.UDPExchanges.Set(src+dst, ue, -1)
|
||||
go func(ue *UDPExchange, dst string) {
|
||||
defer func() {
|
||||
ue.RemoteConn.Close()
|
||||
s.UDPExchanges.Delete(ue.ClientAddr.String() + dst)
|
||||
}()
|
||||
var b [65507]byte
|
||||
for {
|
||||
select {
|
||||
case <-ch:
|
||||
if Debug {
|
||||
log.Printf("The tcp that udp address %s associated closed\n", ue.ClientAddr.String())
|
||||
}
|
||||
return
|
||||
default:
|
||||
if s.UDPTimeout != 0 {
|
||||
if err := ue.RemoteConn.SetDeadline(time.Now().Add(time.Duration(s.UDPTimeout) * time.Second)); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
n, err := ue.RemoteConn.Read(b[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Got UDP data from remote. client: %#v server: %#v remote: %#v data: %#v\n", ue.ClientAddr.String(), ue.RemoteConn.LocalAddr().String(), ue.RemoteConn.RemoteAddr().String(), b[0:n])
|
||||
}
|
||||
a, addr, port, err := ParseAddress(dst)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if a == ATYPDomain {
|
||||
addr = addr[1:]
|
||||
}
|
||||
d1 := NewDatagram(a, addr, port, b[0:n])
|
||||
if _, err := s.UDPConn.WriteToUDP(d1.Bytes(), ue.ClientAddr); err != nil {
|
||||
return
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Sent Datagram. client: %#v server: %#v remote: %#v data: %#v %#v %#v %#v %#v %#v datagram address: %#v\n", ue.ClientAddr.String(), ue.RemoteConn.LocalAddr().String(), ue.RemoteConn.RemoteAddr().String(), d1.Rsv, d1.Frag, d1.Atyp, d1.DstAddr, d1.DstPort, d1.Data, d1.Address())
|
||||
}
|
||||
}
|
||||
}
|
||||
}(ue, dst)
|
||||
return nil
|
||||
}
|
||||
282
socks5_txthinking/server_side.go
Normal file
282
socks5_txthinking/server_side.go
Normal file
@@ -0,0 +1,282 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrVersion is version error
|
||||
ErrVersion = errors.New("Invalid Version")
|
||||
// ErrUserPassVersion is username/password auth version error
|
||||
ErrUserPassVersion = errors.New("Invalid Version of Username Password Auth")
|
||||
// ErrBadRequest is bad request error
|
||||
ErrBadRequest = errors.New("Bad Request")
|
||||
)
|
||||
|
||||
// NewNegotiationRequestFrom read negotiation requst packet from client
|
||||
func NewNegotiationRequestFrom(r io.Reader) (*NegotiationRequest, error) {
|
||||
// memory strict
|
||||
bb := make([]byte, 2)
|
||||
if _, err := io.ReadFull(r, bb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bb[0] != Ver {
|
||||
return nil, ErrVersion
|
||||
}
|
||||
if bb[1] == 0 {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
ms := make([]byte, int(bb[1]))
|
||||
if _, err := io.ReadFull(r, ms); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Got NegotiationRequest: %#v %#v %#v\n", bb[0], bb[1], ms)
|
||||
}
|
||||
return &NegotiationRequest{
|
||||
Ver: bb[0],
|
||||
NMethods: bb[1],
|
||||
Methods: ms,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewNegotiationReply return negotiation reply packet can be writed into client
|
||||
func NewNegotiationReply(method byte) *NegotiationReply {
|
||||
return &NegotiationReply{
|
||||
Ver: Ver,
|
||||
Method: method,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo write negotiation reply packet into client
|
||||
func (r *NegotiationReply) WriteTo(w io.Writer) (int64, error) {
|
||||
i, err := w.Write([]byte{r.Ver, r.Method})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Sent NegotiationReply: %#v %#v\n", r.Ver, r.Method)
|
||||
}
|
||||
return int64(i), nil
|
||||
}
|
||||
|
||||
// NewUserPassNegotiationRequestFrom read user password negotiation request packet from client
|
||||
func NewUserPassNegotiationRequestFrom(r io.Reader) (*UserPassNegotiationRequest, error) {
|
||||
bb := make([]byte, 2)
|
||||
if _, err := io.ReadFull(r, bb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bb[0] != UserPassVer {
|
||||
return nil, ErrUserPassVersion
|
||||
}
|
||||
if bb[1] == 0 {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
ub := make([]byte, int(bb[1])+1)
|
||||
if _, err := io.ReadFull(r, ub); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ub[int(bb[1])] == 0 {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
p := make([]byte, int(ub[int(bb[1])]))
|
||||
if _, err := io.ReadFull(r, p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Got UserPassNegotiationRequest: %#v %#v %#v %#v %#v\n", bb[0], bb[1], ub[:int(bb[1])], ub[int(bb[1])], p)
|
||||
}
|
||||
return &UserPassNegotiationRequest{
|
||||
Ver: bb[0],
|
||||
Ulen: bb[1],
|
||||
Uname: ub[:int(bb[1])],
|
||||
Plen: ub[int(bb[1])],
|
||||
Passwd: p,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewUserPassNegotiationReply return negotiation username password reply packet can be writed into client
|
||||
func NewUserPassNegotiationReply(status byte) *UserPassNegotiationReply {
|
||||
return &UserPassNegotiationReply{
|
||||
Ver: UserPassVer,
|
||||
Status: status,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo write negotiation username password reply packet into client
|
||||
func (r *UserPassNegotiationReply) WriteTo(w io.Writer) (int64, error) {
|
||||
i, err := w.Write([]byte{r.Ver, r.Status})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Sent UserPassNegotiationReply: %#v %#v \n", r.Ver, r.Status)
|
||||
}
|
||||
return int64(i), nil
|
||||
}
|
||||
|
||||
// NewRequestFrom read requst packet from client
|
||||
func NewRequestFrom(r io.Reader) (*Request, error) {
|
||||
bb := make([]byte, 4)
|
||||
if _, err := io.ReadFull(r, bb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bb[0] != Ver {
|
||||
return nil, ErrVersion
|
||||
}
|
||||
var addr []byte
|
||||
if bb[3] == ATYPIPv4 {
|
||||
addr = make([]byte, 4)
|
||||
if _, err := io.ReadFull(r, addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if bb[3] == ATYPIPv6 {
|
||||
addr = make([]byte, 16)
|
||||
if _, err := io.ReadFull(r, addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if bb[3] == ATYPDomain {
|
||||
dal := make([]byte, 1)
|
||||
if _, err := io.ReadFull(r, dal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dal[0] == 0 {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
addr = make([]byte, int(dal[0]))
|
||||
if _, err := io.ReadFull(r, addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr = append(dal, addr...)
|
||||
} else {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
port := make([]byte, 2)
|
||||
if _, err := io.ReadFull(r, port); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Got Request: %#v %#v %#v %#v %#v %#v\n", bb[0], bb[1], bb[2], bb[3], addr, port)
|
||||
}
|
||||
return &Request{
|
||||
Ver: bb[0],
|
||||
Cmd: bb[1],
|
||||
Rsv: bb[2],
|
||||
Atyp: bb[3],
|
||||
DstAddr: addr,
|
||||
DstPort: port,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewReply return reply packet can be writed into client, bndaddr should not have domain length
|
||||
func NewReply(rep byte, atyp byte, bndaddr []byte, bndport []byte) *Reply {
|
||||
if atyp == ATYPDomain {
|
||||
bndaddr = append([]byte{byte(len(bndaddr))}, bndaddr...)
|
||||
}
|
||||
return &Reply{
|
||||
Ver: Ver,
|
||||
Rep: rep,
|
||||
Rsv: 0x00,
|
||||
Atyp: atyp,
|
||||
BndAddr: bndaddr,
|
||||
BndPort: bndport,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo write reply packet into client
|
||||
func (r *Reply) WriteTo(w io.Writer) (int64, error) {
|
||||
i, err := w.Write(append(append([]byte{r.Ver, r.Rep, r.Rsv, r.Atyp}, r.BndAddr...), r.BndPort...))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Sent Reply: %#v %#v %#v %#v %#v %#v\n", r.Ver, r.Rep, r.Rsv, r.Atyp, r.BndAddr, r.BndPort)
|
||||
}
|
||||
return int64(i), nil
|
||||
}
|
||||
|
||||
func NewDatagramFromBytes(bb []byte) (*Datagram, error) {
|
||||
n := len(bb)
|
||||
minl := 4
|
||||
if n < minl {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
var addr []byte
|
||||
if bb[3] == ATYPIPv4 {
|
||||
minl += 4
|
||||
if n < minl {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
addr = bb[minl-4 : minl]
|
||||
} else if bb[3] == ATYPIPv6 {
|
||||
minl += 16
|
||||
if n < minl {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
addr = bb[minl-16 : minl]
|
||||
} else if bb[3] == ATYPDomain {
|
||||
minl += 1
|
||||
if n < minl {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
l := bb[4]
|
||||
if l == 0 {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
minl += int(l)
|
||||
if n < minl {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
addr = bb[minl-int(l) : minl]
|
||||
addr = append([]byte{l}, addr...)
|
||||
} else {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
minl += 2
|
||||
if n <= minl {
|
||||
return nil, ErrBadRequest
|
||||
}
|
||||
port := bb[minl-2 : minl]
|
||||
data := bb[minl:]
|
||||
d := &Datagram{
|
||||
Rsv: bb[0:2],
|
||||
Frag: bb[2],
|
||||
Atyp: bb[3],
|
||||
DstAddr: addr,
|
||||
DstPort: port,
|
||||
Data: data,
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("Got Datagram. data: %#v %#v %#v %#v %#v %#v datagram address: %#v\n", d.Rsv, d.Frag, d.Atyp, d.DstAddr, d.DstPort, d.Data, d.Address())
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// NewDatagram return datagram packet can be writed into client, dstaddr should not have domain length
|
||||
func NewDatagram(atyp byte, dstaddr []byte, dstport []byte, data []byte) *Datagram {
|
||||
if atyp == ATYPDomain {
|
||||
dstaddr = append([]byte{byte(len(dstaddr))}, dstaddr...)
|
||||
}
|
||||
return &Datagram{
|
||||
Rsv: []byte{0x00, 0x00},
|
||||
Frag: 0x00,
|
||||
Atyp: atyp,
|
||||
DstAddr: dstaddr,
|
||||
DstPort: dstport,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes return []byte
|
||||
func (d *Datagram) Bytes() []byte {
|
||||
b := make([]byte, 0)
|
||||
b = append(b, d.Rsv...)
|
||||
b = append(b, d.Frag)
|
||||
b = append(b, d.Atyp)
|
||||
b = append(b, d.DstAddr...)
|
||||
b = append(b, d.DstPort...)
|
||||
b = append(b, d.Data...)
|
||||
return b
|
||||
}
|
||||
119
socks5_txthinking/socks5.go
Normal file
119
socks5_txthinking/socks5.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package socks5
|
||||
|
||||
const (
|
||||
// Ver is socks protocol version
|
||||
Ver byte = 0x05
|
||||
|
||||
// MethodNone is none method
|
||||
MethodNone byte = 0x00
|
||||
// MethodGSSAPI is gssapi method
|
||||
MethodGSSAPI byte = 0x01 // MUST support // todo
|
||||
// MethodUsernamePassword is username/assword auth method
|
||||
MethodUsernamePassword byte = 0x02 // SHOULD support
|
||||
// MethodUnsupportAll means unsupport all given methods
|
||||
MethodUnsupportAll byte = 0xFF
|
||||
|
||||
// UserPassVer is username/password auth protocol version
|
||||
UserPassVer byte = 0x01
|
||||
// UserPassStatusSuccess is success status of username/password auth
|
||||
UserPassStatusSuccess byte = 0x00
|
||||
// UserPassStatusFailure is failure status of username/password auth
|
||||
UserPassStatusFailure byte = 0x01 // just other than 0x00
|
||||
|
||||
// CmdConnect is connect command
|
||||
CmdConnect byte = 0x01
|
||||
// CmdBind is bind command
|
||||
CmdBind byte = 0x02
|
||||
// CmdUDP is UDP command
|
||||
CmdUDP byte = 0x03
|
||||
|
||||
// ATYPIPv4 is ipv4 address type
|
||||
ATYPIPv4 byte = 0x01 // 4 octets
|
||||
// ATYPDomain is domain address type
|
||||
ATYPDomain byte = 0x03 // The first octet of the address field contains the number of octets of name that follow, there is no terminating NUL octet.
|
||||
// ATYPIPv6 is ipv6 address type
|
||||
ATYPIPv6 byte = 0x04 // 16 octets
|
||||
|
||||
// RepSuccess means that success for repling
|
||||
RepSuccess byte = 0x00
|
||||
// RepServerFailure means the server failure
|
||||
RepServerFailure byte = 0x01
|
||||
// RepNotAllowed means the request not allowed
|
||||
RepNotAllowed byte = 0x02
|
||||
// RepNetworkUnreachable means the network unreachable
|
||||
RepNetworkUnreachable byte = 0x03
|
||||
// RepHostUnreachable means the host unreachable
|
||||
RepHostUnreachable byte = 0x04
|
||||
// RepConnectionRefused means the connection refused
|
||||
RepConnectionRefused byte = 0x05
|
||||
// RepTTLExpired means the TTL expired
|
||||
RepTTLExpired byte = 0x06
|
||||
// RepCommandNotSupported means the request command not supported
|
||||
RepCommandNotSupported byte = 0x07
|
||||
// RepAddressNotSupported means the request address not supported
|
||||
RepAddressNotSupported byte = 0x08
|
||||
)
|
||||
|
||||
// NegotiationRequest is the negotiation reqeust packet
|
||||
type NegotiationRequest struct {
|
||||
Ver byte
|
||||
NMethods byte
|
||||
Methods []byte // 1-255 bytes
|
||||
}
|
||||
|
||||
// NegotiationReply is the negotiation reply packet
|
||||
type NegotiationReply struct {
|
||||
Ver byte
|
||||
Method byte
|
||||
}
|
||||
|
||||
// UserPassNegotiationRequest is the negotiation username/password reqeust packet
|
||||
type UserPassNegotiationRequest struct {
|
||||
Ver byte
|
||||
Ulen byte
|
||||
Uname []byte // 1-255 bytes
|
||||
Plen byte
|
||||
Passwd []byte // 1-255 bytes
|
||||
}
|
||||
|
||||
// UserPassNegotiationReply is the negotiation username/password reply packet
|
||||
type UserPassNegotiationReply struct {
|
||||
Ver byte
|
||||
Status byte
|
||||
}
|
||||
|
||||
// Request is the request packet
|
||||
type Request struct {
|
||||
Ver byte
|
||||
Cmd byte
|
||||
Rsv byte // 0x00
|
||||
Atyp byte
|
||||
DstAddr []byte
|
||||
DstPort []byte // 2 bytes
|
||||
}
|
||||
|
||||
// Reply is the reply packet
|
||||
type Reply struct {
|
||||
Ver byte
|
||||
Rep byte
|
||||
Rsv byte // 0x00
|
||||
Atyp byte
|
||||
// CONNECT socks server's address which used to connect to dst addr
|
||||
// BIND ...
|
||||
// UDP socks server's address which used to connect to dst addr
|
||||
BndAddr []byte
|
||||
// CONNECT socks server's port which used to connect to dst addr
|
||||
// BIND ...
|
||||
// UDP socks server's port which used to connect to dst addr
|
||||
BndPort []byte // 2 bytes
|
||||
}
|
||||
|
||||
// Datagram is the UDP packet
|
||||
type Datagram struct {
|
||||
Rsv []byte // 0x00 0x00
|
||||
Frag byte
|
||||
Atyp byte
|
||||
DstAddr []byte
|
||||
DstPort []byte // 2 bytes
|
||||
Data []byte
|
||||
}
|
||||
59
socks5_txthinking/udp.go
Normal file
59
socks5_txthinking/udp.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
// UDP remote conn which u want to connect with your dialer.
|
||||
// Error or OK both replied.
|
||||
// Addr can be used to associate TCP connection with the coming UDP connection.
|
||||
func (r *Request) UDP(c net.Conn, serverAddr net.Addr) (net.Addr, error) {
|
||||
var clientAddr net.Addr
|
||||
var err error
|
||||
if bytes.Compare(r.DstPort, []byte{0x00, 0x00}) == 0 {
|
||||
// If the requested Host/Port is all zeros, the relay should simply use the Host/Port that sent the request.
|
||||
// https://stackoverflow.com/questions/62283351/how-to-use-socks-5-proxy-with-tidudpclient-properly
|
||||
clientAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String())
|
||||
} else {
|
||||
clientAddr, err = net.ResolveUDPAddr("udp", r.Address())
|
||||
}
|
||||
if err != nil {
|
||||
var p *Reply
|
||||
if r.Atyp == ATYPIPv4 || r.Atyp == ATYPDomain {
|
||||
p = NewReply(RepHostUnreachable, ATYPIPv4, []byte{0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00})
|
||||
} else {
|
||||
p = NewReply(RepHostUnreachable, ATYPIPv6, []byte(net.IPv6zero), []byte{0x00, 0x00})
|
||||
}
|
||||
if _, err := p.WriteTo(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if Debug {
|
||||
log.Println("Client wants to start UDP talk use", clientAddr.String())
|
||||
}
|
||||
a, addr, port, err := ParseAddress(serverAddr.String())
|
||||
if err != nil {
|
||||
var p *Reply
|
||||
if r.Atyp == ATYPIPv4 || r.Atyp == ATYPDomain {
|
||||
p = NewReply(RepHostUnreachable, ATYPIPv4, []byte{0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00})
|
||||
} else {
|
||||
p = NewReply(RepHostUnreachable, ATYPIPv6, []byte(net.IPv6zero), []byte{0x00, 0x00})
|
||||
}
|
||||
if _, err := p.WriteTo(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if a == ATYPDomain {
|
||||
addr = addr[1:]
|
||||
}
|
||||
p := NewReply(RepSuccess, a, addr, port)
|
||||
if _, err := p.WriteTo(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return clientAddr, nil
|
||||
}
|
||||
135
socks5_txthinking/util.go
Normal file
135
socks5_txthinking/util.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ParseAddress format address x.x.x.x:xx to raw address.
|
||||
// addr contains domain length
|
||||
func ParseAddress(address string) (a byte, addr []byte, port []byte, err error) {
|
||||
var h, p string
|
||||
h, p, err = net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ip := net.ParseIP(h)
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
a = ATYPIPv4
|
||||
addr = []byte(ip4)
|
||||
} else if ip6 := ip.To16(); ip6 != nil {
|
||||
a = ATYPIPv6
|
||||
addr = []byte(ip6)
|
||||
} else {
|
||||
a = ATYPDomain
|
||||
addr = []byte{byte(len(h))}
|
||||
addr = append(addr, []byte(h)...)
|
||||
}
|
||||
i, _ := strconv.Atoi(p)
|
||||
port = make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(port, uint16(i))
|
||||
return
|
||||
}
|
||||
|
||||
// bytes to address
|
||||
// addr contains domain length
|
||||
func ParseBytesAddress(b []byte) (a byte, addr []byte, port []byte, err error) {
|
||||
if len(b) < 1 {
|
||||
err = errors.New("Invalid address")
|
||||
return
|
||||
}
|
||||
a = b[0]
|
||||
if a == ATYPIPv4 {
|
||||
if len(b) < 1+4+2 {
|
||||
err = errors.New("Invalid address")
|
||||
return
|
||||
}
|
||||
addr = b[1 : 1+4]
|
||||
port = b[1+4 : 1+4+2]
|
||||
return
|
||||
}
|
||||
if a == ATYPIPv6 {
|
||||
if len(b) < 1+16+2 {
|
||||
err = errors.New("Invalid address")
|
||||
return
|
||||
}
|
||||
addr = b[1 : 1+16]
|
||||
port = b[1+16 : 1+16+2]
|
||||
return
|
||||
}
|
||||
if a == ATYPDomain {
|
||||
if len(b) < 1+1 {
|
||||
err = errors.New("Invalid address")
|
||||
return
|
||||
}
|
||||
l := int(b[1])
|
||||
if len(b) < 1+1+l+2 {
|
||||
err = errors.New("Invalid address")
|
||||
return
|
||||
}
|
||||
addr = b[1 : 1+1+l]
|
||||
port = b[1+1+l : 1+1+l+2]
|
||||
return
|
||||
}
|
||||
err = errors.New("Invalid address")
|
||||
return
|
||||
}
|
||||
|
||||
// ToAddress format raw address to x.x.x.x:xx
|
||||
// addr contains domain length
|
||||
func ToAddress(a byte, addr []byte, port []byte) string {
|
||||
var h, p string
|
||||
if a == ATYPIPv4 || a == ATYPIPv6 {
|
||||
h = net.IP(addr).String()
|
||||
}
|
||||
if a == ATYPDomain {
|
||||
if len(addr) < 1 {
|
||||
return ""
|
||||
}
|
||||
if len(addr) < int(addr[0])+1 {
|
||||
return ""
|
||||
}
|
||||
h = string(addr[1:])
|
||||
}
|
||||
p = strconv.Itoa(int(binary.BigEndian.Uint16(port)))
|
||||
return net.JoinHostPort(h, p)
|
||||
}
|
||||
|
||||
// Address return request address like ip:xx
|
||||
func (r *Request) Address() string {
|
||||
var s string
|
||||
if r.Atyp == ATYPDomain {
|
||||
s = bytes.NewBuffer(r.DstAddr[1:]).String()
|
||||
} else {
|
||||
s = net.IP(r.DstAddr).String()
|
||||
}
|
||||
p := strconv.Itoa(int(binary.BigEndian.Uint16(r.DstPort)))
|
||||
return net.JoinHostPort(s, p)
|
||||
}
|
||||
|
||||
// Address return request address like ip:xx
|
||||
func (r *Reply) Address() string {
|
||||
var s string
|
||||
if r.Atyp == ATYPDomain {
|
||||
s = bytes.NewBuffer(r.BndAddr[1:]).String()
|
||||
} else {
|
||||
s = net.IP(r.BndAddr).String()
|
||||
}
|
||||
p := strconv.Itoa(int(binary.BigEndian.Uint16(r.BndPort)))
|
||||
return net.JoinHostPort(s, p)
|
||||
}
|
||||
|
||||
// Address return datagram address like ip:xx
|
||||
func (d *Datagram) Address() string {
|
||||
var s string
|
||||
if d.Atyp == ATYPDomain {
|
||||
s = bytes.NewBuffer(d.DstAddr[1:]).String()
|
||||
} else {
|
||||
s = net.IP(d.DstAddr).String()
|
||||
}
|
||||
p := strconv.Itoa(int(binary.BigEndian.Uint16(d.DstPort)))
|
||||
return net.JoinHostPort(s, p)
|
||||
}
|
||||
9
socks5_txthinking/util_test.go
Normal file
9
socks5_txthinking/util_test.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package socks5
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseAddress(t *testing.T) {
|
||||
t.Log(ParseAddress("127.0.0.1:80"))
|
||||
t.Log(ParseAddress("[::1]:80"))
|
||||
t.Log(ParseAddress("a.com:80"))
|
||||
}
|
||||
Reference in New Issue
Block a user