Different behaviors of golang tcp socket on different OS - windows

I want to simulate the http server with tcp socket written in Go. The program runs well on Linux or Mac, but has some problems on Windows. I have configured the built-in firewall to allow connections on the port. On Windows, when the browser requests the program, it shows me "This site can’t be reached. The connection was reset." Instead, it can response "Hello world" correctly on Linux or Mac. As follows.
// implement http server with tcp socket
package main
import (
"log"
"net"
"os"
)
var content = []byte(`HTTP/1.1 200 OK
Content-type: text/plain
Hello world!`)
func handleConn(conn net.Conn) {
conn.Write(content)
defer conn.Close()
}
func main() {
addr := "localhost:10000"
listener, err := net.Listen("tcp", addr)
checkErr(err)
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
continue
}
go handleConn(conn)
}
}
func checkErr(err error) {
if err != nil {
log.Fatal(err)
os.Exit(1)
}
}
I try to change some code to read all the bytes from the connection on Windows, after that, the program responses correctly.
func handleConn(conn net.Conn) {
var buf = make([]byte, 1024)
_, err := conn.Read(buf)
checkErr(err)
conn.Write(content)
defer conn.Close()
}
But I don't know why must I read all the bytes of connection on Windows.

Related

go network traffic GRE encapsulation

I want to write an application using golang that will intercept any traffic from the NIC to an application and from the application to the nic running on the machine. For example, we are running an HTTP server.
This application will look into the packet, and if the packet has a GRE header from a specific IP, it will decapsulate the traffic and forward using the inner IP header. With traffic from the application to the NIC, I want to encapsulate the packet with a GRE header.
I have no idea where to start and need some advice.
Update my findings:
So I have the following:
package main
import (
"fmt"
"io"
"net"
)
func main() {
ln, err := net.Listen("tcp", "0.0.0.0:8000")
if err != nil {
panic(err)
}
for {
conn, err := ln.Accept()
if err != nil {
panic(err)
}
go handleRequest(conn)
}
}
func handleRequest(conn net.Conn) {
fmt.Println("new client")
proxy, err := net.Dial("tcp", "10.10.1.1:80")
if err != nil {
panic(err)
}
fmt.Println("proxy connected")
go copyIO(conn, proxy)
go copyIO(proxy, conn)
}
func copyIO(src, dest net.Conn) {
defer src.Close()
defer dest.Close()
io.Copy(src, dest)
}
I believe that I have to have the logic in this go copyIO(proxy, conn) call.

Golang SSH Source Port

I have a requirement to use a static source port. We will do an IPTables redirect rule based on this source port. So, the static source port is used as an identifier as multiple connections are pending to the same destination port on the server. Think poor man's TCP mux a la iptables.
I have followed the Golang examples and cobbled some messy code together. I am not a programmer.
The ssh.dial function handles a lot, that becomes apparent once you use net.dial along with ssh.NewClientConn, ssh.NewClient and ssh.NewSession.
I see there is no ProxyCommand like in OpenSSH config options. I was using:
ssh -o ProxyCommand="ncat --source-port %h %p" ...
to achieve the requirement in a Bash script.
Additionally, I apologise for a loaded question but ncat et al. allow me to reuse the source port immediately.
Whereas Golang SSH leaves a TIME-WAIT 0 0 192.168.99.53:31337 192.168.99.7:22 for 60 seconds on Arch Linux.
Obviously, subsequent binds to said source port result in an error.
WRT the below code:
I have omitted the ExampleHostKeyCheck function
I know the sClient.Listen is a poor attempt at getting this work
The remote port forward does NOT appear on the server
I'm assuming a LOT more code is now required to handle the channels etc.?
The shell command does work, the file appears in /tmp/
package main
import (
"bytes"
"log"
"net"
"os"
"bufio"
"strings"
"path/filepath"
"golang.org/x/crypto/ssh"
)
func main() {
hostKey, err := ExampleHostKeyCheck()
conf := &ssh.ClientConfig{
User: "robert",
Auth: []ssh.AuthMethod{
ssh.Password("password"),
},
HostKeyCallback: ssh.FixedHostKey(hostKey),
}
server, _ := net.ResolveTCPAddr("tcp", "centos7.ephemeric.local:22")
client, _ := net.ResolveTCPAddr("tcp", ":31337")
cc, err := net.DialTCP("tcp", client, server)
//cc, err := net.DialTCP("tcp", nil, server)
if err != nil {
log.Fatalf("%s", err)
}
defer cc.Close()
conn, chans, reqs, err := ssh.NewClientConn(cc, "", conf)
if err != nil {
log.Fatalf("%s", err)
}
defer conn.Close()
// https://stackoverflow.com/questions/35906991/go-x-crypto-ssh-how-to-establish-ssh-connection-to-private-instance-over-a-ba
sClient := ssh.NewClient(conn, chans, reqs)
if err != nil {
log.Fatalf("%s", err)
}
defer sClient.Close()
session, err := sClient.NewSession()
if err != nil {
log.Fatalf("%s", err)
}
defer session.Close()
sListen, err := sClient.Listen("tcp", "127.0.0.1:31337")
if err != nil {
log.Fatal("unable to register tcp forward: ", err)
}
defer sListen.Close()
var b bytes.Buffer // import "bytes"
session.Stdout = &b // get output
// You can also pass what gets input to the stdin, allowing you to pipe content from client to server session.Stdin = bytes.NewBufferString("MyInput").
err = session.Run("echo slobwashere >>/tmp/slobwashere; ls")
}
Thank you.

