Emulate net connection without entering net dial - go

I currently am working on vendor go balancer code. I need to remove the tcp dial call and emulate a successful connection without the call. In the code below, there is
this line:
ds, err := net.Dial("tcp", backend.String());if err != nil {
log.Printf("failed to dial %s: %s", backend, err)
us.Close()
return
}
What this does is make a dial to the tcp server and then return connection
response in ds, which is defined here: https://golang.org/pkg/net/#Dial
What i need is to obtain the ds without doing the tcp dialer. I'm
trying to test the load balancer without any actual tcp calls. So,
essentially,
when we enter handleConnection wed create a net connection prior to the tcp dial and use this net conn, which should emulate 100% net connection before the tcp dialing begins.
func copy(wc io.WriteCloser, r io.Reader) { defer wc.Close()
io.Copy(wc, r)
}
func handleConnection(us net.Conn, backend BA.Backend) {
if backend == nil {
log.Printf("no backend available for connection from %s",
us.RemoteAddr())
us.Close()
return
}
host, _, _ := net.SplitHostPort(us.RemoteAddr().String())
_, ok := dbAuthTokenData[host]; if !ok {
w := bufio.NewWriter(us)
w.WriteString("InvalidCredentials")
w.Flush()
us.Close()
return
}
ds, err := net.Dial("tcp", backend.String());if err != nil {
log.Printf("failed to dial %s: %s", backend, err)
us.Close()
return
}
// Ignore errors
go copy(ds, us)
go copy(us, ds)
}
func tcpBalance(bind string, backends BA.Backends) error {
log.Println("using tcp balancing")
ln, err := net.Listen("tcp", bind)
if err != nil {
return fmt.Errorf("failed to bind: %s", err)
}
log.Printf("listening on %s, balancing %d backends", bind, backends.Len())
for {
conn, err := ln.Accept()
if err != nil {
log.Printf("failed to accept: %s", err)
continue
}
go handleConnection(conn, backends.Choose())
}
return err
}
I tried commenting out go handleConnection(conn, backends.Choose()) but that failed.

The pattern you could refactor your code is to create a Dialer interface. In your code example you are using the returned ds just as an io.ReadWriteCloser. So you don't need to implement the whole net.Conn interface. As net.Conn has the read and write method inside everything works
type Dialer interface{
Dial(network, address string) (io.ReadWriteCloser, error)
}
Now let's change your function:
func handleConnection(us net.Conn, backend BA.Backend, d Dialer) {
// ...
// Code here stays
ds, err := d.Dial("tcp", backend.String());if err != nil {
log.Printf("failed to dial %s: %s", backend, err)
us.Close()
return
}
// ...
}
That your production code works you now need to define a type netDialer which wraps the net.Dial() function. In your test you can use a testDialer which uses a bytes.Buffer.
This answers your question:
Emulate net connection without entering net dial

Related

SSH proxy, bad packet length

Implementing an ssh proxy in Go, errors out with bad packet length, these are the errors with ssh in debug mode:
debug1: SSH2_MSG_KEXINIT sent
Bad packet length 1231976033.
ssh_dispatch_run_fatal: Connection to ::1 port 8080: message authentication code incorrect
Code:
func handleSSH(conn net.Conn, r *bufio.Reader, protocol string) {
target, err := url.Parse("ssh://localhost:3333")
if err != nil {
fmt.Println("Error parsing target", err)
conn.Close()
return
}
targetConn, err := net.Dial("tcp", target.Host)
if err != nil {
fmt.Println("error dialing SSH target:", err)
conn.Close()
return
}
defer targetConn.Close()
var wg sync.WaitGroup
wg.Add(2)
go func() {
_, err := io.Copy(targetConn, conn)
if err != nil {
fmt.Println("error copying data to target:", err)
}
wg.Done()
}()
go func() {
_, err := io.Copy(conn, targetConn)
if err != nil {
fmt.Println("error copying data from target:", err)
}
wg.Done()
}()
wg.Wait()
conn.Close()
}
// EDIT
func connection(conn net.Conn) {
r := bufio.NewReader(conn)
protocol, err := r.ReadString('\n')
if err != nil {
fmt.Println("Error reading first line", err)
conn.Close()
return
}
if protocol[0:3] == "SSH" {
handleSSH(conn, r, protocol)
}
}
func main() {
ln, err := net.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
panic(err)
}
go connection(conn)
}
}
EDIT: added code for relevant information on how the connection is initiated and reproduce the error.
My best guess is the ssh negotiation process is being interrupted, and things goes out of sync.
The code is reading the first line from the client and checks the kind of protocol in order to call the appropriate handler:
protocol, err := r.ReadString('\n')
...
if protocol[0:3] == "SSH" {
handleSSH(conn, r, protocol)
}
}
But the code fails to forward the already read bytes to the connected server. These bytes are in protocol and are given to handleSSH. But it fails to send these bytes to the connected server once the connection is established. Instead it only copies new data between client and server.
This means the server does not get the first line from the client. It therefore likely complains about a protocol error with something like Invalid SSH identification string. which gets forwarded to the client and misinterpreted as valid data from an SSH connection.

