How can you ping an IP address from a golang application? The ultimate goal is to check if a server is online.
Does go have a way in the standard library to implement a network ping?
As #desaipath mentions, there is no way to do this in the standard library. However, you do not need to write the code for yourself - it has already been done:
https://github.com/tatsushid/go-fastping
Note, sending ICMP packets requires root privileges
I needed the same thing as you and I've made a workaround (with exec.Command) for my Raspberry Pi to check if servers are online. Here is the experimental code
out, _ := exec.Command("ping", "192.168.0.111", "-c 5", "-i 3", "-w 10").Output()
if strings.Contains(string(out), "Destination Host Unreachable") {
fmt.Println("TANGO DOWN")
} else {
fmt.Println("IT'S ALIVEEE")
}
#jpillora's answer suggests using go-fastping, but that library hasn't been updated since Jan 8, 2016. It may not be an issue as the logic of pinging is quite simple, but if you want a more recent package then there's go-ping.
Although not a real ICMP ping, this is what use to probe my servers using the TCP protocol:
host := "example.com"
port := "80"
timeout := time.Duration(1 * time.Second)
_, err := net.DialTimeout("tcp", host+":"+port, timeout)
if err != nil {
fmt.Printf("%s %s %s\n", host, "not responding", err.Error())
} else {
fmt.Printf("%s %s %s\n", host, "responding on port:", port)
}
No.
Go does not have any built-in way to ping a server in standard library.
You need to write code by yourself.
For that, you can look into icmp section of golang library. And use this list of control messages, to construct icmp message properly.
But, keep in mind that some server administrator shuts down ping service on their server, for security reason. So, If your goal is to ultimately check if server is online or not, this is not 100% reliable method.
package main
import (
"fmt"
"os/exec"
)
func main() {
Command := fmt.Sprintf("ping -c 1 10.2.201.174 > /dev/null && echo true || echo false")
output, err := exec.Command("/bin/sh", "-c", Command).Output()
fmt.Print(string(output))
fmt.Print(err)
}
Related
I am not able to manage the wireguard interface using wgctl on Windows (10). I have used it on linux and all is fine. I'm using Windows 10 latest updates, wireguard.exe latest, go 1.17.3 latest.
I am using a tunnel created with wireguard.exe /installtunnelservice /path/wg0.conf. If I manage the tunnel with the wireguard GUI, it all works fine. But I need to do it programatically.
C:\>wg
interface: wg0
public key: K0BZ3Bk...5tCWo=
private key: (hidden)
listening port: 57538
peer: 7W6tOXI...F7zAo=
endpoint: 159....105:51820
allowed ips: 100.127.128.0/18
latest handshake: 43 seconds ago
transfer: 31.61 KiB received, 115.69 KiB sent
persistent keepalive: every 25 seconds
...
The following code exits with "file does not exist". Having stepped the code into the library, I think that wireguard.exe is using NT Kernel mode and the library does not support it? Can someone please confirm? What is the best way around this?
package main
import (
"log"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
func main() {
wgc, err := wgctrl.New()
if err != nil {
log.Printf("wgctrl.New: %s", err)
}
defer wgc.Close()
cfg := wgtypes.Config{}
port := 51822
cfg.ListenPort = &port
err = wgc.ConfigureDevice("wg0", cfg)
if err != nil {
log.Printf("wgc.ConfigureDevice: %s", err)
}
}
After escalating the issue on GitHub, it turned out to be a bug in the library which was promptly fixed not long after I posted this.
single IP can only support 65535 port to single destination.
I hope the client can reuse the old tcp_session immediately during the performance test, even if session is still in time_wait status.
On my Linux machine, I had opened these switch
sysctl -w net.ipv4.tcp_timestamps=1
sysctl -w net.ipv4.tcp_tw_recycle=1
sysctl -w net.ipv4.tcp_tw_reuse=1
Then I write the following code to verify the socket_reuse option with golang.
In the code, I bind the local port 12345.
after run first
$go run 1.go
$netstat -nat | grep 12345
tcp 0 0 192.168.1.11:12345 111.161.3.173:80 TIME_WAIT
after run secondary
$go run 1.go
Client Connect() called error: cannot assign requested address
It seems that the SO_REUSEADDR can not work.
Can Anyone help to resolve this ?
package main
import (
"fmt"
. "syscall"
)
func main() {
var (
clientsock int
serveraddr SockaddrInet4
err error
)
if clientsock, err = Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); err != nil {
fmt.Println("Client Socket() called error:", err.Error())
return
}
SetsockoptInt(clientsock, SOL_SOCKET, SO_REUSEADDR, 1)
defer Shutdown(clientsock, SHUT_RDWR)
serveraddr.Addr = [4]byte{111, 161, 3, 173}
serveraddr.Port = 80
err = Bind(clientsock, &SockaddrInet4{
Port: 12345,
})
if err = Connect(clientsock, &serveraddr); err != nil {
fmt.Println("Client Connect() called error:", err.Error())
return
}
}
You ought to add two changes to your code:
1) Set socket option unix.SO_REUSEPORT.
if errReusePort := SetsockoptInt(clientsock, SOL_SOCKET, unix.SO_REUSEPORT, 1); errReusePort != nil {
fmt.Printf("reuse port error: %v\n", errReusePort)
return
}
2) Make your code to connect to distinct remote TCP endpoints. Otherwise, due to single source addr/port, TCP wouldn't be able to distinguish between two identical connections (protocol, src-addr, src-port, dst-addr, dst-port). The example below specifies two remote server addresses in the command-line.
$ go run main.go 127.0.0.1
connected
$ go run main.go 127.0.0.2
connected
Find full working code on playground: https://play.golang.org/p/HYLkWlVH6T4
I am building a tcp socket server in golang. It works well with less than ~140 client connection. But if i try to set max connection number to 500, after 140th clients do not connect to the server.
I increased file descriptor count to 1048576 but it does not still work.
$ulimit -n
1048576
I think the problem comes from operation system.(server and clients work on same machine) So OS information:
Mac OS 10.12 Sierra 64 bit.
Does anyone have any idea why i can't increase tcp connection number?
github
I am on the same operating system as you, but I could not reproduce your issue. I am using Go version 1.7.4. I have not tested on Go 1.8, but it was released earlier today.
I created two files, server.go and client.go (reproduced below). When I run them, I get way more than 140 connections. Before running, I switched to root and setup the ulimit like so:
$ sudo -s
$ ulimit -n 10000
The client outputs:
Established 1 connections
...
Established 2971 connections
panic: dial tcp 127.0.0.1:1337: socket: too many open files in system
The server outputs something very similar.
Here is client.go:
package main
import (
"fmt"
"net"
)
func main() {
var numConns int
for {
_, err := net.Dial("tcp", "127.0.0.1:1337")
if err != nil {
panic(err)
}
numConns++
fmt.Println("Established", numConns, "connections")
}
}
And server.go:
package main
import (
"fmt"
"net"
)
func main() {
listener, err := net.Listen("tcp", ":1337")
if err != nil {
panic(err)
}
var numConns int
for {
_, err := listener.Accept()
if err != nil {
panic(err)
}
numConns++
fmt.Println("Got", numConns, "connections")
}
}
This is not a problem with the operating system, but the hardware. It is likely a problem with your router. Commercial routers do tend to fail when you get into that range. My guess is that Alex is testing at a large company or university where they have commercial grade routers, but you are testing at home.
I'm trying to create (and later close) a simple TCP port forward over SSH with Go. I'm new to Golang and statically typed languages. (Coming from Ruby.)
In a terminal I would simply run ssh -L 9000:localhost:9999 user#server.com and this accomplishes what I need. I want to do the same, programmatically with Go.
I have tried using this example as a starting point and this recent test to try to understand what to do, but now I have a pile of confusing jumbled code when it seems like this is actually a very simple thing to do.
Any help would be very much appreciated! :-)
I finally figured out how to do this, I got hints from schmichael in an IRC channel. Thanks to all!
EDIT: A little explanation:
A big part of the problem I was having was that I did not realize a local net.Listener (not just a local net.Conn) needed setup to receive a local request and create the net.Conn before forwarding the bytes.
Also, there exist both port forwards and reverse port forwards and I hadn't previously thought in detail about the fact that a regular port forward also sends bytes back, so copying the remote reader to local writer was not something I had implemented, yet it's very much needed.
Here is an attempt to relate the essence of what this code does:
Listen on local port 9000.
Upon attempted read from local port 9000: (listener.Accept()),
Accept connection and return a local io.Reader and io.Writer and,
Connect to remote server and,
Connect to remote port 9999 returning a io.Reader and io.Writer.
Continually copy local io.Reader bytes to remote io.Writer,
Continually copy remote io.Reader bytes to local io.Writer.
Here is the code:
package main
// Forward from local port 9000 to remote port 9999
import (
"io"
"log"
"net"
"golang.org/x/crypto/ssh"
)
var (
username = "root"
password = "password"
serverAddrString = "192.168.1.100:22"
localAddrString = "localhost:9000"
remoteAddrString = "localhost:9999"
)
func forward(localConn net.Conn, config *ssh.ClientConfig) {
// Setup sshClientConn (type *ssh.ClientConn)
sshClientConn, err := ssh.Dial("tcp", serverAddrString, config)
if err != nil {
log.Fatalf("ssh.Dial failed: %s", err)
}
// Setup sshConn (type net.Conn)
sshConn, err := sshClientConn.Dial("tcp", remoteAddrString)
// Copy localConn.Reader to sshConn.Writer
go func() {
_, err = io.Copy(sshConn, localConn)
if err != nil {
log.Fatalf("io.Copy failed: %v", err)
}
}()
// Copy sshConn.Reader to localConn.Writer
go func() {
_, err = io.Copy(localConn, sshConn)
if err != nil {
log.Fatalf("io.Copy failed: %v", err)
}
}()
}
func main() {
// Setup SSH config (type *ssh.ClientConfig)
config := &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.Password(password),
},
}
// Setup localListener (type net.Listener)
localListener, err := net.Listen("tcp", localAddrString)
if err != nil {
log.Fatalf("net.Listen failed: %v", err)
}
for {
// Setup localConn (type net.Conn)
localConn, err := localListener.Accept()
if err != nil {
log.Fatalf("listen.Accept failed: %v", err)
}
go forward(localConn, config)
}
}
I have used your (damick) example code to build a tiny open source tool: SSHTunnel
https://github.com/SommerEngineering/SSHTunnel
Therefore, the code is freely available at GitHub for anyone: Please feel free to use it for learning purposes or for anything else :) I have mentioned your nickname and also linked to this question.
Best regards,
Thorsten.
I'v finished a simple SSH port forward tool called mallory.
It provides HTTP proxy instead of SOCKS proxy, which is really similar to ssh -D.
The core code is similar to damick's answer.
Create ClientConfig
ssh.Dial to remote SSH server with the config and return Client
Now you can use Client.Dial to forward anything you like.
Dial initiates a connection to the addr from the remote host. The resulting connection has a zero LocalAddr() and RemoteAddr().
If you want to serve a SOCKS proxy server, use Client.Dial to connect to the remote server.
I Wrote a tool,Called gosshtool,with this tool,you can easy to create a simple TCP port forward over SSH with Go.https://github.com/scottkiss/gosshtool
I use this tool implemented a port forward server example project:
https://github.com/scottkiss/gooverssh
package main
import (
"fmt"
"net"
)
func main() {
var localaddr net.TCPAddr
var remoteaddr net.TCPAddr
localaddr.IP = net.ParseIP("192.168.1.104")
localaddr.Port = 6000
remoteaddr.IP = net.ParseIP("192.168.1.104")
remoteaddr.Port = 5000
if localaddr.IP == nil || remoteaddr.IP == nil {
fmt.Println("error")
}
if _, err := net.DialTCP("tcp", &localaddr, &remoteaddr); err != nil {
fmt.Println(err)
}
fmt.Println("End")
}
If the function specify local IP address, it always reports a run time error "dial tcp 192.168.1.104:5000: An invalid argument was supplied." I'm confused, should it always be a nil for local IP address ?
go version : 1.1 Beta
OS: Win7 64bit
Tried in go 1.0.3, it seemed OK
Go's net package, like most such implementations, recognizes port-only syntax for connecting to localhost:
:5000
is equivalent to
<my-ip-address>:5000
Thus, you can do:
net.Dial("tcp", ":5000")
No, it is ok to have laddr argument specified in DialTCP. The problem is that the specified port (6000) is outside of default Win7 dynamic port range and couldn't be used for outgoing connection. It works on Linux and Mac, actually (if you don't forget to change the IP address to one of your own system).