[ Cmii ] [ Octopus ] - add all image tag back up; add socks5 proxy

This commit is contained in:
zeaslity
2024-03-01 09:31:37 +08:00
parent 9d5ca4b50f
commit 52c360eb49
29 changed files with 2886 additions and 83 deletions

View File

@@ -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:

View File

@@ -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 {

View File

@@ -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 !")

View File

@@ -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

View File

@@ -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)

View File

@@ -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
View 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
View 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
View File

@@ -0,0 +1,103 @@
## socks5
[中文](README_ZH.md)
[![Go Report Card](https://goreportcard.com/badge/github.com/txthinking/socks5)](https://goreportcard.com/report/github.com/txthinking/socks5)
[![GoDoc](https://godoc.org/github.com/txthinking/socks5?status.svg)](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

View File

@@ -0,0 +1,102 @@
## socks5
[English](README.md)
[![Go Report Card](https://goreportcard.com/badge/github.com/txthinking/socks5)](https://goreportcard.com/report/github.com/txthinking/socks5)
[![GoDoc](https://godoc.org/github.com/txthinking/socks5?status.svg)](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
View 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
View 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
}

View 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
}

View 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
}

View 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
View 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
View 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
View 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)
}

View File

@@ -0,0 +1,5 @@
package socks5
func main() {
}

View 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...)
}
}

View 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())
}
}

View 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
}

View 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
View 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
}

View 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
View 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
View 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
View 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)
}

View 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"))
}