Version 1.2.0
This commit is contained in:
parent
aab3524082
commit
a16b99af6e
12
Main.go
12
Main.go
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/SommerEngineering/SSHTunnel/Tunnel"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"runtime"
|
||||
)
|
||||
@ -9,7 +10,7 @@ import (
|
||||
func main() {
|
||||
|
||||
// Show the current version:
|
||||
fmt.Println(`SSHTunnel v1.1.0`)
|
||||
fmt.Println(`SSHTunnel v1.2.0`)
|
||||
|
||||
// Allow Go to use all CPUs:
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
@ -29,18 +30,19 @@ func main() {
|
||||
}
|
||||
|
||||
// Create the SSH configuration:
|
||||
Tunnel.SetPassword4Callback(password)
|
||||
config := &ssh.ClientConfig{
|
||||
User: username,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.Password(password),
|
||||
ssh.PasswordCallback(passwordCallback),
|
||||
ssh.KeyboardInteractive(keyboardInteractiveChallenge),
|
||||
ssh.PasswordCallback(Tunnel.PasswordCallback),
|
||||
ssh.KeyboardInteractive(Tunnel.KeyboardInteractiveChallenge),
|
||||
},
|
||||
}
|
||||
|
||||
// Create the local end-point:
|
||||
localListener := createLocalEndPoint()
|
||||
localListener := Tunnel.CreateLocalEndPoint(localAddrString)
|
||||
|
||||
// Accept client connections (will block forever):
|
||||
acceptClients(localListener, config)
|
||||
Tunnel.AcceptClients(localListener, config, serverAddrString, remoteAddrString)
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
package main
|
||||
|
||||
// Just a callback function for the password request.
|
||||
func passwordCallback() (string, error) {
|
||||
return password, nil
|
||||
}
|
@ -1,28 +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)
|
||||
}
|
||||
}
|
||||
}
|
||||
package Tunnel
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
func AcceptClients(connection net.Listener, config *ssh.ClientConfig, serverAddrString, remoteAddrString string) {
|
||||
|
||||
// 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, serverAddrString, remoteAddrString)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +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?
|
||||
)
|
||||
package Tunnel
|
||||
|
||||
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?
|
||||
)
|
@ -1,35 +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
|
||||
}
|
||||
}
|
||||
}
|
||||
package Tunnel
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CreateLocalEndPoint(localAddrString string) (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
|
||||
}
|
||||
}
|
||||
}
|
@ -1,97 +1,97 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func forward(localConn net.Conn, config *ssh.ClientConfig) {
|
||||
|
||||
defer localConn.Close()
|
||||
currentRetriesServer := 0
|
||||
currentRetriesRemote := 0
|
||||
var sshClientConnection *ssh.Client = nil
|
||||
|
||||
// Loop for retries:
|
||||
for {
|
||||
|
||||
// Try to connect to the SSH 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
|
||||
defer sshClientConnection.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Loop for retries:
|
||||
for {
|
||||
|
||||
// Try to create the remote end-point:
|
||||
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)
|
||||
defer sshConn.Close()
|
||||
|
||||
// To be able to close down both transfer threads, we create a channel:
|
||||
quit := make(chan bool)
|
||||
|
||||
// Create the transfers to/from both sides (two new threads are created for this):
|
||||
go transfer(localConn, sshConn, `Local => Remote`, quit)
|
||||
go transfer(sshConn, localConn, `Remote => Local`, quit)
|
||||
|
||||
// Wait and look if any of the two transfer theads are down:
|
||||
isRunning := true
|
||||
for isRunning {
|
||||
select {
|
||||
case <-quit:
|
||||
log.Println(`At least one transfer was stopped.`)
|
||||
isRunning = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Now, close all the channels and therefore, force the other / second thread to go down:
|
||||
log.Println(`Close now all connections.`)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
package Tunnel
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func forward(localConn net.Conn, config *ssh.ClientConfig, serverAddrString, remoteAddrString string) {
|
||||
|
||||
defer localConn.Close()
|
||||
currentRetriesServer := 0
|
||||
currentRetriesRemote := 0
|
||||
var sshClientConnection *ssh.Client = nil
|
||||
|
||||
// Loop for retries:
|
||||
for {
|
||||
|
||||
// Try to connect to the SSH 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
|
||||
defer sshClientConnection.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Loop for retries:
|
||||
for {
|
||||
|
||||
// Try to create the remote end-point:
|
||||
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)
|
||||
defer sshConn.Close()
|
||||
|
||||
// To be able to close down both transfer threads, we create a channel:
|
||||
quit := make(chan bool)
|
||||
|
||||
// Create the transfers to/from both sides (two new threads are created for this):
|
||||
go transfer(localConn, sshConn, `Local => Remote`, quit)
|
||||
go transfer(sshConn, localConn, `Remote => Local`, quit)
|
||||
|
||||
// Wait and look if any of the two transfer theads are down:
|
||||
isRunning := true
|
||||
for isRunning {
|
||||
select {
|
||||
case <-quit:
|
||||
log.Println(`At least one transfer was stopped.`)
|
||||
isRunning = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Now, close all the channels and therefore, force the other / second thread to go down:
|
||||
log.Println(`Close now all connections.`)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
// Another auth. method.
|
||||
func keyboardInteractiveChallenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
||||
|
||||
// Log all the provided data:
|
||||
log.Println(`User: ` + user)
|
||||
log.Println(`Instruction: ` + instruction)
|
||||
log.Println(`Questions:`)
|
||||
for q := range questions {
|
||||
log.Println(q)
|
||||
}
|
||||
|
||||
// How many questions are asked?
|
||||
countQuestions := len(questions)
|
||||
|
||||
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
|
||||
|
||||
} 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
|
||||
return
|
||||
}
|
||||
package Tunnel
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
// Another auth. method.
|
||||
func KeyboardInteractiveChallenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
||||
|
||||
// Log all the provided data:
|
||||
log.Println(`User: ` + user)
|
||||
log.Println(`Instruction: ` + instruction)
|
||||
log.Println(`Questions:`)
|
||||
for q := range questions {
|
||||
log.Println(q)
|
||||
}
|
||||
|
||||
// How many questions are asked?
|
||||
countQuestions := len(questions)
|
||||
|
||||
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] = callbackPassword
|
||||
|
||||
} 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
|
||||
return
|
||||
}
|
10
Tunnel/PasswordCallback.go
Normal file
10
Tunnel/PasswordCallback.go
Normal file
@ -0,0 +1,10 @@
|
||||
package Tunnel
|
||||
|
||||
// Just a callback function for the password request.
|
||||
func PasswordCallback() (string, error) {
|
||||
return callbackPassword, nil
|
||||
}
|
||||
|
||||
func SetPassword4Callback(password string) {
|
||||
callbackPassword = password
|
||||
}
|
@ -1,27 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
// The transfer function.
|
||||
func transfer(fromReader io.Reader, toWriter io.Writer, name string, quit chan bool) {
|
||||
|
||||
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 {
|
||||
|
||||
// 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 {
|
||||
log.Printf("%s transfer closed.\n", name)
|
||||
}
|
||||
|
||||
quit <- true
|
||||
}
|
||||
package Tunnel
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
// The transfer function.
|
||||
func transfer(fromReader io.Reader, toWriter io.Writer, name string, quit chan bool) {
|
||||
|
||||
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 {
|
||||
|
||||
// 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 {
|
||||
log.Printf("%s transfer closed.\n", name)
|
||||
}
|
||||
|
||||
quit <- true
|
||||
}
|
6
Tunnel/Variables.go
Normal file
6
Tunnel/Variables.go
Normal file
@ -0,0 +1,6 @@
|
||||
package Tunnel
|
||||
|
||||
var (
|
||||
currentRetriesLocal = 0 // Check how many retries are occur for creating the local end-point
|
||||
callbackPassword = ``
|
||||
)
|
11
Variables.go
11
Variables.go
@ -1,10 +1,9 @@
|
||||
package main
|
||||
|
||||
var (
|
||||
username = `` // The SSH user's name
|
||||
password = `` // The user's password
|
||||
serverAddrString = `` // The SSH server address
|
||||
localAddrString = `` // The local end-point
|
||||
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
|
||||
username = `` // The SSH user's name
|
||||
password = `` // The user's password
|
||||
serverAddrString = `` // The SSH server address
|
||||
localAddrString = `` // The local end-point
|
||||
remoteAddrString = `` // The remote end-point (on the SSH server's side)
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user