1// SPDX-License-Identifier: ISC
2// Copyright (c) 2014-2020 Bitmark Inc.
3// Use of this source code is governed by an ISC
4// license that can be found in the LICENSE file.
5
6package listeners
7
8import (
9	"crypto/tls"
10	"net"
11	"net/http"
12	"strings"
13	"time"
14
15	"github.com/bitmark-inc/bitmarkd/fault"
16	"github.com/bitmark-inc/bitmarkd/rpc/handler"
17	"github.com/bitmark-inc/logger"
18)
19
20const (
21	httpsLogName       = "http_rpc"
22	minConnectionCount = 1
23	readWriteTimeout   = 10 * time.Second
24)
25
26// HTTPSConfiguration - configuration file data for HTTPS setup
27type HTTPSConfiguration struct {
28	MaximumConnections uint64              `gluamapper:"maximum_connections" json:"maximum_connections"`
29	Listen             []string            `gluamapper:"listen" json:"listen"`
30	Certificate        string              `gluamapper:"certificate" json:"certificate"`
31	PrivateKey         string              `gluamapper:"private_key" json:"private_key"`
32	Allow              map[string][]string `gluamapper:"allow" json:"allow"`
33}
34
35type httpsListener struct {
36	log             *logger.L
37	listenIPAndPort []string
38	tlsConfig       *tls.Config
39	mux             *http.ServeMux
40}
41
42func (h httpsListener) Serve() error {
43	for _, listen := range h.listenIPAndPort {
44		h.log.Infof("starting server: %s on: %q", httpsLogName, listen)
45		if '*' == listen[0] {
46			// change "*:PORT" to "[::]:PORT"
47			// on the assumption that this will listen on tcp4 and tcp6
48			listen = "[::]" + ":" + strings.Split(listen, ":")[1]
49		}
50
51		go doServeHTTPS(listen, h.mux, h.tlsConfig)
52	}
53
54	return nil
55}
56
57type tcpKeepAliveListener struct {
58	*net.TCPListener
59}
60
61func doServeHTTPS(addr string, handler http.Handler, cfg *tls.Config) {
62	s := &http.Server{
63		Addr:           addr,
64		Handler:        handler,
65		ReadTimeout:    readWriteTimeout,
66		WriteTimeout:   readWriteTimeout,
67		MaxHeaderBytes: 1 << 20,
68	}
69
70	cfg.NextProtos = []string{"http/1.1"}
71
72	ln, err := net.Listen("tcp", addr)
73	if err != nil {
74		return
75	}
76
77	tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, cfg)
78
79	_ = s.Serve(tlsListener)
80}
81
82func NewHTTPS(
83	configuration *HTTPSConfiguration,
84	log *logger.L,
85	tlsConfig *tls.Config,
86	hdlr handler.Handler,
87) (Listener, error) {
88	if 0 == len(configuration.Listen) {
89		log.Infof("disable: %s", httpsLogName)
90		return nil, nil
91	}
92
93	if configuration.MaximumConnections < minConnectionCount {
94		log.Errorf("invalid %s maximum connection limit: %d", httpsLogName, configuration.MaximumConnections)
95		return nil, fault.MissingParameters
96	}
97
98	h := httpsListener{
99		log:             log,
100		listenIPAndPort: configuration.Listen,
101		tlsConfig:       tlsConfig,
102	}
103
104	// create access control and format strings to match http.Request.RemoteAddr
105	local := make(map[string][]*net.IPNet)
106	for path, addresses := range configuration.Allow {
107		set := make([]*net.IPNet, len(addresses))
108		local[path] = set
109		for i, ip := range addresses {
110			_, cidr, err := net.ParseCIDR(strings.Trim(ip, " "))
111			if nil != err {
112				return nil, err
113			}
114			set[i] = cidr
115		}
116	}
117
118	hdlr.SetAllow(local)
119
120	h.mux = http.NewServeMux()
121	h.mux.HandleFunc("/bitmarkd/rpc", hdlr.RPC)
122	h.mux.HandleFunc("/bitmarkd/details", hdlr.Details)
123	h.mux.HandleFunc("/bitmarkd/connections", hdlr.Connections)
124	h.mux.HandleFunc("/bitmarkd/peers", hdlr.Peers)
125	h.mux.HandleFunc("/", hdlr.Root)
126
127	return &h, nil
128}
129