Compare commits
7 Commits
v1.0.0-sta
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
56f0a8dd64 | ||
|
c85e21231b | ||
|
6146f74c72 | ||
|
c2a03bfdcf | ||
|
a16b99af6e | ||
|
aab3524082 | ||
|
15803714a8 |
88
Main.go
88
Main.go
@ -1,31 +1,57 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/crypto/ssh"
|
"fmt"
|
||||||
"runtime"
|
"github.com/SommerEngineering/SSHTunnel/Tunnel"
|
||||||
)
|
"github.com/howeyc/gopass"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
func main() {
|
"log"
|
||||||
|
"os"
|
||||||
// Allow Go to use all CPUs:
|
"runtime"
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
)
|
||||||
|
|
||||||
// Read the configuration from the command-line args:
|
func main() {
|
||||||
readFlags()
|
|
||||||
|
// Show the current version:
|
||||||
// Create the SSH configuration:
|
log.Println(`SSHTunnel v1.3.0`)
|
||||||
config := &ssh.ClientConfig{
|
|
||||||
User: username,
|
// Allow Go to use all CPUs:
|
||||||
Auth: []ssh.AuthMethod{
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
ssh.Password(password),
|
|
||||||
ssh.PasswordCallback(passwordCallback),
|
// Read the configuration from the command-line args:
|
||||||
ssh.KeyboardInteractive(keyboardInteractiveChallenge),
|
readFlags()
|
||||||
},
|
|
||||||
}
|
// Check if the password was provided:
|
||||||
|
for true {
|
||||||
// Create the local end-point:
|
if password == `` {
|
||||||
localListener := createLocalEndPoint()
|
// Promt for the password:
|
||||||
|
fmt.Println(`Please provide the password for the connection:`)
|
||||||
// Accept client connections (will block forever):
|
if pass, errPass := gopass.GetPasswd(); errPass != nil {
|
||||||
acceptClients(localListener, config)
|
log.Println(`There was an error reading the password securely: ` + errPass.Error())
|
||||||
}
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
password = string(pass)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the SSH configuration:
|
||||||
|
Tunnel.SetPassword4Callback(password)
|
||||||
|
config := &ssh.ClientConfig{
|
||||||
|
User: username,
|
||||||
|
Auth: []ssh.AuthMethod{
|
||||||
|
ssh.Password(password),
|
||||||
|
ssh.PasswordCallback(Tunnel.PasswordCallback),
|
||||||
|
ssh.KeyboardInteractive(Tunnel.KeyboardInteractiveChallenge),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the local end-point:
|
||||||
|
localListener := Tunnel.CreateLocalEndPoint(localAddrString)
|
||||||
|
|
||||||
|
// Accept client connections (will block forever):
|
||||||
|
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
|
|
||||||
}
|
|
10
README.md
10
README.md
@ -1,6 +1,6 @@
|
|||||||
SSHTunnel
|
SSHTunnel
|
||||||
=========
|
=========
|
||||||
SSHTunnel is a tiny small program to tunnel something through a SSH without any external dependencies. Yes, you do not need any PuTTY installtion for Microsoft Windows. Just download the executable which matches your OS and architecture (32 vs. 64 bits) and run it.
|
SSHTunnel is a tiny small program to tunnel something through a SSH without any external dependencies. Just download the executable which matches your OS and architecture (32 vs. 64 bits) and run it.
|
||||||
|
|
||||||
### Syntax
|
### Syntax
|
||||||
*This example uses the Microsoft Windows executable, but the syntax is the same for e.g. Linux, Unix, Mac, etc.*
|
*This example uses the Microsoft Windows executable, but the syntax is the same for e.g. Linux, Unix, Mac, etc.*
|
||||||
@ -10,19 +10,21 @@ SSHTunnel is a tiny small program to tunnel something through a SSH without any
|
|||||||
- At the SSH server's side, connects to `127.0.0.1` to port `27017` (a MongoDB database)
|
- At the SSH server's side, connects to `127.0.0.1` to port `27017` (a MongoDB database)
|
||||||
- At your local side, provides a listener at `127.0.0.1` at the port `53001`
|
- At your local side, provides a listener at `127.0.0.1` at the port `53001`
|
||||||
- The username for the SSH service is `john`
|
- The username for the SSH service is `john`
|
||||||
- The user's password would be `johndow` ;-)
|
- The user's password would be `johndow` ;-) You can avoid the `-pwd` argument. Thus, the SSHTunnel will ask for the password.
|
||||||
- Now, you are able to use your local MongoDB software and can connect to port `53001` at `localhost`.
|
- Now, you are able to use your local MongoDB software and can connect to port `53001` at `localhost`.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
- The whole code is open source and can be used for any purpose (also commercial)
|
- The whole code is open source and can be used for any purpose (also commercial)
|
||||||
- If you want, you can compile the code by your own by using the Go compiler (http://www.golang.org)
|
- If you want, you can compile the code by your own by using the [Go](http://www.golang.org)
|
||||||
- The program just needs very low resources e.g. around 1.3 MB memory for Microsoft Windows 8.1
|
- The program just needs very low resources e.g. around 1.3 MB memory for Microsoft Windows 8.1
|
||||||
- SSHTunnel is scalable and, if necessary, can utilise all your CPUs
|
- SSHTunnel is scalable and, if necessary, can utilise all your CPUs
|
||||||
- If a connection cannot setup, the program re-tries it
|
- If a connection cannot setup, the program re-tries it
|
||||||
- At the moment, SSHTunnel uses only the password authentication methods. Therefore, it is currently not possible to use e.g. a certificate, etc. Nevertheless, the implementation of this feature is possible.
|
- At the moment, SSHTunnel uses only the password authentication methods. Therefore, it is currently not possible to use e.g. a certificate, etc. Nevertheless, the implementation of this feature is possible.
|
||||||
- The configuration must be provided by using the command-line arguments. It is currently not possible to use e.g. a configuration file.
|
- The configuration must be provided by using the command-line arguments. It is currently not possible to use e.g. a configuration file.
|
||||||
|
- You can avoid the password argument if you prefer to provide the password on demand.
|
||||||
|
- [Ocean Remote Connections](https://github.com/SommerEngineering/OceanRemoteConnections) is a simple GUI for SSH Tunnel, PuTTY, RDP and WinSCP.
|
||||||
|
|
||||||
### Download
|
### Download
|
||||||
Go and get the latest release from GitHub's release page: https://github.com/SommerEngineering/SSHTunnel/releases
|
Go and get the latest release from the [release page](https://github.com/SommerEngineering/SSHTunnel/releases).
|
||||||
|
|
||||||
*Based on damick's example code from http://stackoverflow.com/questions/21417223/simple-ssh-port-forward-in-golang*
|
*Based on damick's example code from http://stackoverflow.com/questions/21417223/simple-ssh-port-forward-in-golang*
|
||||||
|
@ -8,7 +8,7 @@ func readFlags() {
|
|||||||
flag.StringVar(&serverAddrString, `server`, `127.0.0.1:22`, `The (remote) SSH server, e.g. 'my.host.com', 'my.host.com:22', '127.0.0.1:22', 'localhost:22'.`)
|
flag.StringVar(&serverAddrString, `server`, `127.0.0.1:22`, `The (remote) SSH server, e.g. 'my.host.com', 'my.host.com:22', '127.0.0.1:22', 'localhost:22'.`)
|
||||||
flag.StringVar(&localAddrString, `local`, `127.0.0.1:50000`, `The local end-point of the tunnel, e.g. '127.0.0.1:50000', 'localhost:50000'.`)
|
flag.StringVar(&localAddrString, `local`, `127.0.0.1:50000`, `The local end-point of the tunnel, e.g. '127.0.0.1:50000', 'localhost:50000'.`)
|
||||||
flag.StringVar(&remoteAddrString, `remote`, `127.0.0.1:27017`, `The remote side end-point (e.g. on the machine with the SSH server), e.g. a MongoDB (port 27017) '127.0.0.1:27017', a web server '127.0.0.1:80'`)
|
flag.StringVar(&remoteAddrString, `remote`, `127.0.0.1:27017`, `The remote side end-point (e.g. on the machine with the SSH server), e.g. a MongoDB (port 27017) '127.0.0.1:27017', a web server '127.0.0.1:80'`)
|
||||||
flag.StringVar(&username, `user`, `username`, `The user's name for the SSD server.`)
|
flag.StringVar(&username, `user`, `username`, `The user's name for the SSH server.`)
|
||||||
flag.StringVar(&password, `pwd`, `password`, `The user's password for the SSD server.`)
|
flag.StringVar(&password, `pwd`, ``, `The user's password for the SSH server.`)
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
package main
|
package Tunnel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
func acceptClients(connection net.Listener, config *ssh.ClientConfig) {
|
func AcceptClients(connection net.Listener, config *ssh.ClientConfig, serverAddrString, remoteAddrString string) {
|
||||||
|
|
||||||
// Endless loop
|
// Endless loop
|
||||||
for {
|
for {
|
||||||
|
|
||||||
// Accept (another) client connection:
|
// Accept (another) client connection:
|
||||||
if localConn, err := connection.Accept(); err != nil {
|
if localConn, err := connection.Accept(); err != nil {
|
||||||
|
|
||||||
// Fail
|
// Fail
|
||||||
log.Printf("Accepting a client failed: %s\n", err.Error())
|
log.Printf("Accepting a client failed: %s\n", err.Error())
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
log.Println(`Client accepted.`)
|
log.Println(`Client accepted.`)
|
||||||
|
|
||||||
// Start the forwarding:
|
// Start the forwarding:
|
||||||
go forward(localConn, config)
|
go forward(localConn, config, serverAddrString, remoteAddrString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package main
|
package Tunnel
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxRetriesLocal = 16 // How many retries are allowed to create the local end-point?
|
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?
|
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?
|
maxRetriesServer = 16 // How many retries are allowed to create the SSH server's connection?
|
||||||
)
|
)
|
@ -1,35 +1,35 @@
|
|||||||
package main
|
package Tunnel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createLocalEndPoint() (localListener net.Listener) {
|
func CreateLocalEndPoint(localAddrString string) (localListener net.Listener) {
|
||||||
|
|
||||||
// Loop for the necessary retries
|
// Loop for the necessary retries
|
||||||
for {
|
for {
|
||||||
|
|
||||||
// Try to create the local end-point
|
// Try to create the local end-point
|
||||||
if localListenerObj, err := net.Listen(`tcp`, localAddrString); err != nil {
|
if localListenerObj, err := net.Listen(`tcp`, localAddrString); err != nil {
|
||||||
|
|
||||||
// It was not able to create the end-point:
|
// It was not able to create the end-point:
|
||||||
currentRetriesLocal++
|
currentRetriesLocal++
|
||||||
log.Printf("Was not able to create the local end-point %s: %s\n", localAddrString, err.Error())
|
log.Printf("Was not able to create the local end-point %s: %s\n", localAddrString, err.Error())
|
||||||
|
|
||||||
// Is another retry possible?
|
// Is another retry possible?
|
||||||
if currentRetriesLocal < maxRetriesLocal {
|
if currentRetriesLocal < maxRetriesLocal {
|
||||||
log.Println(`Retry...`)
|
log.Println(`Retry...`)
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
} else {
|
} else {
|
||||||
log.Fatalln(`No more retries for the local end-point: ` + localAddrString) // => Exit
|
log.Fatalln(`No more retries for the local end-point: ` + localAddrString) // => Exit
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Success!
|
// Success!
|
||||||
log.Println(`Listen to local address ` + localAddrString)
|
log.Println(`Listen to local address ` + localAddrString)
|
||||||
localListener = localListenerObj
|
localListener = localListenerObj
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,97 +1,97 @@
|
|||||||
package main
|
package Tunnel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func forward(localConn net.Conn, config *ssh.ClientConfig) {
|
func forward(localConn net.Conn, config *ssh.ClientConfig, serverAddrString, remoteAddrString string) {
|
||||||
|
|
||||||
defer localConn.Close()
|
defer localConn.Close()
|
||||||
currentRetriesServer := 0
|
currentRetriesServer := 0
|
||||||
currentRetriesRemote := 0
|
currentRetriesRemote := 0
|
||||||
var sshClientConnection *ssh.Client = nil
|
var sshClientConnection *ssh.Client = nil
|
||||||
|
|
||||||
// Loop for retries:
|
// Loop for retries:
|
||||||
for {
|
for {
|
||||||
|
|
||||||
// Try to connect to the SSD server:
|
// Try to connect to the SSH server:
|
||||||
if sshClientConn, err := ssh.Dial(`tcp`, serverAddrString, config); err != nil {
|
if sshClientConn, err := ssh.Dial(`tcp`, serverAddrString, config); err != nil {
|
||||||
|
|
||||||
// Failed:
|
// Failed:
|
||||||
currentRetriesServer++
|
currentRetriesServer++
|
||||||
log.Printf("Was not able to connect with the SSH server %s: %s\n", serverAddrString, err.Error())
|
log.Printf("Was not able to connect with the SSH server %s: %s\n", serverAddrString, err.Error())
|
||||||
|
|
||||||
// Is a retry alowed?
|
// Is a retry alowed?
|
||||||
if currentRetriesServer < maxRetriesServer {
|
if currentRetriesServer < maxRetriesServer {
|
||||||
log.Println(`Retry...`)
|
log.Println(`Retry...`)
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// After the return, this thread is closed down. The client can try it again...
|
// After the return, this thread is closed down. The client can try it again...
|
||||||
log.Println(`No more retries for connecting the SSH server.`)
|
log.Println(`No more retries for connecting the SSH server.`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Success:
|
// Success:
|
||||||
log.Println(`Connected to the SSH server ` + serverAddrString)
|
log.Println(`Connected to the SSH server ` + serverAddrString)
|
||||||
sshClientConnection = sshClientConn
|
sshClientConnection = sshClientConn
|
||||||
defer sshClientConnection.Close()
|
defer sshClientConnection.Close()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop for retries:
|
// Loop for retries:
|
||||||
for {
|
for {
|
||||||
|
|
||||||
// Try to create the remote end-point:
|
// Try to create the remote end-point:
|
||||||
if sshConn, err := sshClientConnection.Dial(`tcp`, remoteAddrString); err != nil {
|
if sshConn, err := sshClientConnection.Dial(`tcp`, remoteAddrString); err != nil {
|
||||||
|
|
||||||
// Failed:
|
// Failed:
|
||||||
currentRetriesRemote++
|
currentRetriesRemote++
|
||||||
log.Printf("Was not able to create the remote end-point %s: %s\n", remoteAddrString, err.Error())
|
log.Printf("Was not able to create the remote end-point %s: %s\n", remoteAddrString, err.Error())
|
||||||
|
|
||||||
// Is another retry allowed?
|
// Is another retry allowed?
|
||||||
if currentRetriesRemote < maxRetriesRemote {
|
if currentRetriesRemote < maxRetriesRemote {
|
||||||
log.Println(`Retry...`)
|
log.Println(`Retry...`)
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// After the return, this thread is closed down. The client can try it again...
|
// 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.`)
|
log.Println(`No more retries for connecting the remote end-point.`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Fine, the connections are up and ready :-)
|
// Fine, the connections are up and ready :-)
|
||||||
log.Printf("The remote end-point %s is connected.\n", remoteAddrString)
|
log.Printf("The remote end-point %s is connected.\n", remoteAddrString)
|
||||||
defer sshConn.Close()
|
defer sshConn.Close()
|
||||||
|
|
||||||
// To be able to close down both transfer threads, we create a channel:
|
// To be able to close down both transfer threads, we create a channel:
|
||||||
quit := make(chan bool)
|
quit := make(chan bool)
|
||||||
|
|
||||||
// Create the transfers to/from both sides (two new threads are created for this):
|
// Create the transfers to/from both sides (two new threads are created for this):
|
||||||
go transfer(localConn, sshConn, `Local => Remote`, quit)
|
go transfer(localConn, sshConn, `Local => Remote`, quit)
|
||||||
go transfer(sshConn, localConn, `Remote => Local`, quit)
|
go transfer(sshConn, localConn, `Remote => Local`, quit)
|
||||||
|
|
||||||
// Wait and look if any of the two transfer theads are down:
|
// Wait and look if any of the two transfer theads are down:
|
||||||
isRunning := true
|
isRunning := true
|
||||||
for isRunning {
|
for isRunning {
|
||||||
select {
|
select {
|
||||||
case <-quit:
|
case <-quit:
|
||||||
log.Println(`At least one transfer was stopped.`)
|
log.Println(`At least one transfer was stopped.`)
|
||||||
isRunning = false
|
isRunning = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, close all the channels and therefore, force the other / second thread to go down:
|
// Now, close all the channels and therefore, force the other / second thread to go down:
|
||||||
log.Println(`Close now all connections.`)
|
log.Println(`Close now all connections.`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,35 +1,35 @@
|
|||||||
package main
|
package Tunnel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Another auth. method.
|
// 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 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:`)
|
||||||
for q := range questions {
|
for q := range questions {
|
||||||
log.Println(q)
|
log.Println(q)
|
||||||
}
|
}
|
||||||
|
|
||||||
// How many questions are asked?
|
// How many questions are asked?
|
||||||
countQuestions := len(questions)
|
countQuestions := len(questions)
|
||||||
|
|
||||||
if countQuestions == 1 {
|
if countQuestions == 1 {
|
||||||
|
|
||||||
// We expect that in this case (only one question is asked), that the server want to know the password ;-)
|
// 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 = make([]string, countQuestions, countQuestions)
|
||||||
answers[0] = password
|
answers[0] = callbackPassword
|
||||||
|
|
||||||
} else if countQuestions > 1 {
|
} else if countQuestions > 1 {
|
||||||
|
|
||||||
// After logging, this call will exit the whole program:
|
// After logging, this call will exit the whole program:
|
||||||
log.Fatalln(`The SSH server is asking multiple questions! This program cannot handle this case.`)
|
log.Fatalln(`The SSH server is asking multiple questions! This program cannot handle this case.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = nil
|
err = nil
|
||||||
return
|
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
|
package Tunnel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The transfer function.
|
// The transfer function.
|
||||||
func transfer(fromReader io.Reader, toWriter io.Writer, name string, quit chan bool) {
|
func transfer(fromReader io.Reader, toWriter io.Writer, name string, quit chan bool) {
|
||||||
|
|
||||||
log.Printf("%s transfer started.", name)
|
log.Printf("%s transfer started.", name)
|
||||||
|
|
||||||
// This call blocks until the client or service will close the connection.
|
// This call blocks until the client or service will close the connection.
|
||||||
// Therefore, this call maybe takes hours or even longer. Concern, may this
|
// 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
|
// program will be used to connect multiple servers to make e.g. a database
|
||||||
// available...
|
// available...
|
||||||
if _, err := io.Copy(toWriter, fromReader); err != nil {
|
if _, err := io.Copy(toWriter, fromReader); err != nil {
|
||||||
|
|
||||||
// In this case, we do not fail the whole program: Regarding how the client
|
// 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'.
|
// 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())
|
log.Printf("%s transfer failed: %s\n", name, err.Error())
|
||||||
} else {
|
} else {
|
||||||
log.Printf("%s transfer closed.\n", name)
|
log.Printf("%s transfer closed.\n", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
quit <- true
|
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
|
package main
|
||||||
|
|
||||||
var (
|
var (
|
||||||
username = `` // The SSH user's name
|
username = `` // The SSH user's name
|
||||||
password = `` // The user's password
|
password = `` // The user's password
|
||||||
serverAddrString = `` // The SSH server address
|
serverAddrString = `` // The SSH server address
|
||||||
localAddrString = `` // The local end-point
|
localAddrString = `` // The local end-point
|
||||||
remoteAddrString = `` // The remote end-point (on the SSH server's side)
|
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