Files
reseed-tools/reseed/server.go
2019-06-21 01:16:27 -04:00

196 lines
5.0 KiB
Go

package reseed
import (
"bytes"
"context"
"crypto/tls"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"strconv"
"time"
"github.com/cretz/bine/tor"
"github.com/cretz/bine/torutil/ed25519"
"github.com/gorilla/handlers"
"github.com/justinas/alice"
"gopkg.in/throttled/throttled.v2"
"gopkg.in/throttled/throttled.v2/store"
)
const (
i2pUserAgent = "Wget/1.11.4"
)
type Server struct {
*http.Server
Reseeder Reseeder
Blacklist *Blacklist
OnionListener *tor.OnionService
}
func NewServer(prefix string, trustProxy bool) *Server {
config := &tls.Config{
MinVersion: tls.VersionTLS10,
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
},
CurvePreferences: []tls.CurveID{tls.CurveP384, tls.CurveP521}, // default CurveP256 removed
}
h := &http.Server{TLSConfig: config}
server := Server{Server: h, Reseeder: nil}
th := throttled.RateLimit(throttled.PerHour(4), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(200000))
middlewareChain := alice.New()
if trustProxy {
middlewareChain = middlewareChain.Append(proxiedMiddleware)
}
errorHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
if _, err := w.Write(nil); nil != err {
log.Println(err)
}
})
mux := http.NewServeMux()
mux.Handle("/", middlewareChain.Append(disableKeepAliveMiddleware, loggingMiddleware).Then(errorHandler))
mux.Handle(prefix+"/i2pseeds.su3", middlewareChain.Append(disableKeepAliveMiddleware, loggingMiddleware, verifyMiddleware, th.Throttle).Then(http.HandlerFunc(server.reseedHandler)))
server.Handler = mux
return &server
}
func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(newBlacklistListener(ln, srv.Blacklist))
}
func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
addr := srv.Addr
if addr == "" {
addr = ":https"
}
if srv.TLSConfig == nil {
srv.TLSConfig = &tls.Config{}
}
if srv.TLSConfig.NextProtos == nil {
srv.TLSConfig.NextProtos = []string{"http/1.1"}
}
var err error
srv.TLSConfig.Certificates = make([]tls.Certificate, 1)
srv.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return err
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
tlsListener := tls.NewListener(newBlacklistListener(ln, srv.Blacklist), srv.TLSConfig)
return srv.Serve(tlsListener)
}
func (srv *Server) ListenAndServeOnion(startConf *tor.StartConf, listenConf *tor.ListenConf, onionKey string) error {
log.Println("Starting and registering onion service, please wait a couple of minutes...")
tor, err := tor.Start(nil, startConf)
if err != nil {
return err
}
defer tor.Close()
listenCtx, listenCancel := context.WithTimeout(context.Background(), 3*time.Minute)
defer listenCancel()
srv.OnionListener, err = tor.Listen(listenCtx, listenConf)
if err != nil {
return err
}
err = ioutil.WriteFile(onionKey, []byte(srv.OnionListener.Key.(ed25519.KeyPair).PrivateKey()), 0644)
if err != nil {
return err
}
log.Printf("Onionv3 server started on http://%v.onion\n", srv.OnionListener.ID)
return srv.Serve(srv.OnionListener)
}
func (srv *Server) reseedHandler(w http.ResponseWriter, r *http.Request) {
var peer Peer
if ip, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
peer = Peer(ip)
} else {
peer = Peer(r.RemoteAddr)
}
su3Bytes, err := srv.Reseeder.PeerSu3Bytes(peer)
if nil != err {
http.Error(w, "500 Unable to serve su3", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Disposition", "attachment; filename=i2pseeds.su3")
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Length", strconv.FormatInt(int64(len(su3Bytes)), 10))
io.Copy(w, bytes.NewReader(su3Bytes))
}
func disableKeepAliveMiddleware(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Connection", "close")
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
func loggingMiddleware(next http.Handler) http.Handler {
return handlers.CombinedLoggingHandler(os.Stdout, next)
}
func verifyMiddleware(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
if i2pUserAgent != r.UserAgent() {
http.Error(w, "403 Forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
func proxiedMiddleware(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
if prior, ok := r.Header["X-Forwarded-For"]; ok {
r.RemoteAddr = prior[0]
}
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}