gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/rpc/http.go (about) 1 // Copyright 2018 The aquachain Authors 2 // This file is part of the aquachain library. 3 // 4 // The aquachain library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The aquachain library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the aquachain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rpc 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "io" 24 "math/rand" 25 "mime" 26 "net" 27 "net/http" 28 "time" 29 30 "strings" 31 32 "github.com/rs/cors" 33 "gitlab.com/aquachain/aquachain/common/log" 34 "gitlab.com/aquachain/aquachain/p2p/netutil" 35 ) 36 37 const ( 38 contentType = "application/json" 39 maxHTTPRequestContentLength = 1024 * 128 40 ) 41 42 // httpReadWriteNopCloser wraps a io.Reader and io.Writer with a NOP Close method. 43 type httpReadWriteNopCloser struct { 44 io.Reader 45 io.Writer 46 } 47 48 // Close does nothing and returns always nil 49 func (t *httpReadWriteNopCloser) Close() error { 50 return nil 51 } 52 53 // NewHTTPServer creates a new HTTP RPC server around an API provider. 54 // 55 // Deprecated: Server implements http.Handler 56 func NewHTTPServer(cors []string, vhosts []string, allowIP []string, behindreverseproxy bool, srv *Server) *http.Server { 57 // Check IPs, hostname, then CORS (in that order) 58 handler := newAllowIPHandler(allowIP, behindreverseproxy, newVHostHandler(vhosts, newCorsHandler(newLoggedHandler(srv), cors))) 59 return &http.Server{Handler: handler, ReadTimeout: 2 * time.Second, WriteTimeout: 2 * time.Second, IdleTimeout: time.Second * 30} 60 } 61 62 // ServeHTTP serves JSON-RPC requests over HTTP. 63 func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 64 // Permit dumb empty requests for remote health-checks (AWS) 65 if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" { 66 return 67 } 68 if code, err := validateRequest(r); err != nil { 69 uip := getIP(r, srv.reverseproxy) 70 log.Debug("invalid request", "from", uip, "size", r.ContentLength) 71 enc := json.NewEncoder(w) 72 enc.SetIndent(" ", " ") 73 w.WriteHeader(code) 74 enc.Encode(map[string]string{"error": err.Error()}) 75 return 76 } 77 // All checks passed, create a codec that reads direct from the request body 78 // untilEOF and writes the response to w and order the server to process a 79 // single request. 80 body := io.LimitReader(r.Body, maxHTTPRequestContentLength) 81 codec := NewJSONCodec(&httpReadWriteNopCloser{body, w}) 82 defer codec.Close() 83 84 w.Header().Set("content-type", contentType) 85 srv.ServeSingleRequest(codec, OptionMethodInvocation) 86 } 87 88 // validateRequest returns a non-zero response code and error message if the 89 // request is invalid. 90 func validateRequest(r *http.Request) (int, error) { 91 if r.Method == http.MethodPut || r.Method == http.MethodDelete { 92 return http.StatusMethodNotAllowed, errors.New("method not allowed") 93 } 94 if r.ContentLength > maxHTTPRequestContentLength { 95 err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxHTTPRequestContentLength) 96 return http.StatusRequestEntityTooLarge, err 97 } 98 mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")) 99 if r.Method != http.MethodOptions && (err != nil || mt != contentType) { 100 err := fmt.Errorf("invalid content type, only %s is supported", contentType) 101 return http.StatusUnsupportedMediaType, err 102 } 103 return 0, nil 104 } 105 106 func newLoggedHandler(srv *Server) http.Handler { 107 return loggedHandler{srv, srv.reverseproxy} 108 } 109 110 func (l loggedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 111 reqid := fmt.Sprintf("%02X", rand.Uint32()) 112 lrw := &lrwriter{ResponseWriter: w, statusCode: http.StatusOK} 113 logfn := log.Debug 114 uip := getIP(r, l.reverseproxy) 115 logfn("<<< http-rpc: "+reqid, "from", uip, "path", r.URL.Path, "ua", r.UserAgent(), "method", r.Method, "host", r.Host, "size", r.ContentLength) 116 117 l.h.ServeHTTP(lrw, r) 118 if lrw.statusCode != 200 { 119 logfn = log.Warn 120 } 121 logfn(">>> http-rpc: "+reqid, "code", lrw.statusCode, "status", http.StatusText(lrw.statusCode)) 122 } 123 124 // override WriteHeader, just saving response code 125 func (lrw *lrwriter) WriteHeader(code int) { 126 lrw.statusCode = code 127 lrw.ResponseWriter.WriteHeader(code) 128 } 129 130 type loggedHandler struct { 131 h http.Handler 132 reverseproxy bool 133 } 134 type lrwriter struct { 135 http.ResponseWriter 136 statusCode int 137 } 138 139 func newCorsHandler(h http.Handler, allowedOrigins []string) http.Handler { 140 // disable CORS support if user has not specified a custom CORS configuration 141 if len(allowedOrigins) == 0 { 142 return h 143 } 144 c := cors.New(cors.Options{ 145 AllowedOrigins: allowedOrigins, 146 AllowedMethods: []string{http.MethodPost, http.MethodGet}, 147 MaxAge: 600, 148 AllowedHeaders: []string{"*"}, 149 }) 150 return c.Handler(h) 151 } 152 153 // virtualHostHandler is a handler which validates the Host-header of incoming requests. 154 // The virtualHostHandler can prevent DNS rebinding attacks, which do not utilize CORS-headers, 155 // since they do in-domain requests against the RPC api. Instead, we can see on the Host-header 156 // which domain was used, and validate that against a whitelist. 157 type virtualHostHandler struct { 158 vhosts map[string]struct{} 159 next http.Handler 160 } 161 162 // ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler 163 func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 164 // if r.Host is not set, we can continue serving since a browser would set the Host header 165 if r.Host == "" { 166 h.next.ServeHTTP(w, r) 167 return 168 } 169 host, _, err := net.SplitHostPort(r.Host) 170 if err != nil { 171 // Either invalid (too many colons) or no port specified 172 host = r.Host 173 } 174 if ipAddr := net.ParseIP(host); ipAddr != nil { 175 // It's an IP address, we can serve that 176 h.next.ServeHTTP(w, r) 177 return 178 179 } 180 // Not an ip address, but a hostname. Need to validate 181 if _, exist := h.vhosts["*"]; exist { 182 h.next.ServeHTTP(w, r) 183 return 184 } 185 if _, exist := h.vhosts[host]; exist { 186 h.next.ServeHTTP(w, r) 187 return 188 } 189 http.Error(w, "invalid host specified", http.StatusForbidden) 190 } 191 192 func newVHostHandler(vhosts []string, next http.Handler) http.Handler { 193 vhostMap := make(map[string]struct{}) 194 for _, allowedHost := range vhosts { 195 vhostMap[strings.ToLower(allowedHost)] = struct{}{} 196 } 197 return &virtualHostHandler{vhostMap, next} 198 } 199 200 // allowIPHandler is a handler which only allows certain IP 201 type allowIPHandler struct { 202 allowedIPs *netutil.Netlist 203 next http.Handler 204 reverseproxy bool // if behind a reverse proxy (uses X-FORWARDED-FOR header) 205 } 206 207 func getIP(r *http.Request, reverseproxy bool) net.IP { 208 if reverseproxy { 209 for _, h := range []string{"X-Forwarded-For", "X-Real-Ip"} { 210 addresses := strings.Split(r.Header.Get(h), ",") 211 // march from right to left until we get a public address 212 // that will be the address right before our proxy. 213 for i := len(addresses) - 1; i >= 0; i-- { 214 // header can contain spaces too, strip those out. 215 ip := strings.TrimSpace(addresses[i]) 216 realIP := net.ParseIP(ip) 217 if realIP == nil { 218 continue 219 } 220 if !realIP.IsGlobalUnicast() || netutil.IsLAN(realIP) || netutil.IsSpecialNetwork(realIP) { 221 // bad address, go to next 222 continue 223 } 224 225 return net.ParseIP(ip) 226 } 227 } 228 } 229 remoteAddr, _, err := net.SplitHostPort(r.RemoteAddr) 230 if err != nil { 231 // Either invalid (too many colons) or no port specified 232 remoteAddr = strings.Split(r.RemoteAddr, ":")[0] 233 } 234 return net.ParseIP(remoteAddr) 235 } 236 237 // ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler 238 func (h *allowIPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 239 ip := getIP(r, h.reverseproxy) 240 log.Trace("allowip: checking vs allow IPs", "ip", ip) 241 if h.allowedIPs.Contains(ip) { 242 h.next.ServeHTTP(w, r) 243 return 244 } 245 log.Warn("allowip: blocking http rpc connection", "OffendingIP", ip, "User-Agent", r.UserAgent()) 246 http.Error(w, "", http.StatusForbidden) 247 } 248 249 func newAllowIPHandler(allowIPmasks []string, behindreverseproxy bool, next http.Handler) http.Handler { 250 var allowIPMap = new(netutil.Netlist) 251 for i := range allowIPmasks { 252 allowIPMap.Add(allowIPmasks[i]) 253 } 254 return &allowIPHandler{allowIPMap, next, behindreverseproxy} 255 }