SoFunction
Updated on 2025-03-05

Golang's operation of connecting to mysql through ssh proxy

I won't say much nonsense, let's just read the code~

package main
import (
	"bytes"
	"context"
	"database/sql"
	"errors"
	"fmt"
	"/go-sql-driver/mysql"
	"/x/crypto/ssh"
	"io"
	"io/ioutil"
	"net"
	"os"
)
type ViaSSHDialer struct {
	client *
	_ *
}
 
func (self *ViaSSHDialer) Dial(context ,addr string) (, error) {
	return ("tcp", addr)
}
type remoteScriptType byte
type remoteShellType byte
 
const (
	cmdLine remoteScriptType = iota
	rawScript
	scriptFile 
	interactiveShell remoteShellType = iota
	nonInteractiveShell
)
 
type Client struct {
	client *
}
func main() {
	client, err := DialWithPasswd("ip:port", "user", "password")
	if err != nil {
		panic(err)
	}
	out, err := ("ls -l").Output()
	if err != nil {
		panic(err)
	}
	(string(out))
	// Now we register the ViaSSHDialer with the ssh connection as a parameter
	("mysql+tcp", (&ViaSSHDialer{,nil}).Dial)
	//("mysql+tcp", (&ViaSSHDialer{}).Dial)
	if db, err := ("mysql", ("%s:%s@mysql+tcp(%s)/%s","Aiqitest", "uf6amk146d2aoemi7", "139.196.174.234:3306", "Aiqitest"));
	err == nil {
		("Successfully connected to the db\n")
		if rows, err := ("SELECT id, name FROM table ORDER BY id"); err == nil {
			for () {
				var id int64
				var name string
				(&id, &name)
				("ID: %d Name: %s\n", id, name)
			}
			()
		} else {
			("Failure: %s", ())
		}
 
		()
}
	}
 
// DialWithPasswd starts a client connection to the given SSH server with passwd authmethod.
func DialWithPasswd(addr, user, passwd string) (*Client, error) {
	config := &{
		User: user,
		Auth: []{
			(passwd),
		},
		HostKeyCallback: (func(hostname string, remote , key ) error { return nil }),
	}
 
	return Dial("tcp", addr, config)
}
 
// DialWithKey starts a client connection to the given SSH server with key authmethod.
func DialWithKey(addr, user, keyfile string) (*Client, error) {
	key, err := (keyfile)
	if err != nil {
		return nil, err
	}
 
	signer, err := (key)
	if err != nil {
		return nil, err
	}
 
	config := &{
		User: user,
		Auth: []{
			(signer),
		},
		HostKeyCallback: (func(hostname string, remote , key ) error { return nil }),
	}
 
	return Dial("tcp", addr, config)
}
 
// DialWithKeyWithPassphrase same as DialWithKey but with a passphrase to decrypt the private key
func DialWithKeyWithPassphrase(addr, user, keyfile string, passphrase string) (*Client, error) {
	key, err := (keyfile)
	if err != nil {
		return nil, err
	}
 
	signer, err := (key, []byte(passphrase))
	if err != nil {
		return nil, err
	}
 
	config := &{
		User: user,
		Auth: []{
			(signer),
		},
		HostKeyCallback: (func(hostname string, remote , key ) error { return nil }),
	} 
	return Dial("tcp", addr, config)
}
 
// Dial starts a client connection to the given SSH server.
// This is wrap the 
func Dial(network, addr string, config *) (*Client, error) {
	client, err := (network, addr, config)
	if err != nil {
		return nil, err
	}
	return &Client{
		client: client,
	}, nil
}
 
func (c *Client) Close() error {
	return ()
}
 
// Cmd create a command on client
func (c *Client) Cmd(cmd string) *remoteScript {
	return &remoteScript{
		_type: cmdLine,
		client: ,
		script: (cmd + "\n"),
	}
}
 
