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