diff --git a/cmd/keygen.go b/cmd/keygen.go
index 8e3ff53..bf7d1d1 100644
--- a/cmd/keygen.go
+++ b/cmd/keygen.go
@@ -19,8 +19,8 @@ import (
func NewKeygenCommand() cli.Command {
return cli.Command{
Name: "keygen",
- Usage: "Generate keys for reseed Su3 signing",
- Description: "Generate keys for reseed Su3 signing",
+ Usage: "Generate keys for reseed SU3 signing",
+ Description: "Generate keys for reseed SU3 signing",
Action: keygenAction,
Flags: []cli.Flag{
cli.StringFlag{
diff --git a/cmd/reseeder.go b/cmd/reseeder.go
index 4998790..e1c2619 100644
--- a/cmd/reseeder.go
+++ b/cmd/reseeder.go
@@ -3,7 +3,6 @@ package cmd
import (
"fmt"
"log"
- "net/http"
"github.com/MDrollette/go-i2p/reseed"
"github.com/codegangsta/cli"
@@ -18,31 +17,60 @@ func NewReseedCommand() cli.Command {
Flags: []cli.Flag{
cli.StringFlag{
Name: "addr",
- Value: "127.0.0.1:8080",
+ Value: "127.0.0.1:9090",
Usage: "IP and port to listen on",
},
+ cli.StringFlag{
+ Name: "netdb",
+ Usage: "Path to NetDB directory containing routerInfos",
+ },
+ cli.StringFlag{
+ Name: "tlscert",
+ Value: "cert.pem",
+ Usage: "Path to tls certificate",
+ },
+ cli.StringFlag{
+ Name: "tlskey",
+ Value: "key.pem",
+ Usage: "Path to tls key",
+ },
+ cli.StringFlag{
+ Name: "keyfile",
+ Value: "reseed_private.pem",
+ Usage: "Path to your su3 signing private key",
+ },
},
}
}
func reseedAction(c *cli.Context) {
- log.Println("Starting server on", c.String("addr"))
+ netdbDir := c.String("netdb")
+ if netdbDir == "" {
+ fmt.Println("--netdb is required")
+ return
+ }
- netdb := reseed.NewLocalNetDb(c.Args().Get(0))
+ // load our signing privKey
+ privKey, err := loadPrivateKey(c.String("keyfile"))
+ if nil != err {
+ log.Fatalln(err)
+ }
+
+ // create a local file netdb provider
+ netdb := reseed.NewLocalNetDb(netdbDir)
+
+ // create a reseeder
reseeder := reseed.NewReseeder(netdb)
+ reseeder.SigningKey = privKey
+ reseeder.SignerId = []byte("matt@drollette.com")
- http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- peer := reseeder.Peer(r)
- seeds, err := reseeder.Seed(peer)
- if nil != err {
- fmt.Fprintf(w, "Problem: '%s'", err)
- return
- }
+ // create a server
+ server := reseed.NewServer()
+ server.Reseeder = reseeder
+ server.Addr = c.String("addr")
- for _, s := range seeds {
- fmt.Fprintf(w, "%s\n", s.Name)
- }
- })
+ // @todo generate self-signed keys if they don't exist
- http.ListenAndServe("127.0.0.1:9090", nil)
+ log.Printf("Server listening on %s\n", server.Addr)
+ server.ListenAndServeTLS(c.String("tlscert"), c.String("tlskey"))
}
diff --git a/cmd/su3.go b/cmd/su3.go
deleted file mode 100644
index 7a9cdad..0000000
--- a/cmd/su3.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package cmd
-
-import (
- "crypto/x509"
- "encoding/pem"
- "io/ioutil"
- "log"
- "net/http"
-
- "github.com/MDrollette/go-i2p/reseed"
- "github.com/codegangsta/cli"
-)
-
-func NewSu3Command() cli.Command {
- return cli.Command{
- Name: "su3",
- Usage: "Do SU3 things",
- Description: "Do SU3 things",
- Action: su3Action,
- Flags: []cli.Flag{},
- }
-}
-
-func su3Action(c *cli.Context) {
- netdb := reseed.NewLocalNetDb(c.Args().Get(0))
- reseeder := reseed.NewReseeder(netdb)
-
- // make a fake request to get a peer
- r, _ := http.NewRequest("GET", "/i2pseeds.su3", nil)
-
- peer := reseeder.Peer(r)
- seeds, err := reseeder.Seed(peer)
- if nil != err {
- log.Fatalln(err)
- return
- }
-
- // load our signing privKey
- privPem, err := ioutil.ReadFile("reseed_private.pem")
- if nil != err {
- log.Fatalln(err)
- return
- }
- privDer, _ := pem.Decode(privPem)
- privKey, err := x509.ParsePKCS1PrivateKey(privDer.Bytes)
- if nil != err {
- log.Fatalln(err)
- return
- }
-
- // create an SU3 from the seed
- su3File, err := reseeder.CreateSu3(seeds)
-
- // sign the su3 with our key
- su3File.SignerId = []byte("matt@drollette.com")
- su3File.Sign(privKey)
-
- //write the file to disk
- ioutil.WriteFile("i2pseeds.su3", su3File.Bytes(), 0777)
-}
diff --git a/reseed/legacy.go b/reseed/legacy.go
index f8fa87e..a633308 100644
--- a/reseed/legacy.go
+++ b/reseed/legacy.go
@@ -22,21 +22,6 @@ const (
LIST_TEMPLATE = `
NetDB`
)
-func proxiedHandler(h http.Handler) http.Handler {
- return remoteAddrFixup{h}
-}
-
-type remoteAddrFixup struct {
- h http.Handler
-}
-
-func (h remoteAddrFixup) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if prior, ok := r.Header["X-Forwarded-For"]; ok {
- r.RemoteAddr = prior[0]
- }
- h.h.ServeHTTP(w, r)
-}
-
type Config struct {
NetDBDir string
RefreshInterval time.Duration
@@ -67,10 +52,6 @@ func Run(config *Config) {
th := throttled.RateLimit(throttled.PerMin(config.RateLimit), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(1000))
muxWithMiddlewares := th.Throttle(r)
- if config.Proxy {
- muxWithMiddlewares = proxiedHandler(muxWithMiddlewares)
- }
-
if config.Verbose {
muxWithMiddlewares = handlers.CombinedLoggingHandler(os.Stdout, muxWithMiddlewares)
}
diff --git a/reseed/server.go b/reseed/server.go
index e5f180b..d1c20a0 100644
--- a/reseed/server.go
+++ b/reseed/server.go
@@ -1,128 +1,89 @@
package reseed
import (
- "io/ioutil"
- "log"
- "math/rand"
+ "bytes"
+ "crypto/tls"
+ "fmt"
+ "io"
"net/http"
"os"
- "path/filepath"
- "regexp"
- "sync"
- "github.com/MDrollette/go-i2p/su3"
+ "github.com/PuerkitoBio/throttled"
+ "github.com/PuerkitoBio/throttled/store"
+ "github.com/gorilla/handlers"
+ "github.com/justinas/alice"
)
-type routerInfo struct {
- Name string
- Data []byte
+const (
+ I2P_USER_AGENT = "Wget/1.11.4"
+)
+
+type Server struct {
+ *http.Server
+ Reseeder Reseeder
}
-type Peer string
-type Seed []routerInfo
+func NewServer() *Server {
+ config := &tls.Config{MinVersion: tls.VersionTLS10}
+ h := &http.Server{TLSConfig: config}
+ server := Server{h, nil}
-type Reseeder interface {
- // seed a peer with routerinfos
- Seed(p Peer) (Seed, error)
- // get a peer from a given request
- Peer(r *http.Request) Peer
- // create an Su3 file from the given seeds
- CreateSu3(seeds Seed) (*su3.Su3File, error)
+ th := throttled.RateLimit(throttled.PerHour(4), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(10000))
+
+ middlewareChain := alice.New(proxiedMiddleware, loggingMiddleware, verifyMiddleware, th.Throttle)
+
+ mux := http.NewServeMux()
+ mux.Handle("/i2pseeds.su3", middlewareChain.Then(http.HandlerFunc(server.reseedHandler)))
+ server.Handler = mux
+
+ return &server
}
-type ReseederImpl struct {
- netdb NetDbProvider
- peers map[string]Peer
- m sync.Mutex
+func (s *Server) indexHandler(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintf(w, "Index")
}
-func NewReseeder(netdb NetDbProvider) *ReseederImpl {
- return &ReseederImpl{
- netdb: netdb,
- peers: make(map[string]Peer),
- }
-}
+func (s *Server) reseedHandler(w http.ResponseWriter, r *http.Request) {
+ peer := s.Reseeder.Peer(r)
-func (rs *ReseederImpl) Seed(p Peer) (Seed, error) {
- all, err := rs.netdb.RouterInfos()
+ seeds, err := s.Reseeder.Seeds(peer)
if nil != err {
- return nil, err
- }
-
- return Seed(all), nil
-}
-
-func (rs *ReseederImpl) Peer(r *http.Request) Peer {
- rs.m.Lock()
- defer rs.m.Unlock()
-
- if p, ok := rs.peers[r.RemoteAddr]; !ok {
- rs.peers[r.RemoteAddr] = Peer(r.RemoteAddr)
- } else {
- return p
- }
-
- return rs.peers[r.RemoteAddr]
-}
-
-func (rs *ReseederImpl) CreateSu3(seeds Seed) (*su3.Su3File, error) {
- su3File := su3.NewSu3File()
- su3File.FileType = su3.FILE_TYPE_ZIP
- su3File.ContentType = su3.CONTENT_TYPE_RESEED
-
- zipped, err := zipSeeds(seeds)
- if nil != err {
- return nil, err
- }
- su3File.Content = zipped
-
- return su3File, nil
-}
-
-type NetDbProvider interface {
- // Get all router infos
- RouterInfos() ([]routerInfo, error)
-}
-
-type LocalNetDbImpl struct {
- Path string
-}
-
-func NewLocalNetDb(path string) *LocalNetDbImpl {
- return &LocalNetDbImpl{
- Path: path,
- }
-}
-
-func (db *LocalNetDbImpl) RouterInfos() (routerInfos []routerInfo, err error) {
- var src []os.FileInfo
- if src, err = ioutil.ReadDir(db.Path); nil != err {
+ http.Error(w, "500 Unable to provide seeds", http.StatusInternalServerError)
return
}
- // randomize the file order
- files := make([]os.FileInfo, len(src))
- perm := rand.Perm(len(src))
- for i, v := range perm {
- files[v] = src[i]
+ su3, err := s.Reseeder.CreateSu3(seeds)
+ if nil != err {
+ http.Error(w, "500 Unable to generate SU3", http.StatusInternalServerError)
+ return
}
- r, _ := regexp.Compile("^routerInfo-[A-Za-z0-9-=~]+.dat$")
-
- for _, file := range files {
- if r.MatchString(file.Name()) {
- riBytes, err := ioutil.ReadFile(filepath.Join(db.Path, file.Name()))
- if nil != err {
- log.Println(err)
- continue
- }
-
- routerInfos = append(routerInfos, routerInfo{
- Name: file.Name(),
- Data: riBytes,
- })
- }
- }
-
- return
+ io.Copy(w, bytes.NewReader(su3.Bytes()))
+}
+
+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 I2P_USER_AGENT != 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)
}
diff --git a/reseed/utils.go b/reseed/utils.go
index a526bc2..8a58b75 100644
--- a/reseed/utils.go
+++ b/reseed/utils.go
@@ -6,7 +6,7 @@ import (
"io/ioutil"
)
-func zipSeeds(seeds Seed) ([]byte, error) {
+func zipSeeds(seeds Seeds) ([]byte, error) {
// Create a buffer to write our archive to.
buf := new(bytes.Buffer)
@@ -32,14 +32,14 @@ func zipSeeds(seeds Seed) ([]byte, error) {
return buf.Bytes(), nil
}
-func uzipSeeds(c []byte) (Seed, error) {
+func uzipSeeds(c []byte) (Seeds, error) {
input := bytes.NewReader(c)
zipReader, err := zip.NewReader(input, int64(len(c)))
if nil != err {
return nil, err
}
- var seeds Seed
+ var seeds Seeds
for _, f := range zipReader.File {
rc, err := f.Open()
if err != nil {