package main import ( "bytes" "fmt" "log" "net" "os" "regexp" "runtime" "strings" "time" ) type protocolReq map[string]string type funcReq map[string]string func NewProtocol() protocolReq { pr := make(protocolReq) pr["cmpfile"] = "" pr["client"] = "76" pr["api"] = "99999" pr["enableStreams"] = "" hn, err := os.Hostname() checkError(err) pr["host"] = hn pr["port"] = "localhost:8192" pr["sndbuf"] = "98303" pr["rcvbuf"] = "796356" pr["func"] = "protocol" return pr } func NewUserInfoReq() funcReq { uir := make(funcReq) uir["prog"] = "gop4" uir["version"] = strings.Join( []string{ (runtime.GOOS + runtime.GOARCH), "1"}, "/") hn, err := os.Hostname() checkError(err) uir["client"] = hn // Lookup .p4config OR use hostname wd, err := os.Getwd() checkError(err) uir["cwd"] = wd uir["os"] = "UNIX" // Guess this is a switch on GOOS to get LE's uir["user"] = "brett" // Lookup .p4config OR use os username? uir["charset"] = "1" uir["func"] = "user-info" return uir } func createRequest(p protocolReq, f funcReq) bytes.Buffer { header := make([]byte, 5) length := make([]byte, 4) var protoB bytes.Buffer // These are out of order, does it matter???? probably for key, value := range p { protoB.WriteString(key) protoB.WriteByte(byte(0)) l := getVarLen(value) copy(length[:], l[0:3]) protoB.Write(length) if value != "" { protoB.WriteString(value) } protoB.WriteByte(byte(0)) } var funcB bytes.Buffer for key, value := range f { funcB.WriteString(key) funcB.WriteByte(byte(0)) l := getVarLen(value) copy(length[:], l[0:3]) funcB.Write(length) if value != "" { funcB.WriteString(value) } funcB.WriteByte(byte(0)) } var fullB bytes.Buffer pH := createHeader(protoB) copy(header[:], pH[0:4]) fullB.Write(header) protoB.WriteTo(&fullB) fH := createHeader(funcB) copy(header[:], fH[0:4]) fullB.Write(header) funcB.WriteTo(&fullB) return fullB } func createHeader(b bytes.Buffer) [5]byte { if b.Len() >= 0x1fffffff { log.Panicf("String %s is too long to be sent", b) } // Not entirely convinced this will act the same as c++ but heres to hoping var header [5]byte header[1] = byte((b.Len() / 0x1) % 0x100) header[2] = byte((b.Len() / 0x100) % 0x100) header[3] = byte((b.Len() / 0x10000) % 0x100) header[4] = byte((b.Len() / 0x1000000) % 0x100) header[0] = header[1] ^ header[2] ^ header[3] ^ header[4] return header } func getVarLen(s string) [4]byte { var length [4]byte length[0] = byte((len(s) / 0x1) % 0x100) length[1] = byte((len(s) / 0x100) % 0x100) length[2] = byte((len(s) / 0x10000) % 0x100) length[3] = byte((len(s) / 0x1000000) % 0x100) return length } func getHeader(c net.Conn) int { header := make([]byte, 5) n, err := c.Read(header) if readError(err, n, 5) { log.Printf("Stopped getting messages\n%s", err) c.Close() os.Exit(1) } return int(header[1]) } type message struct { size int data *[]byte } type data struct { length [4]byte value string } func decodeLength(l [4]byte) int { length := int(l[0])*0x1 + int(l[1])*0x100 + int(l[2])*0x10000 + int(l[3])*0x1000000 return length } func translate(m <-chan message, resp chan<- map[string]data) { for msg := range m { buf := bytes.NewBuffer(*msg.data) kv := make(map[string]data) l := buf.Len() i := 0 //Read in the buffer for { if i == l { // We've reached the end break } var key bytes.Buffer for { n := buf.Next(1) i++ if n[0] != 0 { key.Write(n) } else { break } } var length [4]byte copy(length[:], buf.Next(4)) i += 4 l := decodeLength(length) value := buf.Next(l) kv[key.String()] = data{length, string(value)} i += l end := buf.Next(1) if int(end[0]) != 0 { log.Panicf("Expected null got %+v", end) } i++ } resp <- kv } } func handleConn(c net.Conn) { p4info := createRequest(NewProtocol(), NewUserInfoReq()) _, err := p4info.WriteTo(c) checkError(err) msgc := make(chan message) resp := make(chan map[string]data) go translate(msgc, resp) for { c.SetReadDeadline(time.Now().Add(time.Second)) size := getHeader(c) buf := make([]byte, size) n, err := c.Read(buf) checkError(err) if err != nil || n == 0 { c.Close() break } /*Perhaps pointless using channels/goroutine, since we are blocking for the response anyway*/ msgc <- message{size, &buf} kv := <-resp // If we get func = release we have finished reading f, ok := kv["func"] if ok && f.value == "release" { log.Println("Finished reading") break } else if ok && f.value == "client-Message" { log.Println(fmtMessage(kv)) } } } func fmtMessage(kv map[string]data) string { s := kv["fmt0"].value r, err := regexp.Compile("%([A-Za-z0-9]*)%") checkError(err) ma := r.FindAllStringSubmatch(s, -1) for _, m := range ma { if len(m) > 0 { rm, err := regexp.Compile(m[0]) checkError(err) s = rm.ReplaceAllLiteralString(s, kv[m[1]].value) } } return s } func readError(err error, size int, s_err int) bool { if err != nil || size != s_err { return true } return false } func checkError(err error) { if err != nil { fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) os.Exit(1) } } func main() { port := "8192" ips, err := net.LookupIP("localhost") checkError(err) for _, ip := range ips { fmt.Println("Resolved addr ", ip) } addr := strings.Join([]string{ips[0].String(), port}, ":") fmt.Println("Joined addr string ", addr) tcpAddr, err := net.ResolveTCPAddr("tcp", addr) checkError(err) fmt.Println("TCPAddr ", tcpAddr.String()) conn, err := net.DialTCP("tcp", nil, tcpAddr) checkError(err) handleConn(conn) }