How to set ToS field in IP header for a TCP connection using Golang - sockets

I am trying to create a TCP server and client using Golang where I am able to set the Type of Service field in the IP header in order to prioritise different traffic flows.
The client and servers are able to communicate but I can not figure out how to set the ToS field.
I have tried using the ipv4 Golang package with the method described here: https://godoc.org/golang.org/x/net/ipv4#NewConn
A simplified server example:
func main () {
ln, err := net.Listen("tcp4", "192.168.0.20:1024")
if err != nil {
// error handling
}
defer ln.Close()
for {
c, err := ln.Accept()
if err != nil {
// error handling
}
go func(c net.Conn) {
defer c.Close()
if err := ipv4.NewConn(c).SetTOS(0x28); err != nil {
fmt.Println("Error: ", err.Error())
}
}(c)
}
And the corresponding client (also simplified)
func main () {
conn, err := net.Dial("tcp4", "192.168.0.20:1024")
if err != nil {
fmt.Println(err)
}
for {
writer := bufio.NewWriter(conn)
// Create "packet"
Data := make([]byte, 1200)
endLine := "\r\n"
//Set packetLength
length := strconv.FormatInt(int64(1200), 10)
copy(Data[0:], length)
//Set ID
idString := strconv.FormatInt(int64(1), 10)
if strings.Contains(idString, "\r") || strings.Contains(idString, "\n") || strings.Contains(idString, "\r\n") {
fmt.Println("This is gonna result in an error in the id string.")
}
idbuf := []byte(idString)
copy(Data[15:], idbuf)
//Set timestamp
timestamp0 := time.Now().UnixNano()
timestampString := strconv.FormatInt(timestamp0, 10)
if strings.Contains(timestampString, "\r") || strings.Contains(timestampString, "\n") || strings.Contains(timestampString, "\r\n") {
fmt.Println("This is gonna result in an error in the timestamp string.")
}
buf := []byte(timestampString)
copy(Data[50:], buf)
copy(Data[int(1200)-2:], endLine)
if len(Data) != int(1200) {
fmt.Println("This is also gonna be an error. Length is: ", len(Data))
}
//Send the data and flush the writer
writer.Write(Data)
writer.Flush()
}
//time.Sleep(1*time.Nanosecond)
}
I have also tried creating my own dialer with a control function that passes a syscall in order to set the socket like this:
dialer := &net.Dialer{
Timeout: 5 * time.Second,
Deadline: time.Time{},
LocalAddr: tcpAddr,
DualStack: false,
FallbackDelay: 0,
KeepAlive: 0,
Resolver: nil,
Control: highPrio,
}
func highPrio(network, address string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
// set the socket options
err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 128)
if err != nil {
log.Println("setsocketopt: ", err)
}
})
I am verifying that it does not work by inspecting the traffic with Wireshark and am using Windows 10 Pro as my OS.

I am try you ToS set method at Dial() with golang 1.15.5 and its worked:
dialer := net.Dialer{
Timeout: this.TcpWaitConnectTimeout,
}
dialer.Control = func(network, address string, c syscall.RawConn) error {
var err error
c.Control(func(fd uintptr) {
err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x80)
})
return err
}
c, err := dialer.Dial("tcp", this.serverAddr)
tcpdump show me right ToS

Related

Golang http client - connectex: Only one usage of each socket address (protocol/network address/port) is normally permitted

