* Vendor: update gitea.com/macaron/session to a177a270 * make vendor * Vendor: update gitea.com/macaron/macaron to 0db5d458 * make vendor * Vendor: update gitea.com/macaron/cache to 905232fb * make vendor * Vendor: update gitea.com/macaron/i18n to 4ca3dd0c * make vendor * Vendor: update gitea.com/macaron/gzip to efa5e847 * make vendor * Vendor: update gitea.com/macaron/captcha to e8597820 * make vendor
		
			
				
	
	
		
			107 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			107 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
package couchbase
 | 
						|
 | 
						|
/*
 | 
						|
 | 
						|
The goal here is to map a hostname:port combination to another hostname:port
 | 
						|
combination. The original hostname:port gives the name and regular KV port
 | 
						|
of a couchbase server. We want to determine the corresponding SSL KV port.
 | 
						|
 | 
						|
To do this, we have a pool services structure, as obtained from
 | 
						|
the /pools/default/nodeServices API.
 | 
						|
 | 
						|
For a fully configured two-node system, the structure may look like this:
 | 
						|
{"rev":32,"nodesExt":[
 | 
						|
	{"services":{"mgmt":8091,"mgmtSSL":18091,"fts":8094,"ftsSSL":18094,"indexAdmin":9100,"indexScan":9101,"indexHttp":9102,"indexStreamInit":9103,"indexStreamCatchup":9104,"indexStreamMaint":9105,"indexHttps":19102,"capiSSL":18092,"capi":8092,"kvSSL":11207,"projector":9999,"kv":11210,"moxi":11211},"hostname":"172.23.123.101"},
 | 
						|
	{"services":{"mgmt":8091,"mgmtSSL":18091,"indexAdmin":9100,"indexScan":9101,"indexHttp":9102,"indexStreamInit":9103,"indexStreamCatchup":9104,"indexStreamMaint":9105,"indexHttps":19102,"capiSSL":18092,"capi":8092,"kvSSL":11207,"projector":9999,"kv":11210,"moxi":11211,"n1ql":8093,"n1qlSSL":18093},"thisNode":true,"hostname":"172.23.123.102"}]}
 | 
						|
 | 
						|
In this case, note the "hostname" fields, and the "kv" and "kvSSL" fields.
 | 
						|
 | 
						|
For a single-node system, perhaps brought up for testing, the structure may look like this:
 | 
						|
{"rev":66,"nodesExt":[
 | 
						|
	{"services":{"mgmt":8091,"mgmtSSL":18091,"indexAdmin":9100,"indexScan":9101,"indexHttp":9102,"indexStreamInit":9103,"indexStreamCatchup":9104,"indexStreamMaint":9105,"indexHttps":19102,"kv":11210,"kvSSL":11207,"capi":8092,"capiSSL":18092,"projector":9999,"n1ql":8093,"n1qlSSL":18093},"thisNode":true}],"clusterCapabilitiesVer":[1,0],"clusterCapabilities":{"n1ql":["enhancedPreparedStatements"]}}
 | 
						|
 | 
						|
Here, note that there is only a single entry in the "nodeExt" array and that it does not have a "hostname" field.
 | 
						|
We will assume that either hostname fields are present, or there is only a single node.
 | 
						|
*/
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"net"
 | 
						|
	"strconv"
 | 
						|
)
 | 
						|
 | 
						|
func ParsePoolServices(jsonInput string) (*PoolServices, error) {
 | 
						|
	ps := &PoolServices{}
 | 
						|
	err := json.Unmarshal([]byte(jsonInput), ps)
 | 
						|
	return ps, err
 | 
						|
}
 | 
						|
 | 
						|
// Accepts a "host:port" string representing the KV TCP port and the pools
 | 
						|
// nodeServices payload and returns a host:port string representing the KV
 | 
						|
// TLS port on the same node as the KV TCP port.
 | 
						|
// Returns the original host:port if in case of local communication (services
 | 
						|
// on the same node as source)
 | 
						|
func MapKVtoSSL(hostport string, ps *PoolServices) (string, bool, error) {
 | 
						|
	return MapKVtoSSLExt(hostport, ps, false)
 | 
						|
}
 | 
						|
 | 
						|
func MapKVtoSSLExt(hostport string, ps *PoolServices, force bool) (string, bool, error) {
 | 
						|
	host, port, err := net.SplitHostPort(hostport)
 | 
						|
	if err != nil {
 | 
						|
		return "", false, fmt.Errorf("Unable to split hostport %s: %v", hostport, err)
 | 
						|
	}
 | 
						|
 | 
						|
	portInt, err := strconv.Atoi(port)
 | 
						|
	if err != nil {
 | 
						|
		return "", false, fmt.Errorf("Unable to parse host/port combination %s: %v", hostport, err)
 | 
						|
	}
 | 
						|
 | 
						|
	var ns *NodeServices
 | 
						|
	for i := range ps.NodesExt {
 | 
						|
		hostname := ps.NodesExt[i].Hostname
 | 
						|
		if len(hostname) != 0 && hostname != host {
 | 
						|
			/* If the hostname is the empty string, it means the node (and by extension
 | 
						|
			   the cluster) is configured on the loopback. Further, it means that the client
 | 
						|
			   should use whatever hostname it used to get the nodeServices information in
 | 
						|
			   the first place to access the cluster. Thus, when the hostname is empty in
 | 
						|
			   the nodeService entry we can assume that client will use the hostname it used
 | 
						|
			   to access the KV TCP endpoint - and thus that it automatically "matches".
 | 
						|
			   If hostname is not empty and doesn't match then we move to the next entry.
 | 
						|
			*/
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		kvPort, found := ps.NodesExt[i].Services["kv"]
 | 
						|
		if !found {
 | 
						|
			/* not a node with a KV service  */
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if kvPort == portInt {
 | 
						|
			ns = &(ps.NodesExt[i])
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if ns == nil {
 | 
						|
		return "", false, fmt.Errorf("Unable to parse host/port combination %s: no matching node found among %d", hostport, len(ps.NodesExt))
 | 
						|
	}
 | 
						|
	kvSSL, found := ns.Services["kvSSL"]
 | 
						|
	if !found {
 | 
						|
		return "", false, fmt.Errorf("Unable to map host/port combination %s: target host has no kvSSL port listed", hostport)
 | 
						|
	}
 | 
						|
 | 
						|
	//Don't encrypt for communication between local nodes
 | 
						|
	if !force && (len(ns.Hostname) == 0 || ns.ThisNode) {
 | 
						|
		return hostport, false, nil
 | 
						|
	}
 | 
						|
 | 
						|
	ip := net.ParseIP(host)
 | 
						|
	if ip != nil && ip.To4() == nil && ip.To16() != nil { // IPv6 and not a FQDN
 | 
						|
		// Prefix and suffix square brackets as SplitHostPort removes them,
 | 
						|
		// see: https://golang.org/pkg/net/#SplitHostPort
 | 
						|
		host = "[" + host + "]"
 | 
						|
	}
 | 
						|
 | 
						|
	return fmt.Sprintf("%s:%d", host, kvSSL), true, nil
 | 
						|
}
 |