316 lines
8.6 KiB
Go
316 lines
8.6 KiB
Go
package reseed
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/tls"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/cretz/bine/tor"
|
|
"github.com/eyedeekay/sam3"
|
|
"github.com/eyedeekay/sam3/i2pkeys"
|
|
"github.com/gorilla/handlers"
|
|
"github.com/justinas/alice"
|
|
"github.com/libp2p/go-libp2p-core/host"
|
|
gostream "github.com/libp2p/go-libp2p-gostream"
|
|
p2phttp "github.com/libp2p/go-libp2p-http"
|
|
"github.com/throttled/throttled"
|
|
"github.com/throttled/throttled/store"
|
|
)
|
|
|
|
const (
|
|
i2pUserAgent = "Wget/1.11.4"
|
|
)
|
|
|
|
type Server struct {
|
|
*http.Server
|
|
I2P *sam3.SAM
|
|
I2PSession *sam3.StreamSession
|
|
I2PListener *sam3.StreamListener
|
|
I2PKeys i2pkeys.I2PKeys
|
|
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) ListenAndServeOnionTLS(startConf *tor.StartConf, listenConf *tor.ListenConf, certFile, keyFile string) error {
|
|
log.Println("Starting and registering OnionV3 HTTPS 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
|
|
}
|
|
srv.Addr = srv.OnionListener.ID
|
|
if srv.TLSConfig == nil {
|
|
srv.TLSConfig = &tls.Config{
|
|
ServerName: srv.OnionListener.ID,
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
log.Printf("Onionv3 server started on https://%v.onion\n", srv.OnionListener.ID)
|
|
|
|
// tlsListener := tls.NewListener(newBlacklistListener(srv.OnionListener, srv.Blacklist), srv.TLSConfig)
|
|
tlsListener := tls.NewListener(srv.OnionListener, srv.TLSConfig)
|
|
|
|
return srv.Serve(tlsListener)
|
|
}
|
|
|
|
func (srv *Server) ListenAndServeOnion(startConf *tor.StartConf, listenConf *tor.ListenConf) error {
|
|
log.Println("Starting and registering OnionV3 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
|
|
}
|
|
|
|
log.Printf("Onionv3 server started on http://%v.onion\n", srv.OnionListener.ID)
|
|
return srv.Serve(srv.OnionListener)
|
|
}
|
|
|
|
func (srv *Server) ListenAndServeI2PTLS(samaddr string, I2PKeys i2pkeys.I2PKeys, certFile, keyFile string) error {
|
|
log.Println("Starting and registering I2P service, please wait a couple of minutes...")
|
|
var err error
|
|
srv.I2P, err = sam3.NewSAM(samaddr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
srv.I2PSession, err = srv.I2P.NewStreamSession("", I2PKeys, []string{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
srv.I2PListener, err = srv.I2PSession.Listen()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
srv.Addr = srv.I2PListener.Addr().(i2pkeys.I2PAddr).Base32()
|
|
if srv.TLSConfig == nil {
|
|
srv.TLSConfig = &tls.Config{
|
|
ServerName: srv.I2PListener.Addr().(i2pkeys.I2PAddr).Base32(),
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
log.Printf("I2P server started on https://%v\n", srv.I2PListener.Addr().(i2pkeys.I2PAddr).Base32())
|
|
|
|
// tlsListener := tls.NewListener(newBlacklistListener(srv.OnionListener, srv.Blacklist), srv.TLSConfig)
|
|
tlsListener := tls.NewListener(srv.I2PListener, srv.TLSConfig)
|
|
|
|
return srv.Serve(tlsListener)
|
|
}
|
|
|
|
func (srv *Server) ListenAndServeI2P(samaddr string, I2PKeys i2pkeys.I2PKeys) error {
|
|
log.Println("Starting and registering I2P service, please wait a couple of minutes...")
|
|
var err error
|
|
srv.I2P, err = sam3.NewSAM(samaddr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
srv.I2PSession, err = srv.I2P.NewStreamSession("", I2PKeys, []string{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
srv.I2PListener, err = srv.I2PSession.Listen()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Printf("I2P server started on http://%v.onion\n", srv.OnionListener.ID)
|
|
return srv.Serve(srv.I2PListener)
|
|
}
|
|
|
|
// ListenAndServeLibP2P is used to serve the reseed server over libp2p http connections
|
|
func (srv *Server) ListenAndServeLibP2P(hst host.Host) error {
|
|
listener, err := gostream.Listen(hst, p2phttp.DefaultP2PProtocol)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return srv.Serve(listener)
|
|
}
|
|
|
|
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)
|
|
}
|