I`m trying to create a Golang net/http based web service, which takes a JSON input from user, and then it queries a third party web API itselves, to get data and then returns answer to client. I use http.Client{} client.Do() method to query the third party web API. When I perform a performance test (JMeter), very often I get the error: connectex: Only one usage of each socket address (protocol/network address/port) is normally permitted. I suspected the race state, but race detection shows no state of race. To my mind, posting HTTP requests too often, may cause port exhaustion over time. So, I tried to balance queries between two or three instances (hosts). However, I still get the error rather often. Is there a way to avoid this error? Is there any architecture trick, to make my service high-load ready? You can see the simplified code example below:
var upstream = []string{"1.1.1.1", "2.2.2.2", "3.3.3.3"}
var scheme = "http"
var timeout = 5
func main() {
fmt.Println("Listening on *: 8080")
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func handler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, http.StatusText(405), 405)
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println("Error:", err)
http.Error(w, http.StatusText(400), 400)
return
}
var jsonEntity = make(map[string]string)
json.Unmarshal(body, &jsonEntity)
payload := bytes.NewReader(body)
url := r.URL
url.Host = upstream[0]
if len(upstream) > 1 {
url.Host = upstream[random(0, len(upstream))]
}
url.Scheme = scheme
proxyReq, err := http.NewRequest(http.MethodPost, url.String(), payload)
if err != nil {
fmt.Println("Error:", err)
http.Error(w, http.StatusText(400), 400)
return
}
client := http.Client{Timeout: time.Duration(timeout) * time.Second}
response, err := client.Do(proxyReq) // <- IT FAILS HERE!!!
if err != nil {
fmt.Println("Error:", err)
http.Error(w, http.StatusText(500), 500)
sendWithCheck(w, "%s", []byte(`{"answerParam":"error"}`))
return
}
defer func() {
if err := response.Body.Close(); err != nil {
log.Println("Error:", err)
}
}()
res, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Println("Error:", err)
http.Error(w, http.StatusText(500), 500)
sendWithCheck(w, "%s", []byte(`{"answerParam":"error"}`))
return
}
if response.StatusCode == 200 {
sendWithCheck(w, "%s", res)
return
}
w.WriteHeader(response.StatusCode)
return
}

Exec a function when (re)connected to MongoDB

I'm using https://github.com/mongodb/mongo-go-driver
Is there any chance to be notified when Go MongoDB client is connected or reconnected? I wish to log this event, but don't see a way, so currently reconnecting happens on the background and is invisible.
Trying according to the advice in a comment:
type wConn struct {
net.Conn
}
func (c *wConn) Close() error {
fmt.Println("closed")
return c.Conn.Close()
}
type wDialer struct {
net.Dialer
}
func NewDialer() *wDialer {
dialer := &wDialer{}
dialer.Timeout = 5 * time.Second
return dialer
}
func (d *wDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
c, err := ... // ??? How to get a connection?
// Like this?
// nd := &net.Dialer{}
// c, err := nd.DialContext(ctx, network, address)
if err != nil {
return nil, err
}
nc := &wConn{Conn: c}
return nc, nil
}
...
td := NewDialer()
client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://127.0.0.1:27017").SetDialer(td))
Here the question near ??? is what is a way to get a Conn connection object?

UDP Socket not reading from server in Go

I'm developing a fast dns client in go just to mess around with But I'm facing troubles at the time of reading from server responses cause it never arrives and I know it actually did because I have WireShark open and it read the packet.
Here is the code sample(8.8.8.8 is Google DNS and the hex msg is a valid DNS query):
package main
import (
"fmt"
"net"
"encoding/hex"
"bufio"
)
func CheckError(err error) {
if err != nil {
fmt.Println("Error: " , err)
}
}
func main() {
Conn, err := net.Dial("udp", "8.8.8.8:53")
CheckError(err)
defer Conn.Close()
msg, _ := hex.DecodeString("5ab9010000010000000000001072312d2d2d736e2d68357137646e65650b676f6f676c65766964656f03636f6d0000010001")
scanner := bufio.NewScanner(Conn)
buf := []byte(msg)
_, err1 := Conn.Write(buf)
if err1 != nil {
fmt.Println(msg, err1)
}
for scanner.Scan() {
fmt.Println(scanner.Bytes())
}
}
Here you have the proof that it actually arrives:
WireShark Screen Capture
I've testes reading directly from conn with:
func main() {
Conn, err := net.Dial("udp", "8.8.8.8:53")
CheckError(err)
defer Conn.Close()
msg, _ := hex.DecodeString("5ab9010000010000000000001072312d2d2d736e2d68357137646e65650b676f6f676c65766964656f03636f6d0000010001")
buf := []byte(msg)
_, err1 := Conn.Write(buf)
if err1 != nil {
fmt.Println(msg, err1)
}
Reader(Conn)
}
func Reader(conn net.Conn) {
var buf []byte
for {
conn.Read(buf)
fmt.Println(buf)
}
}
You can't use bufio around a UDP connection. UDP is not a stream oriented protocol, so you need to differentiate the individual datagrams yourself, and avoid partial reads to prevent data loss.
In order to read from an io.Reader, you must have space allocated to read into, and you need to use the bytes read value returned from the Read operation. Your example could be reduced to:
conn, err := net.Dial("udp", "8.8.8.8:53")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
msg, _ := base64.RawStdEncoding.DecodeString("WrkBAAABAAAAAAAAEHIxLS0tc24taDVxN2RuZWULZ29vZ2xldmlkZW8DY29tAAABAAE")
resp := make([]byte, 512)
conn.Write(msg)
n, err := conn.Read(resp)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%q\n", resp[:n])

How to read data (xml) sent by server if it doesn't send new line

Let's say we try to communicate with a server (XMPP) which sends back XML data. We can use
conn, err := net.Dial("tcp", s.Addr+":5222")
//...
r := bufio.NewReader(conn)
//...
s, err := s.R.ReadString(10) // to read a string
But there is one problem that the server doesn't send the \10 (newline) symbol. I also tried 12 but without any luck. Same goes for readLine function as it also relies on \10. So how do I read the data sent by server?
I tried using '>' as a delimiter and succeeded to receive only parts of the messages (predictable). I had an idea to loop while error is nil and use delimiter of '>' but it also didn't work.
My research shown that the last symbol of the message is really '>' (62) and there is not any anything else at the end.
Use an xml.Decoder to read stanzas from an XMPP stream.
conn, err := net.Dial("tcp", s.Addr+":5222")
if err != nil {
// handle error
}
dec := xml.NewDecoder(conn)
Use the decoder Token method to read the root document element and to skip over character data between stanzas:
func readStartElement(dec *xml.Decoder) (xml.StartElement, error) {
for {
t, err := dec.Token()
if err != nil {
return xml.StartElement{}, err
}
switch t := t.(type) {
case xml.StartElement:
return t, nil
}
}
}
Use the decoder DecodeElement method to read a stanza:
func readStanza(dec *xml.Decoder) (interface{}, error) {
se, err := readStartElement(dec)
if err != nil {
return nil, err
}
var v interface{}
switch se.Name.Space + " " + se.Name.Local {
case "jabber:client message":
v = &jabberMessage{} // jabberMessage is struct type defined by app for messages
// Add other stanza types here.
default:
v = &struct{}{}
}
if err := dec.DecodeElement(v, &se); err != nil {
return nil, err
}
return v, nil
}
Type switch on the return value from readStanza to handle the different types of received stanzas.
A client reads stanzas synchronously. Here's rough outline (ignoring authentication, etc).
conn, err := net.Dial("tcp", s.Addr+":5222")
if err != nil {
// handle error
}
dec := xml.NewDecoder(conn)
// read and discard root element
_, err := readStartElement(dec)
if err != nil {
// handle error
}
// read stanzas
for {
v, err := readStanza(dec)
if err != nil {
// handle error
// must break out of loop on error
}
switch v := v.(type) {
case *jabberMessage:
// handle message
case *someOtherStanzaType:
// handle other stanza types
// ... and so on
}
}

golang tcp socket does not send message after write immediately

My GO version is 1.1.1
the sever recieved messages after connection close, but NoDelay was setted.
Is there something wrong
addr, _ := net.ResolveTCPAddr("tcp", "localhost:5432")
conn, err := net.DialTCP("tcp", nil, addr)
defer conn.Close()
if err != nil {
fmt.Println("connect fail")
return
}
err = conn.SetNoDelay(true)
if err != nil {
fmt.Println(err.Error())
}
for {
var message string
_, err := fmt.Scanln(&message)
if err != nil && err.Error() != "unexpected newline" {
fmt.Println("input finished", err)
break
}
if message == "" {
fmt.Println("no input, end")
break
}
// message = fmt.Sprintf("%s\n",message)
//fmt.Fprintf(conn, message) // send immediately but following message won't send any more
conn.Write([]byte(message)) // won't send until connection close
}
There doesn't seem to be anything vitally wrong with your code so I'm guessing the error is on the server end.
If you create a local TCP server on port 5432 you can test this.
Try running the below server code and then test your client code against it. It just echos all received data to stdout.
package main
import (
"io"
"log"
"net"
"os"
)
func main() {
l, err := net.Listen("tcp", "localhost:5432")
if err != nil {
log.Fatal(err)
}
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
log.Fatal(err)
}
go func(c net.Conn) {
defer c.Close()
io.Copy(os.Stdout, c)
}(conn)
}
}
You should see each line sent to the client printed (without the newline) as soon as you hit enter.
the problem is on the server end.
func handleConnection(conn net.Conn) {
// I didn't put it in for loop
message, err := bufio.NewReader(conn).ReadString('\n')
}