github.com/ergo-services/ergo@v1.999.224/proto/dist/registrar.go (about) 1 package dist 2 3 import ( 4 "context" 5 "crypto/tls" 6 "encoding/binary" 7 "fmt" 8 "io" 9 "net" 10 "strconv" 11 "strings" 12 "sync/atomic" 13 "time" 14 15 "github.com/ergo-services/ergo/etf" 16 "github.com/ergo-services/ergo/lib" 17 "github.com/ergo-services/ergo/node" 18 ) 19 20 const ( 21 DefaultEPMDPort uint16 = 4369 22 23 epmdAliveReq = 120 24 epmdAliveResp = 121 25 epmdAliveRespX = 118 26 epmdPortPleaseReq = 122 27 epmdPortResp = 119 28 epmdNamesReq = 110 29 30 // wont be implemented 31 // epmdDumpReq = 100 32 // epmdKillReq = 107 33 // epmdStopReq = 115 34 35 // Extra data 36 ergoExtraMagic = 4411 37 ergoExtraVersion1 = 1 38 ) 39 40 // epmd implements registrar interface 41 type epmdRegistrar struct { 42 // EPMD server 43 enableEPMD bool 44 host string 45 port uint16 46 47 // Node 48 name string 49 nodePort uint16 50 nodeName string 51 nodeHost string 52 handshakeVersion node.HandshakeVersion 53 54 running int32 55 extra []byte 56 } 57 58 func CreateRegistrar() node.Registrar { 59 registrar := &epmdRegistrar{ 60 port: DefaultEPMDPort, 61 } 62 return registrar 63 } 64 65 func CreateRegistrarWithLocalEPMD(host string, port uint16) node.Registrar { 66 if port == 0 { 67 port = DefaultEPMDPort 68 } 69 registrar := &epmdRegistrar{ 70 enableEPMD: true, 71 host: host, 72 port: port, 73 } 74 return registrar 75 } 76 77 func CreateRegistrarWithRemoteEPMD(host string, port uint16) node.Registrar { 78 if port == 0 { 79 port = DefaultEPMDPort 80 } 81 registrar := &epmdRegistrar{ 82 host: host, 83 port: port, 84 } 85 return registrar 86 } 87 88 func (e *epmdRegistrar) Register(ctx context.Context, name string, options node.RegisterOptions) error { 89 if atomic.CompareAndSwapInt32(&e.running, 0, 1) == false { 90 return fmt.Errorf("registrar is already running") 91 } 92 93 n := strings.Split(name, "@") 94 if len(n) != 2 { 95 return fmt.Errorf("(EMPD) FQDN for node name is required (example: node@hostname)") 96 } 97 98 e.name = name 99 e.nodeName = n[0] 100 e.nodeHost = n[1] 101 e.nodePort = options.Port 102 e.handshakeVersion = options.HandshakeVersion 103 104 e.composeExtra(options) 105 ready := make(chan error) 106 107 go func() { 108 defer atomic.StoreInt32(&e.running, 0) 109 buf := make([]byte, 16) 110 var reconnecting bool 111 112 for { 113 if ctx.Err() != nil { 114 // node is stopped 115 return 116 } 117 118 // try to start embedded EPMD server 119 if e.enableEPMD { 120 startServerEPMD(ctx, e.host, e.port) 121 } 122 123 // register this node on EPMD server 124 conn, err := e.registerNode(options) 125 if err != nil { 126 if reconnecting == false { 127 ready <- err 128 break 129 } 130 lib.Warning("EPMD client: can't register node %q (%s). Retry in 3 seconds...", name, err) 131 time.Sleep(3 * time.Second) 132 continue 133 } 134 135 go func() { 136 <-ctx.Done() 137 conn.Close() 138 }() 139 140 if reconnecting == false { 141 ready <- nil 142 reconnecting = true 143 } 144 145 for { 146 _, err := conn.Read(buf) 147 if err == nil { 148 continue 149 } 150 break 151 } 152 lib.Log("[%s] EPMD client: closing connection", name) 153 } 154 }() 155 156 defer close(ready) 157 return <-ready 158 } 159 160 func (e *epmdRegistrar) Resolve(name string) (node.Route, error) { 161 var route node.Route 162 163 n := strings.Split(name, "@") 164 if len(n) != 2 { 165 return node.Route{}, fmt.Errorf("incorrect FQDN node name (example: node@localhost)") 166 } 167 conn, err := net.Dial("tcp", net.JoinHostPort(n[1], fmt.Sprintf("%d", e.port))) 168 if err != nil { 169 return node.Route{}, err 170 } 171 172 defer conn.Close() 173 174 if err := e.sendPortPleaseReq(conn, n[0]); err != nil { 175 return node.Route{}, err 176 } 177 178 route.Node = n[0] 179 route.Host = n[1] 180 181 err = e.readPortResp(&route, conn) 182 if err != nil { 183 return node.Route{}, err 184 } 185 186 return route, nil 187 } 188 189 func (e *epmdRegistrar) ResolveProxy(name string) (node.ProxyRoute, error) { 190 var route node.ProxyRoute 191 return route, lib.ErrProxyNoRoute 192 } 193 func (e *epmdRegistrar) RegisterProxy(name string, maxhop int, flags node.ProxyFlags) error { 194 return lib.ErrUnsupported 195 } 196 func (e *epmdRegistrar) UnregisterProxy(name string) error { 197 return lib.ErrUnsupported 198 } 199 func (e *epmdRegistrar) Config() (node.RegistrarConfig, error) { 200 return node.RegistrarConfig{}, lib.ErrUnsupported 201 } 202 func (e *epmdRegistrar) ConfigItem(name string) (etf.Term, error) { 203 return nil, lib.ErrUnsupported 204 } 205 206 // just stub 207 func (e *epmdRegistrar) SetConfigUpdateCallback(func(string, etf.Term) error) error { 208 return lib.ErrUnsupported 209 } 210 211 func (e *epmdRegistrar) composeExtra(options node.RegisterOptions) { 212 buf := make([]byte, 4) 213 214 // 2 bytes: ergoExtraMagic 215 binary.BigEndian.PutUint16(buf[0:2], uint16(ergoExtraMagic)) 216 // 1 byte Extra version 217 buf[2] = ergoExtraVersion1 218 // 1 byte flag enabled TLS 219 if options.EnableTLS { 220 buf[3] = 1 221 } 222 e.extra = buf 223 return 224 } 225 226 func (e *epmdRegistrar) readExtra(route *node.Route, buf []byte) { 227 if len(buf) < 4 { 228 return 229 } 230 magic := binary.BigEndian.Uint16(buf[0:2]) 231 if uint16(ergoExtraMagic) != magic { 232 return 233 } 234 235 if buf[2] != ergoExtraVersion1 { 236 return 237 } 238 239 if buf[3] == 1 { 240 route.Options.TLS = &tls.Config{} 241 } 242 243 route.Options.IsErgo = true 244 return 245 } 246 247 func (e *epmdRegistrar) registerNode(options node.RegisterOptions) (net.Conn, error) { 248 // 249 registrarHost := e.host 250 if registrarHost == "" { 251 registrarHost = e.nodeHost 252 } 253 dialer := net.Dialer{ 254 KeepAlive: 15 * time.Second, 255 } 256 dsn := net.JoinHostPort(registrarHost, strconv.Itoa(int(e.port))) 257 conn, err := dialer.Dial("tcp", dsn) 258 if err != nil { 259 return nil, err 260 } 261 262 if err := e.sendAliveReq(conn); err != nil { 263 conn.Close() 264 return nil, err 265 } 266 267 if err := e.readAliveResp(conn); err != nil { 268 conn.Close() 269 return nil, err 270 } 271 272 lib.Log("[%s] EPMD client: node registered", e.name) 273 return conn, nil 274 } 275 276 func (e *epmdRegistrar) sendAliveReq(conn net.Conn) error { 277 buf := make([]byte, 2+14+len(e.nodeName)+len(e.extra)) 278 binary.BigEndian.PutUint16(buf[0:2], uint16(len(buf)-2)) 279 buf[2] = byte(epmdAliveReq) 280 binary.BigEndian.PutUint16(buf[3:5], e.nodePort) 281 // http://erlang.org/doc/reference_manual/distributed.html (section 13.5) 282 // 77 — regular public node, 72 — hidden 283 // We use a regular one 284 buf[5] = 77 285 // Protocol TCP 286 buf[6] = 0 287 // HighestVersion 288 binary.BigEndian.PutUint16(buf[7:9], uint16(HandshakeVersion6)) 289 // LowestVersion 290 binary.BigEndian.PutUint16(buf[9:11], uint16(HandshakeVersion5)) 291 // length Node name 292 l := len(e.nodeName) 293 binary.BigEndian.PutUint16(buf[11:13], uint16(l)) 294 // Node name 295 offset := (13 + l) 296 copy(buf[13:offset], e.nodeName) 297 // Extra data 298 l = len(e.extra) 299 binary.BigEndian.PutUint16(buf[offset:offset+2], uint16(l)) 300 copy(buf[offset+2:offset+2+l], e.extra) 301 // Send 302 if _, err := conn.Write(buf); err != nil { 303 return err 304 } 305 return nil 306 } 307 308 func (e *epmdRegistrar) readAliveResp(conn net.Conn) error { 309 buf := make([]byte, 16) 310 if _, err := conn.Read(buf); err != nil { 311 return err 312 } 313 switch buf[0] { 314 case epmdAliveResp, epmdAliveRespX: 315 default: 316 return fmt.Errorf("malformed EPMD response %v", buf) 317 } 318 if buf[1] != 0 { 319 if buf[1] == 1 { 320 return fmt.Errorf("can not register node with %q, name is taken", e.nodeName) 321 } 322 return fmt.Errorf("can not register %q, code: %v", e.nodeName, buf[1]) 323 } 324 return nil 325 } 326 327 func (e *epmdRegistrar) sendPortPleaseReq(conn net.Conn, name string) error { 328 buflen := uint16(2 + len(name) + 1) 329 buf := make([]byte, buflen) 330 binary.BigEndian.PutUint16(buf[0:2], uint16(len(buf)-2)) 331 buf[2] = byte(epmdPortPleaseReq) 332 copy(buf[3:buflen], name) 333 _, err := conn.Write(buf) 334 return err 335 } 336 337 func (e *epmdRegistrar) readPortResp(route *node.Route, c net.Conn) error { 338 339 buf := make([]byte, 1024) 340 n, err := c.Read(buf) 341 if err != nil && err != io.EOF { 342 return fmt.Errorf("reading from link - %s", err) 343 } 344 buf = buf[:n] 345 346 if buf[0] == epmdPortResp && buf[1] == 0 { 347 p := binary.BigEndian.Uint16(buf[2:4]) 348 nameLen := binary.BigEndian.Uint16(buf[10:12]) 349 route.Port = p 350 extraStart := 12 + int(nameLen) 351 // read extra data 352 buf = buf[extraStart:] 353 extraLen := binary.BigEndian.Uint16(buf[:2]) 354 buf = buf[2 : extraLen+2] 355 e.readExtra(route, buf) 356 return nil 357 } else if buf[1] > 0 { 358 return fmt.Errorf("desired node not found") 359 } else { 360 return fmt.Errorf("malformed reply - %#v", buf) 361 } 362 }