Retry, Refactoring & Doc
+ Refactoring of some functions + Added the retrying of all three connections + Added the documentation to the code + Improved the error messages
This commit is contained in:
parent
cc97b844c0
commit
6ae3b46fc1
28
AcceptClients.go
Normal file
28
AcceptClients.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func acceptClients(connection net.Listener, config *ssh.ClientConfig) {
|
||||||
|
|
||||||
|
// Endless loop
|
||||||
|
for {
|
||||||
|
|
||||||
|
// Accept (another) client connection:
|
||||||
|
if localConn, err := connection.Accept(); err != nil {
|
||||||
|
|
||||||
|
// Fail
|
||||||
|
log.Printf("Accepting a client failed: %s\n", err.Error())
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Success
|
||||||
|
log.Println(`Client accepted.`)
|
||||||
|
|
||||||
|
// Start the forwarding:
|
||||||
|
go forward(localConn, config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
Constants.go
Normal file
7
Constants.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxRetriesLocal = 16 // How many retries are allowed to create the local end-point?
|
||||||
|
maxRetriesRemote = 16 // How many retries are allowed to create the remote end-point?
|
||||||
|
maxRetriesServer = 16 // How many retries are allowed to create the SSH server's connection?
|
||||||
|
)
|
35
CreateLocalEndPoint.go
Normal file
35
CreateLocalEndPoint.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createLocalEndPoint() (localListener net.Listener) {
|
||||||
|
|
||||||
|
// Loop for the necessary retries
|
||||||
|
for {
|
||||||
|
|
||||||
|
// Try to create the local end-point
|
||||||
|
if localListenerObj, err := net.Listen(`tcp`, localAddrString); err != nil {
|
||||||
|
|
||||||
|
// It was not able to create the end-point:
|
||||||
|
currentRetriesLocal++
|
||||||
|
log.Printf("Was not able to create the local end-point %s: %s\n", localAddrString, err.Error())
|
||||||
|
|
||||||
|
// Is another retry possible?
|
||||||
|
if currentRetriesLocal < maxRetriesLocal {
|
||||||
|
log.Println(`Retry...`)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
} else {
|
||||||
|
log.Fatalln(`No more retries for the local end-point: ` + localAddrString) // => Exit
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Success!
|
||||||
|
log.Println(`Listen to local address ` + localAddrString)
|
||||||
|
localListener = localListenerObj
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
Forward.go
72
Forward.go
@ -4,20 +4,74 @@ import (
|
|||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func forward(localConn net.Conn, config *ssh.ClientConfig) {
|
func forward(localConn net.Conn, config *ssh.ClientConfig) {
|
||||||
|
|
||||||
sshClientConn, err := ssh.Dial("tcp", serverAddrString, config)
|
currentRetriesServer := 0
|
||||||
if err != nil {
|
currentRetriesRemote := 0
|
||||||
log.Printf("ssh.Dial failed: %s\n", err)
|
var sshClientConnection *ssh.Client = nil
|
||||||
return
|
|
||||||
|
// Loop for retries:
|
||||||
|
for {
|
||||||
|
|
||||||
|
// Try to connect to the SSD server:
|
||||||
|
if sshClientConn, err := ssh.Dial(`tcp`, serverAddrString, config); err != nil {
|
||||||
|
|
||||||
|
// Failed:
|
||||||
|
currentRetriesServer++
|
||||||
|
log.Printf("Was not able to connect with the SSH server %s: %s\n", serverAddrString, err.Error())
|
||||||
|
|
||||||
|
// Is a retry alowed?
|
||||||
|
if currentRetriesServer < maxRetriesServer {
|
||||||
|
log.Println(`Retry...`)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// After the return, this thread is closed down. The client can try it again...
|
||||||
|
log.Println(`No more retries for connecting the SSH server.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Success:
|
||||||
|
log.Println(`Connected to the SSH server ` + serverAddrString)
|
||||||
|
sshClientConnection = sshClientConn
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sshConn, err := sshClientConn.Dial("tcp", remoteAddrString); err != nil {
|
// Loop for retries:
|
||||||
log.Println(`Was not able to create the tunnel: ` + err.Error())
|
for {
|
||||||
} else {
|
|
||||||
go transfer(localConn, sshConn)
|
// Try to create the remote end-point:
|
||||||
go transfer(sshConn, localConn)
|
if sshConn, err := sshClientConnection.Dial(`tcp`, remoteAddrString); err != nil {
|
||||||
|
|
||||||
|
// Failed:
|
||||||
|
currentRetriesRemote++
|
||||||
|
log.Printf("Was not able to create the remote end-point %s: %s\n", remoteAddrString, err.Error())
|
||||||
|
|
||||||
|
// Is another retry allowed?
|
||||||
|
if currentRetriesRemote < maxRetriesRemote {
|
||||||
|
log.Println(`Retry...`)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// After the return, this thread is closed down. The client can try it again...
|
||||||
|
log.Println(`No more retries for connecting the remote end-point.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Fine, the connections are up and ready :-)
|
||||||
|
log.Printf("The remote end-point %s is connected.\n", remoteAddrString)
|
||||||
|
|
||||||
|
// Create the transfers to/from both sides (two new threads are created for this):
|
||||||
|
go transfer(localConn, sshConn, `Local => Remote`)
|
||||||
|
go transfer(sshConn, localConn, `Remote => Local`)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,10 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Another auth. method.
|
||||||
func keyboardInteractiveChallenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
func keyboardInteractiveChallenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
||||||
|
|
||||||
|
// Log all the provided data:
|
||||||
log.Println(`User: ` + user)
|
log.Println(`User: ` + user)
|
||||||
log.Println(`Instruction: ` + instruction)
|
log.Println(`Instruction: ` + instruction)
|
||||||
log.Println(`Questions:`)
|
log.Println(`Questions:`)
|
||||||
@ -13,11 +15,19 @@ func keyboardInteractiveChallenge(user, instruction string, questions []string,
|
|||||||
log.Println(q)
|
log.Println(q)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// How many questions are asked?
|
||||||
countQuestions := len(questions)
|
countQuestions := len(questions)
|
||||||
answers = make([]string, countQuestions, countQuestions)
|
|
||||||
|
|
||||||
if countQuestions > 0 {
|
if countQuestions == 1 {
|
||||||
|
|
||||||
|
// We expect that in this case (only one question is asked), that the server want to know the password ;-)
|
||||||
|
answers = make([]string, countQuestions, countQuestions)
|
||||||
answers[0] = password
|
answers[0] = password
|
||||||
|
|
||||||
|
} else if countQuestions > 1 {
|
||||||
|
|
||||||
|
// After logging, this call will exit the whole program:
|
||||||
|
log.Fatalln(`The SSH server is asking multiple questions! This program cannot handle this case.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = nil
|
err = nil
|
||||||
|
28
Main.go
28
Main.go
@ -2,13 +2,18 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"log"
|
"runtime"
|
||||||
"net"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
// Allow Go to use all CPUs:
|
||||||
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
||||||
|
// Read the configuration from the command-line args:
|
||||||
readFlags()
|
readFlags()
|
||||||
|
|
||||||
|
// Create the SSH configuration:
|
||||||
config := &ssh.ClientConfig{
|
config := &ssh.ClientConfig{
|
||||||
User: username,
|
User: username,
|
||||||
Auth: []ssh.AuthMethod{
|
Auth: []ssh.AuthMethod{
|
||||||
@ -18,20 +23,9 @@ func main() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
localListener, err := net.Listen(`tcp`, localAddrString)
|
// Create the local end-point:
|
||||||
if err != nil {
|
localListener := createLocalEndPoint()
|
||||||
log.Printf("net.Listen failed: %v\n", err)
|
|
||||||
} else {
|
|
||||||
log.Println(`Listen to local address.`)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
// Accept client connections (will block forever):
|
||||||
localConn, err := localListener.Accept()
|
acceptClients(localListener, config)
|
||||||
if err != nil {
|
|
||||||
log.Printf("listen.Accept failed: %v\n", err)
|
|
||||||
} else {
|
|
||||||
log.Println(`Accepted a client.`)
|
|
||||||
go forward(localConn, config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
// Just a callback function for the password request.
|
||||||
func passwordCallback() (string, error) {
|
func passwordCallback() (string, error) {
|
||||||
return password, nil
|
return password, nil
|
||||||
}
|
}
|
||||||
|
16
Transfer.go
16
Transfer.go
@ -5,11 +5,21 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func transfer(fromReader io.Reader, toWriter io.Writer) {
|
// The transfer function.
|
||||||
|
func transfer(fromReader io.Reader, toWriter io.Writer, name string) {
|
||||||
|
|
||||||
|
log.Printf("%s transfer started.", name)
|
||||||
|
|
||||||
|
// This call blocks until the client or service will close the connection.
|
||||||
|
// Therefore, this call maybe takes hours or even longer. Concern, may this
|
||||||
|
// program will be used to connect multiple servers to make e.g. a database
|
||||||
|
// available...
|
||||||
if _, err := io.Copy(toWriter, fromReader); err != nil {
|
if _, err := io.Copy(toWriter, fromReader); err != nil {
|
||||||
log.Printf("io.Copy failed: %v\n", err)
|
|
||||||
|
// In this case, we do not fail the whole program: Regarding how the client
|
||||||
|
// or the service was e.g. shut down, the error may only means 'client has been closed'.
|
||||||
|
log.Printf("%s transfer failed: %s\n", name, err.Error())
|
||||||
} else {
|
} else {
|
||||||
log.Println(`Transfer closed.`)
|
log.Printf("%s transfer closed.\n", name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
Variables.go
11
Variables.go
@ -1,9 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
var (
|
var (
|
||||||
username = "name"
|
username = `` // The SSH user's name
|
||||||
password = "pwd"
|
password = `` // The user's password
|
||||||
serverAddrString = "server:22"
|
serverAddrString = `` // The SSH server address
|
||||||
localAddrString = "localhost:53001"
|
localAddrString = `` // The local end-point
|
||||||
remoteAddrString = "localhost:27017"
|
remoteAddrString = `` // The remote end-point (on the SSH server's side)
|
||||||
|
currentRetriesLocal = 0 // Check how many retries are occur for creating the local end-point
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user