// Script
func (c *Client) Script(script string) *remoteScript {
	return &remoteScript{
		_type: rawScript,
		client: ,
		script: (script + "\n"),
	}
}
 
// ScriptFile
func (c *Client) ScriptFile(fname string) *remoteScript {
	return &remoteScript{
		_type:   scriptFile,
		client:   ,
		scriptFile: fname,
	}
}
 
type remoteScript struct {
	client   *
	_type   remoteScriptType
	script   *
	scriptFile string
	err    error
 
	stdout 
	stderr 
}
 
// Run
func (rs *remoteScript) Run() error {
	if  != nil {
		()
		return 
	}
 
	if rs._type == cmdLine {
		return ()
	} else if rs._type == rawScript {
		return ()
	} else if rs._type == scriptFile {
		return ()
	} else {
		return ("Not supported remoteScript type")
	}
}
 
func (rs *remoteScript) Output() ([]byte, error) {
	if  != nil {
		return nil, ("Stdout already set")
	}
	var out 
	 = &out
	err := ()
	return (), err
}
 
func (rs *remoteScript) SmartOutput() ([]byte, error) {
	if  != nil {
		return nil, ("Stdout already set")
	}
	if  != nil {
		return nil, ("Stderr already set")
	}
 
	var (
		stdout 
		stderr 
	)
	 = &stdout
	 = &stderr
	err := ()
	if err != nil {
		return (), err
	}
	return (), err
}
 
func (rs *remoteScript) Cmd(cmd string) *remoteScript {
	_, err := (cmd + "\n")
	if err != nil {
		 = err
	}
	return rs
}
 
func (rs *remoteScript) SetStdio(stdout, stderr ) *remoteScript {
	 = stdout
	 = stderr
	return rs
}
 
func (rs *remoteScript) runCmd(cmd string) error {
	session, err := ()
	if err != nil {
		return err
	}
	defer ()
 
	 = 
	 = 
 
	if err := (cmd); err != nil {
		return err
	}
	return nil
}
 
func (rs *remoteScript) runCmds() error {
	for {
		statment, err := ('\n')
		if err ==  {
			break
		}
		if err != nil {
			return err
		}
 
		if err := (statment); err != nil {
			return err
		}
	}
 
	return nil
}
 
func (rs *remoteScript) runScript() error {
	session, err := ()
	if err != nil {
		return err
	}
 
	 = 
	 = 
	 =  
	if err := (); err != nil {
		return err
	}
	if err := (); err != nil {
		return err
	}
 
	return nil
}
 
func (rs *remoteScript) runScriptFile() error {
	var buffer 
	file, err := ()
	if err != nil {
		return err
	}
	_, err = (&buffer, file)
	if err != nil {
		return err
	}
 
	 = &buffer
	return ()
}
 
type remoteShell struct {
	client     *
	requestPty   bool
	terminalConfig *TerminalConfig
 
	stdin 
	stdout 
	stderr 
}
 
type TerminalConfig struct {
	Term  string
	Hight int
	Weight int
	Modes 
}
 
// Terminal create a interactive shell on client.
func (c *Client) Terminal(config *TerminalConfig) *remoteShell {
	return &remoteShell{
		client:     ,
		terminalConfig: config,
		requestPty:   true,
	}
}
 
// Shell create a noninteractive shell on client.
func (c *Client) Shell() *remoteShell {
	return &remoteShell{
		client:   ,
		requestPty: false,
	}
}
 
func (rs *remoteShell) SetStdio(stdin , stdout, stderr ) *remoteShell {
	 = stdin
	 = stdout
	 = stderr
	return rs
}
 
// Start start a remote shell on client
func (rs *remoteShell) Start() error {
	session, err := ()
	if err != nil {
		return err
	}
	defer ()
 
	if  == nil {
		 = 
	} else {
		 = 
	}
	if  == nil {
		 = 
	} else {
		 = 
	}
	if  == nil {
		 = 
	} else {
		 = 
	}
 
	if  {
		tc := 
		if tc == nil {
			tc = &TerminalConfig{
				Term:  "xterm",
				Hight: 40,
				Weight: 80,
			}
		}
		if err := (, , , ); err != nil {
			return err
		}
	}
 
	if err := (); err != nil {
		return err
	}
 
	if err := (); err != nil {
		return err
	} 
	return nil
}

