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  }