Golang grpc: how to determine when the server has started listening?

So I have the following:
type Node struct {
Table map[string]string
thing.UnimplementedGreeterServer
address string
}
func (n *Node) Start() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
thing.RegisterGreeterServer(s, n)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
In my main function I'll spin up mulitple nodes like so:
func main() {
n :=Node{Table: map[string]string{}}
go n.Start()
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
}
The problem is, because I'm spinning up the node concurrently, there's a chance the dial up connection might not work because the node might not have been setup yet.
Ideally, I'd like a done channel that tells me when the grpc server has actually started listening. How do I accomplish this?
This is essntially the same problem as How to add hook on golang grpc server start? which doesn't have an answer
s.Serve(listener) blocks, so you can't achieve your purpose by having a done chan, instead you have to implement the healthcheck and readiness for your service, and check those before performing any request by the client.
The server should implement the following proto:
syntax = "proto3";
package grpc.health.v1;
message HealthCheckRequest {
string service = 1;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
SERVICE_UNKNOWN = 3; // Used only by the Watch method.
}
ServingStatus status = 1;
}
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}
For example, the envoy proxy grpc_health_check works with the above proto.
Read GRPC Health Checking Protocol for more information.
The server can be Dialed as soon as net.Listen returns a nil error. Dial will block until the server calls Accept (which will happen somewhere in s.Serve in this case).
Either move creation of the listener into the caller and pass it as an argument:
func (n *Node) Start(lis net.Listener) {
s := grpc.NewServer()
thing.RegisterGreeterServer(s, n)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
n := Node{Table: map[string]string{}}
go n.Start(lis)
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
}
Or signal that the listener is up after Listen returns:
func (n *Node) Start(up chan struct{}) {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
if up != nil {
close(up)
}
s := grpc.NewServer()
thing.RegisterGreeterServer(s, n)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
func main() {
n := Node{Table: map[string]string{}}
up := make(chan struct{})
go n.Start(up)
<-up
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
}
For all those who are still looking for an answer to this, here is another simple way to do it. Start the server in a child routine. Here is a code snippet:
// Start the server in a child routine
go func() {
if err := s.Serve(listener); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}()
fmt.Println("Server succesfully started on port :50051")
In my case I am using MongoDB as well, so when you run it, you get:
grpc-go-mongodb-cobra>go run server/main.go
Starting server on port :50051...
Connecting to MongoDB...
Connected to MongoDB
Server succesfully started on port :50051
I have also written a Blog post on this, with working code in GitHub. Here is the link: https://softwaredevelopercentral.blogspot.com/2021/03/golang-grpc-microservice.html

How to cache a TCP reverse proxy data transmission?

I've accomplished implementing TCP reverse proxy in GoLang. But unfortunately couldn't come up with implementing caching to a TCP reverse proxy. Is it possible to do so, if yes, is there any resource out there? Is caching possible on a TCP (Transport Layer of Network)?
Here's the simple TCP reverse proxy in Golang.
package main
import (
"io"
"log"
"net"
)
//Proxy struct
type Proxy struct {
laddr, raddr *net.TCPAddr
lconn, rconn io.ReadWriteCloser
errorSignal chan bool
}
// New Create a new Proxy instance.
func New(lconn *net.TCPConn, laddr, raddr *net.TCPAddr) *Proxy {
return &Proxy{
lconn: lconn,
laddr: laddr,
raddr: raddr,
errorSignal: make(chan bool),
}
}
//TCPAddressResolver resolves an address and returns to a struct having ip and port.
func TCPAddressResolver(addr string) (tcpAddress *net.TCPAddr, err error) {
tcpAddress, err = net.ResolveTCPAddr("tcp", addr)
return
}
func main() {
listenerAddress, err := TCPAddressResolver(":8080")
if err != nil {
log.Fatalf("Failed to resolve local address: %v", err)
}
remoteAddress, err := TCPAddressResolver(":3000")
if err != nil {
log.Fatalf("Failed to resolve remote address: %v", err)
}
listener, err := net.ListenTCP("tcp", listenerAddress)
if err != nil {
log.Fatalf("Failed to open local port to listen: %v", err)
}
log.Printf("Simple Proxy started on: %d and forwards to port %d", listenerAddress.Port, remoteAddress.Port)
for {
conn, err := listener.AcceptTCP()
if err != nil {
log.Fatalf("Failed to accept connection: %v", err)
continue
}
var p *Proxy
// HTTP is a stateless protocol thus a proxy needs to reinitiate the new next incoming call (conn)
// each time it finishes handling the previous one.
p = New(conn, listenerAddress, remoteAddress)
p.Start()
}
}
//Start initiates transmission of data to and from the remote to client side.
func (p *Proxy) Start() {
defer p.lconn.Close()
var err error
p.rconn, err = net.DialTCP("tcp", nil, p.raddr)
if err != nil {
log.Fatalf("Remote connection failure: %v", err)
}
defer p.rconn.Close()
go p.CopySrcDst(p.lconn, p.rconn)
go p.CopySrcDst(p.rconn, p.lconn)
//Wait for everything to close -- This one blocks the routine.
<-p.errorSignal
log.Printf("Closing Start routine \n")
}
func (p *Proxy) err(err error) {
if err != io.EOF {
log.Printf("Warning: %v: Setting error signal to true", err)
}
p.errorSignal <- true
}
//CopySrcDst copies data from src to dest
func (p *Proxy) CopySrcDst(src, dst io.ReadWriteCloser) {
buff := make([]byte, 1024)
for {
n, err := src.Read(buff)
if err != nil {
// Reading error.
p.err(err)
return
}
dataFromBuffer := buff[:n]
n, err = dst.Write(dataFromBuffer)
if err != nil {
// Writing error.
p.err(err)
return
}
}
}
You are asking how to save data read from an io.Reader. That's different from caching.
The easiest approach is to tee the reader into a buffer.
While you are at it, you might as well use io.Copy instead of the similar code in the question. The code in the question does not handle the case when read returns n > 0 and a non-nil error.
Use an error group to coordinate waiting for the goroutines and collecting error status.
var g errgroup.Group
var rbuf, lbuf bytes.Buffer
g.Go(func() error {
_, err := io.Copy(lconn, io.TeeReader(p.rconn, &rbuf))
return err
})
g.Go(func() error {
_, err := io.Copy(rconn, io.TeeReader(p.lconn, &lbuf))
return err
})
if err := g.Wait(); err != nil {
// handle error
}
// rbuf and lbuf have the contents of the two streams.
The name of the programming language is "Go", not "Golang" or "GoLang".

"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.

Retrieve uri path from net.Conn

I'm working with someone else's code and I need some assistance.
Here we have a tcp load balancer. What I need is access to the request uri from this piece of code before we do the net dial.
I looked through the API Documentation here: https://golang.org/pkg/net/ but was unable to find any relevant methods under the net.Conn namespace for retrieving the current request path.
func copy(wc io.WriteCloser, r io.Reader) {
defer wc.Close()
io.Copy(wc, r)
}
func handleConnection(us net.Conn, backend BA.Backend) {
if backend == nil {
log.Printf("no backend available for connection from %s", us.RemoteAddr())
us.Close()
return
}
ip:=us.RemoteAddr().String()
parts:=strings.Split(ip,":")
ip=parts[0]
//w := bufio.NewWriter(us)
//w.WriteString(+"\n")
//w.Flush()
ds, err := net.Dial("tcp", backend.String())
if err != nil {
log.Printf("failed to dial %s: %s", backend, err)
us.Close()
return
}
// Ignore errors
go copy(ds, us)
go copy(us, ds)
}
func tcpBalance(bind string, backends BA.Backends) error {
log.Println("using tcp balancing")
ln, err := net.Listen("tcp", bind)
if err != nil {
return fmt.Errorf("failed to bind: %s", err)
}
log.Printf("listening on %s, balancing %d backends", bind, backends.Len())
for {
conn, err := ln.Accept()
if err != nil {
log.Printf("failed to accept: %s", err)
continue
}
go handleConnection(conn, backends.Choose())
}
return err
}
This is not possible because not every TCP connection is an HTTP connection and the TCP protocol has no concept of URIs. Many other protocols are built on TCP and it's not that protocol's responsibility to know about them.
HTTP is an Application Layer (layer 7) protocol which uses the TCP Transport Layer (layer 4) protocol. As such, TCP connections have no concept of HTTP because lower layer protocols have no concept of the layers that may be built on top of them.
You can learn more by reading about the OSI model.

Resources