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/throttled/throttled" "github.com/throttled/throttled/store" "github.com/gorilla/handlers" "github.com/justinas/alice" ) 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, // }, MinVersion: tls.VersionTLS13, PreferServerCipherSuites: true, CipherSuites: []uint16{ tls.TLS_AES_256_GCM_SHA384, tls.TLS_CHACHA20_POLY1305_SHA256, }, 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) }