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  }