Supplement: Use golang to write socks5 proxy server 2-ssh remote proxy

Last time I used golang to implement the local socks5 proxy, but using the proxy is of course for harmonious Internet access, so this time I will introduce the use of ssh to implement the remote proxy, using the official ssh package

/x/crypto/ssh

It's not difficult to connect to ssh with golang

Read the key, set the configuration, and connect to the server is OK (it is not recommended to connect to ssh with username + password)

 b, err := ("/home/myml/.ssh/id_rsa")
 if err != nil {
 (err)
 return
 }
 pKey, err := (b)
 if err != nil {
 (err)
 return
 }
 config := {
 User: "userName",
 Auth: []{
  (pKey),
 },
 }
 client, err = ("tcp", "Host:22", &config)
 if err != nil {
 (err)
 return
 }
 ("Connecting to the server successfully")
 defer ()

In this way, you get a client, which has a Dial() function to create socket connections. This is created on the server, which can break through network restrictions. Add to the last sock5 proxy, change it to, and the server can access it with the proxy.

 server, err := ("tcp", addr)
 if err != nil {
 (err)
 return
 }
 ([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
 go (server, conn)
 (conn, server)

Below is the code that can successfully run and perform remote proxy (test in Chrome and proxychains). The ssh server and configuration information must be modified to your own.

// socks5ProxyProxy project 
package main 
import (
 "bytes"
 "encoding/binary"
 "fmt"
 "io"
 "io/ioutil"
 "log"
 "net" 
 "/x/crypto/ssh"
)
 
func socks5Proxy(conn ) {
 defer ()
 
 var b [1024]byte 
 n, err := (b[:])
 if err != nil {
 (err)
 return
 }
 ("% x", b[:n]) 
 ([]byte{0x05, 0x00})
 
 n, err = (b[:])
 if err != nil {
 (err)
 return
 }
 ("% x", b[:n])
 
 var addr string
 switch b[3] {
 case 0x01:
 sip := sockIP{}
 if err := ((b[4:n]), , &sip); err != nil {
  ("Request parsing error")
  return
 }
 addr = ()
 case 0x03:
 host := string(b[5 : n-2])
 var port uint16
 err = ((b[n-2:n]), , &port)
 if err != nil {
  (err)
  return
 }
 addr = ("%s:%d", host, port)
 }
 
 server, err := ("tcp", addr)
 if err != nil {
 (err)
 return
 }
 ([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
 go (server, conn)
 (conn, server)
}
 
type sockIP struct {
 A, B, C, D byte
 PORT    uint16
}
 
func (ip sockIP) toAddr() string {
 return ("%d.%d.%d.%d:%d", , , , , )
}
 
func socks5ProxyStart() {
 ( | )
 
 server, err := ("tcp", ":8080")
 if err != nil {
 (err)
 }
 defer ()
 ("Start accept connections")
 for {
 client, err := ()
 if err != nil {
  (err)
  return
 }
 ("A new connection")
 go socks5Proxy(client)
 }
}
 
var client * 
func main() {
 b, err := ("/home/myml/.ssh/id_rsa")
 if err != nil {
 (err)
 return
 }
 pKey, err := (b)
 if err != nil {
 (err)
 return
 }
 config := {
 User: "user",
 Auth: []{
  (pKey),
 },
 }
 client, err = ("tcp", "host:22", &config)
 if err != nil {
 (err)
 return
 }
 ("Connecting to the server successfully")
 defer ()
 ()
 socks5ProxyStart()
 return
}
 

The above is personal experience. I hope you can give you a reference and I hope you can support me more. If there are any mistakes or no complete considerations, I would like to give you advice.