"connect: connection refused" while attempting to connect to localhost

I'm currently attempting to create a TCP service that will just log/store whatever is sent to it. I can't seem to understand why I cannot connect to my localhost using DialTCP. I keep getting
dial tcp 127.0.0.1:8080: connect: connection refused
func main() {
errCh := make(chan error)
tcpAddr, _ := net.ResolveTCPAddr("tcp", "localhost:8080")
for {
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
log.Println("Could not connect -> ", err.Error())
} else {
recordMessage(conn, errCh)
err = <-errCh
log.Println("Error", err)
conn.Close()
}
log.Println("trying again in 10 seconds..")
time.Sleep(30 * time.Second)
}
}
I looked over my Firewall settings and noting seems to be blocking it. I'm still not sure if its due to something related to my Firewall or if I'm just missing something super obvious.
Start by running this Go program in a terminal -- it listens to port 2000 but you could change it to 8080 or whatever you wish:
func main() {
// Listen on TCP port 2000 on all interfaces.
l, err := net.Listen("tcp", ":2000")
if err != nil {
log.Fatal(err)
}
defer l.Close()
for {
// Wait for a connection.
conn, err := l.Accept()
if err != nil {
log.Fatal(err)
}
// Handle the connection in a new goroutine.
// The loop then returns to accepting, so that
// multiple connections may be served concurrently.
go func(c net.Conn) {
log.Println(c)
// Echo all incoming data.
io.Copy(c, c)
// Shut down the connection.
c.Close()
}(conn)
}
}
Then in a separate terminal run this simple client:
func main() {
var addr string
if len(os.Args) > 1 {
addr = os.Args[1]
} else {
addr = "localhost:2000"
}
conn, err := net.Dial("tcp", addr)
if err != nil {
log.Fatal(err)
// handle error
}
fmt.Fprintf(conn, "foobar")
conn.Close()
}
Asking it to connect to the same port. The connection should succeed and you should see the server logging something.
Now try to connect with your client.
Without writing Go, you could to these things with the nc command-line tool (netcat). nc -lv PORT creates a simple listening server on PORT, for example.

SMTP connection read welcome message

I try to connect on smtp server and read welcome message. This is my code:
package main
import (
"fmt"
"net"
"time"
"net/smtp"
"bufio"
)
func main() {
// attempt a connection
conn, _ := net.DialTimeout("tcp", "88.198.24.108:25", 15 * time.Second)
buf := bufio.NewReader(conn)
bytes, _ := buf.ReadBytes('\n')
fmt.Printf("%s", bytes)
client, err := smtp.NewClient(conn, "88.198.24.108")
if err != nil {
fmt.Println("1>>", err)
return
}
client.Quit()
conn.Close()
}
Problem is after read welcome message stop running and wait to go in timeout, I want to read/print welcome message and continue.
220 example.me ESMTP Haraka/2.8.18 ready
1>> 421 timeout
An inspection of the standard library source indicates that smtp.NewClient() reads the SMTP banner from the remote host and throws it away.
func NewClient(conn net.Conn, host string) (*Client, error) {
text := textproto.NewConn(conn)
_, _, err := text.ReadResponse(220)
if err != nil {
text.Close()
return nil, err
}
c := &Client{Text: text, conn: conn, serverName: host, localName: "localhost"}
_, c.tls = conn.(*tls.Conn)
return c, nil
}
You want to read this banner and decide whether to send mail based on its contents.
Since you have already read the banner yourself, and presumably will make a decision on that, instead of calling smtp.NewClient() you should then implement the rest of NewClient() in your own code, possibly something like this:
client := &smtp.Client{Text: text, conn: conn, serverName: host, localName: "localhost"}
_, client.tls = conn.(*tls.Conn)

Golang: Mixing Gin with an UDP server

I'm trying to use both a UDP server to listen continuously to datagrams and a http server, but the string "UDP server up and listening on port..." and command "server.Run()" are never reached.
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net"
)
func handleUDPConnection(conn *net.UDPConn) {
buffer := make([]byte, 8096)
n, addr, err := conn.ReadFromUDP(buffer)
if err != nil {
log.Fatal(err)
} else {
fmt.Println("UDP client: ", addr)
fmt.Println("Received from UDP client: ", string(buffer[:n]))
}
}
func main() {
server := gin.Default()
host, port := "localhost", "41234"
udpAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%s", host, port))
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
server.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "pong"})
})
for {
handleUDPConnection(conn)
}
fmt.Sprintf("UDP server up and listening on port %s\n", port)
server.Run()
}
How can I make it work?
There is an infinite loop in your code.
for {
handleUDPConnection(conn)
}
This will repetedly call the handleUDPConnection function until the program exits without ever moving on to
fmt.Sprintf("UDP server up and listening on port %s\n", port)
server.Run()
Perhaps you want to deal with the connections in a go thread. This would be something more like this:
//define an exit variable
keepListening := true
//spawn a go routine (starts the function on another thread*)
go func() {
for keepListening {
handleUDPConnection(conn)
}
}()
//notify the user that the server is listening
fmt.Sprintf("UDP server up and listening on port %s\n", port)
//run the server (I assume this function call is blocking
server.Run()
//stop the go routine when the server is done running
keepListening = false
Hope this helps!
*a goroutine is not a thread. It can be useful/simple to think of it like that, but they are distinctly different. Here's an article explaining some of the differences and advantages.

Resources