github.com/ergo-services/ergo@v1.999.224/proto/dist/epmd.go (about) 1 package dist 2 3 import ( 4 "context" 5 "encoding/binary" 6 "fmt" 7 "net" 8 "strconv" 9 "strings" 10 "sync" 11 "time" 12 13 "github.com/ergo-services/ergo/lib" 14 ) 15 16 type registeredNode struct { 17 port uint16 18 hidden bool 19 hi uint16 20 lo uint16 21 extra []byte 22 } 23 24 type epmd struct { 25 port uint16 26 nodes map[string]registeredNode 27 nodesMutex sync.Mutex 28 } 29 30 func startServerEPMD(ctx context.Context, host string, port uint16) error { 31 lc := net.ListenConfig{} 32 listener, err := lc.Listen(ctx, "tcp", net.JoinHostPort(host, strconv.Itoa(int(port)))) 33 if err != nil { 34 lib.Log("Can't start embedded EPMD service: %s", err) 35 return err 36 } 37 38 epmd := epmd{ 39 port: port, 40 nodes: make(map[string]registeredNode), 41 } 42 go epmd.serve(listener) 43 lib.Log("Started embedded EMPD service and listen port: %d", port) 44 45 return nil 46 } 47 48 func (e *epmd) serve(l net.Listener) { 49 for { 50 c, err := l.Accept() 51 if err != nil { 52 lib.Log("EPMD server stopped: %s", err.Error()) 53 return 54 } 55 lib.Log("EPMD accepted new connection from %s", c.RemoteAddr().String()) 56 go e.handle(c) 57 } 58 } 59 60 func (e *epmd) handle(c net.Conn) { 61 var name string 62 var node registeredNode 63 buf := make([]byte, 1024) 64 65 defer c.Close() 66 for { 67 n, err := c.Read(buf) 68 lib.Log("Request from EPMD client: %v", buf[:n]) 69 if err != nil { 70 lib.Log("EPMD unregistering node: '%s'", name) 71 e.nodesMutex.Lock() 72 delete(e.nodes, name) 73 e.nodesMutex.Unlock() 74 return 75 } 76 if len(buf) < 6 { 77 lib.Log("Too short") 78 return 79 } 80 81 buf = buf[:n] 82 // buf[0:1] - length 83 if uint16(n-2) != binary.BigEndian.Uint16(buf[0:2]) { 84 continue 85 } 86 87 switch buf[2] { 88 case epmdAliveReq: 89 name, node, err = e.readAliveReq(buf[3:]) 90 if err != nil { 91 // send error and close connection 92 e.sendAliveResp(c, 1) 93 return 94 } 95 96 // check if node with this name is already registered 97 e.nodesMutex.Lock() 98 _, exist := e.nodes[name] 99 e.nodesMutex.Unlock() 100 if exist { 101 // send error and close connection 102 e.sendAliveResp(c, 1) 103 return 104 } 105 106 // send alive response 107 if err := e.sendAliveResp(c, 0); err != nil { 108 return 109 } 110 111 // register new node 112 e.nodesMutex.Lock() 113 e.nodes[name] = node 114 e.nodesMutex.Unlock() 115 116 // enable keep alive on this connection 117 if tcp, ok := c.(*net.TCPConn); ok { 118 tcp.SetKeepAlive(true) 119 tcp.SetKeepAlivePeriod(15 * time.Second) 120 tcp.SetNoDelay(true) 121 } 122 continue 123 124 case epmdPortPleaseReq: 125 requestedName := string(buf[3:n]) 126 127 e.nodesMutex.Lock() 128 node, exist := e.nodes[requestedName] 129 e.nodesMutex.Unlock() 130 131 if exist == false { 132 lib.Log("EPMD: looking for '%s'. Not found", name) 133 c.Write([]byte{epmdPortResp, 1}) 134 return 135 } 136 e.sendPortPleaseResp(c, requestedName, node) 137 return 138 139 case epmdNamesReq: 140 e.sendNamesResp(c, buf[3:n]) 141 return 142 143 default: 144 lib.Log("unknown EPMD request") 145 return 146 } 147 148 } 149 } 150 151 func (e *epmd) readAliveReq(req []byte) (string, registeredNode, error) { 152 if len(req) < 10 { 153 return "", registeredNode{}, fmt.Errorf("Malformed EPMD request %v", req) 154 } 155 // Name length 156 l := binary.BigEndian.Uint16(req[8:10]) 157 // Name 158 name := string(req[10 : 10+l]) 159 // Hidden 160 hidden := false 161 if req[2] == 72 { 162 hidden = true 163 } 164 // node 165 node := registeredNode{ 166 port: binary.BigEndian.Uint16(req[0:2]), 167 hidden: hidden, 168 hi: binary.BigEndian.Uint16(req[4:6]), 169 lo: binary.BigEndian.Uint16(req[6:8]), 170 extra: req[10+l:], 171 } 172 173 return name, node, nil 174 } 175 176 func (e *epmd) sendAliveResp(c net.Conn, code int) error { 177 buf := make([]byte, 4) 178 buf[0] = epmdAliveResp 179 buf[1] = byte(code) 180 181 // Creation. Ergo doesn't use it. Just for Erlang nodes. 182 binary.BigEndian.PutUint16(buf[2:], uint16(1)) 183 _, err := c.Write(buf) 184 return err 185 } 186 187 func (e *epmd) sendPortPleaseResp(c net.Conn, name string, node registeredNode) { 188 buf := make([]byte, 12+len(name)+2+len(node.extra)) 189 buf[0] = epmdPortResp 190 191 // Result 0 192 buf[1] = 0 193 // Port 194 binary.BigEndian.PutUint16(buf[2:4], uint16(node.port)) 195 // Hidden 196 if node.hidden { 197 buf[4] = 72 198 } else { 199 buf[4] = 77 200 } 201 // Protocol TCP 202 buf[5] = 0 203 // Highest version 204 binary.BigEndian.PutUint16(buf[6:8], uint16(node.hi)) 205 // Lowest version 206 binary.BigEndian.PutUint16(buf[8:10], uint16(node.lo)) 207 // Name 208 binary.BigEndian.PutUint16(buf[10:12], uint16(len(name))) 209 offset := 12 + len(name) 210 copy(buf[12:offset], name) 211 // Extra 212 l := len(node.extra) 213 copy(buf[offset:offset+l], node.extra) 214 // send 215 c.Write(buf) 216 return 217 } 218 219 func (e *epmd) sendNamesResp(c net.Conn, req []byte) { 220 var str strings.Builder 221 var s string 222 var buf [4]byte 223 224 binary.BigEndian.PutUint32(buf[0:4], uint32(e.port)) 225 str.WriteString(string(buf[0:])) 226 227 e.nodesMutex.Lock() 228 for k, v := range e.nodes { 229 // io:format("name ~ts at port ~p~n", [NodeName, Port]). 230 s = fmt.Sprintf("name %s at port %d\n", k, v.port) 231 str.WriteString(s) 232 } 233 e.nodesMutex.Unlock() 234 235 c.Write([]byte(str.String())) 236 return 237 }