diff --git a/ci-build.sh b/ci-build.sh index a72ccde..d25983e 100755 --- a/ci-build.sh +++ b/ci-build.sh @@ -29,6 +29,7 @@ build() { fi echo "Building project" + go generate ${SRCFILES} go build -o ${PROJECTNAME} ${GOOPTIONS} ${SRCFILES} if [[ ! -z ${DRONE_TAG} ]] diff --git a/cmd/ipbl/ipbl.go b/cmd/ipbl/ipbl.go index 6777f41..47f75e8 100644 --- a/cmd/ipbl/ipbl.go +++ b/cmd/ipbl/ipbl.go @@ -1,42 +1,44 @@ package main import ( + "context" "log" "git.paulbsd.com/paulbsd/ipbl/src/config" "git.paulbsd.com/paulbsd/ipbl/src/database" - "git.paulbsd.com/paulbsd/ipbl/src/ipbl" - "git.paulbsd.com/paulbsd/ipbl/src/ipblws" + "git.paulbsd.com/paulbsd/ipbl/src/models" + "git.paulbsd.com/paulbsd/ipbl/src/routers" _ "github.com/lib/pq" "github.com/robfig/cron" ) -//var version string +//go:generate ../../scripts/version.sh func main() { + var ctx context.Context var cfg config.Config cfg.GetConfig() - //cfg.Options.Version = version + cfg.Options.Version = version // Initialize database app context - err := database.Init(&cfg) + err := database.Initialize(&ctx, &cfg) if err != nil { log.Fatalln(err) } defer cfg.Db.Close() // Handles IP with no reverse DNS - go ipbl.ScanIP(&cfg) + go models.ScanIP(&cfg) // Add cron task to handle them cr := cron.New() cr.AddFunc("0 * * * * *", func() { - ipbl.ScanIP(&cfg) + models.ScanIP(&cfg) }) cr.Start() // Run the ipbl web service - err = ipblws.RunServer(&cfg) + err = routers.RunServer(&ctx, &cfg) if err != nil { log.Fatalln(err) } diff --git a/cmd/ipbl/version.go b/cmd/ipbl/version.go new file mode 100644 index 0000000..31f3cbb --- /dev/null +++ b/cmd/ipbl/version.go @@ -0,0 +1,3 @@ +package main + +var version = "1.0.0" diff --git a/scripts/version.sh b/scripts/version.sh new file mode 100755 index 0000000..2ddf046 --- /dev/null +++ b/scripts/version.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +VERSION=$(git tag | tail -n 1) +echo 'package main + +var version = "'${VERSION}'"' > version.go \ No newline at end of file diff --git a/src/api/ip.go b/src/api/ip.go new file mode 100644 index 0000000..6ce4b58 --- /dev/null +++ b/src/api/ip.go @@ -0,0 +1,9 @@ +package api + +// IP describe IP objects via API calls +type IP struct { + ID int `json:"id"` + IP string `json:"ip"` + Rdns string `json:"rdns"` + Src string `json:"src"` +} diff --git a/src/config/main.go b/src/config/main.go index 6f43c26..314e93b 100644 --- a/src/config/main.go +++ b/src/config/main.go @@ -31,12 +31,12 @@ func (cfg *Config) GetConfig() error { cfg.Switchs.Init = init cfg.Switchs.Port = port - inicfg, err := ini.Load(configfile) + var inicfg, err = ini.Load(configfile) if err != nil { return err } - ipblsection := inicfg.Section("ipbl") + var ipblsection = inicfg.Section("ipbl") cfg.DbParams.DbHostname = ipblsection.Key("hostname").MustString("localhost") cfg.DbParams.DbUsername = ipblsection.Key("username").MustString("username") diff --git a/src/database/main.go b/src/database/main.go index 14f994b..2e18741 100644 --- a/src/database/main.go +++ b/src/database/main.go @@ -1,21 +1,22 @@ package database import ( + "context" "fmt" "log" "os" "git.paulbsd.com/paulbsd/ipbl/src/config" - "git.paulbsd.com/paulbsd/ipbl/src/ipbl" + "git.paulbsd.com/paulbsd/ipbl/src/models" _ "github.com/lib/pq" "xorm.io/xorm" "xorm.io/xorm/names" ) // Init creates connection to database and exec Schema -func Init(cfg *config.Config) (err error) { - databaseEngine := "postgres" - tables := []interface{}{ipbl.IP{}, ipbl.Cfg{}, ipbl.Src{}} +func Initialize(ctx *context.Context, cfg *config.Config) (err error) { + var databaseEngine = "postgres" + var tables = []interface{}{models.IP{}, models.Cfg{}, models.Src{}} cfg.Db, err = xorm.NewEngine(databaseEngine, fmt.Sprintf("%s://%s:%s@%s/%s", diff --git a/src/ipbl/cfg.go b/src/ipbl/cfg.go deleted file mode 100644 index 6fdff4b..0000000 --- a/src/ipbl/cfg.go +++ /dev/null @@ -1,8 +0,0 @@ -package ipbl - -// Cfg is ipbl config -type Cfg struct { - ID int `xorm:"pk autoincr" json:"-"` - Key string `xorm:"text notnull unique" json:"key"` - Value string `xorm:"text default" json:"value"` -} diff --git a/src/ipblws/serverhandle.go b/src/ipblws/serverhandle.go deleted file mode 100644 index f338193..0000000 --- a/src/ipblws/serverhandle.go +++ /dev/null @@ -1 +0,0 @@ -package ipblws diff --git a/src/ipblws/utils.go b/src/ipblws/utils.go deleted file mode 100644 index f4cdf39..0000000 --- a/src/ipblws/utils.go +++ /dev/null @@ -1,17 +0,0 @@ -package ipblws - -import ( - "git.paulbsd.com/paulbsd/ipbl/src/config" -) - -// ConfigAccess make ip authorization to configuration -func ConfigAccess(cfg config.Config, ip string) (ret bool) { - switch ip { - case "127.0.0.1": - return true - case "::1": - return true - default: - return - } -} diff --git a/src/models/cfg.go b/src/models/cfg.go new file mode 100644 index 0000000..4d542a0 --- /dev/null +++ b/src/models/cfg.go @@ -0,0 +1,21 @@ +package models + +import ( + "strings" + + "git.paulbsd.com/paulbsd/ipbl/src/config" +) + +func GetWhitelists(cfg config.Config) (res []string) { + var w = Cfg{Key: "whitelist"} + cfg.Db.Get(&w) + res = strings.Split(w.Value, ",") + return +} + +// Cfg is ipbl config +type Cfg struct { + ID int `xorm:"pk autoincr" json:"-"` + Key string `xorm:"text notnull unique" json:"key"` + Value string `xorm:"text default" json:"value"` +} diff --git a/src/ipbl/ip.go b/src/models/ip.go similarity index 59% rename from src/ipbl/ip.go rename to src/models/ip.go index e5222c5..8c96463 100644 --- a/src/ipbl/ip.go +++ b/src/models/ip.go @@ -1,21 +1,50 @@ -package ipbl +package models import ( + "context" "database/sql" "fmt" "log" "net" "time" + "git.paulbsd.com/paulbsd/ipbl/src/api" "git.paulbsd.com/paulbsd/ipbl/src/config" ) -func (i *IP) UpdateRDNS() (result string) { +// GetIps ... +func GetIps(ctx *context.Context, config *config.Config, limit int) (apimailboxes []*api.IP, err error) { + var ips []IP + err = config.Db.Limit(limit).Find(&ips) + for _, ml := range ips { + apimailboxes = append(apimailboxes, ml.APIFormat()) + } + return +} + +// GetIp ... +func GetIp(ctx *context.Context, config *config.Config, ipquery interface{}) (apiip *api.IP, err error) { + var ip IP + has, err := config.Db.Where("ip = ?", ipquery).Get(&ip) + if !has { + err = fmt.Errorf("Not Found") + return nil, err + } + if err != nil { + return + } + apiip = ip.APIFormat() + return +} + +func (i *IP) UpdateRDNS() (result string, err error) { res, err := net.LookupAddr(i.IP) if err != nil { - return "" + result = "" + } else { + result = res[0] } - return res[0] + return } func (i *IP) InsertIP(cfg *config.Config) (num int64, err error) { @@ -36,13 +65,12 @@ func InsertIPBulk(cfg *config.Config, ips *[]IP) (numinserts int64, numfail int6 } func ScanIP(cfg *config.Config) (err error) { - log.Println("Starting set of rdns") for { var orphans = []IP{} cfg.Db.Where("rdns IS NULL").Asc("ip").Find(&orphans) if len(orphans) > 0 { for _, i := range orphans { - reverse := i.UpdateRDNS() + reverse, _ := i.UpdateRDNS() if reverse == "" { fmt.Printf("Set \"none\" rdns to IP %s\n", i.IP) i.Rdns.String = "none" @@ -60,10 +88,22 @@ func ScanIP(cfg *config.Config) (err error) { break } } - log.Println("End set of rdns") return } +// APIFormat returns a JSON formatted object of IP +func (admin *IP) APIFormat() *api.IP { + if admin == nil { + return nil + } + return &api.IP{ + ID: admin.ID, + IP: admin.IP, + Rdns: admin.Rdns.String, + Src: admin.Src, + } +} + // IP describe IP objects type IP struct { ID int `xorm:"pk autoincr" json:"-"` diff --git a/src/ipbl/src.go b/src/models/src.go similarity index 90% rename from src/ipbl/src.go rename to src/models/src.go index 8108354..862e142 100644 --- a/src/ipbl/src.go +++ b/src/models/src.go @@ -1,4 +1,4 @@ -package ipbl +package models // Src is src types type Src struct { diff --git a/src/models/utils.go b/src/models/utils.go new file mode 100644 index 0000000..d37c53d --- /dev/null +++ b/src/models/utils.go @@ -0,0 +1,3 @@ +package models + +const keyname string = "id" diff --git a/src/ipblws/server.go b/src/routers/funcs.go similarity index 53% rename from src/ipblws/server.go rename to src/routers/funcs.go index 67520ad..2a4fd83 100644 --- a/src/ipblws/server.go +++ b/src/routers/funcs.go @@ -1,23 +1,20 @@ -package ipblws +package routers import ( + "context" "fmt" "log" "net/http" "strconv" "git.paulbsd.com/paulbsd/ipbl/src/config" - "git.paulbsd.com/paulbsd/ipbl/src/ipbl" + "git.paulbsd.com/paulbsd/ipbl/src/models" "github.com/labstack/echo/v4" ) -// RunServer runs the main echo HTTP server -func RunServer(cfg *config.Config) (err error) { - e := echo.New() - - e.HideBanner = cfg.Options.HideBanner - +// RegisterRoutes runs the main echo HTTP server +func RegisterRoutes(e *echo.Echo, ctx *context.Context, cfg *config.Config) { e.GET("/", func(c echo.Context) error { return c.HTML(http.StatusOK, ` @@ -25,58 +22,56 @@ func RunServer(cfg *config.Config) (err error) { `) }) + + // IPs e.GET("/ip/:ip", func(c echo.Context) (err error) { - var ip = ipbl.IP{IP: c.Param("ip")} - res, _ := cfg.Db.Get(&ip) - if res { - return c.JSON(http.StatusOK, ip) - } - msg := fmt.Sprintf("IP %s not found", c.Param("ip")) - log.Println(msg) - return c.JSON(http.StatusNotFound, msg) + ret, err := models.GetIp(ctx, cfg, c.Param("ip")) + return JSONResult(c, err, ret) }) e.POST("/ip", func(c echo.Context) (err error) { - var ip = new(ipbl.IP) + var ip = new(models.IP) err = c.Bind(ip) if err != nil { - return c.JSON(http.StatusInternalServerError, "Error when parsing body") + return JSONResult(c, fmt.Errorf("Error when parsing body"), "") } num, err := ip.InsertIP(cfg) if err != nil { - return c.JSON(http.StatusInternalServerError, "Error inserting data") + return JSONResult(c, fmt.Errorf("Error inserting data"), "") } - msg := fmt.Sprintf("Inserted %d IP", num) + var msg = fmt.Sprintf("Inserted %d IP", num) log.Println(msg) return c.JSON(http.StatusOK, msg) }) e.GET("/ips", func(c echo.Context) (err error) { - var ips = []ipbl.IP{} - var limit int = 50 + limit := 50 if c.QueryParam("limit") != "" { - limit, err = strconv.Atoi(c.QueryParam("limit")) - if err != nil { - limit = 50 - } + limit, _ = strconv.Atoi(c.QueryParam("limit")) } - cfg.Db.Limit(limit).Desc("created").Find(&ips) - return c.JSON(http.StatusOK, ips) + ret, err := models.GetIps(ctx, cfg, limit) + return JSONResult(c, err, ret) }) e.POST("/ips", func(c echo.Context) (err error) { - var ips = []ipbl.IP{} + var ips = []models.IP{} err = c.Bind(&ips) if err != nil { return c.JSON(http.StatusInternalServerError, "Error when parsing body") } - numinsert, numfail, _ := ipbl.InsertIPBulk(cfg, &ips) + numinsert, numfail, _ := models.InsertIPBulk(cfg, &ips) msg := fmt.Sprintf("Inserted %d IP, %d errors", numinsert, numfail) log.Println(msg) return c.JSON(http.StatusOK, msg) }) + e.GET("/ips/whitelist", func(c echo.Context) (err error) { + var whitelists = models.GetWhitelists(*cfg) + if len(whitelists) > 0 { + return c.JSON(http.StatusOK, whitelists) + } + return c.JSON(http.StatusInternalServerError, "") + }) e.Logger.Fatal( e.Start( fmt.Sprintf(":%d", cfg.Switchs.Port))) - return } diff --git a/src/routers/main.go b/src/routers/main.go new file mode 100644 index 0000000..c9e0079 --- /dev/null +++ b/src/routers/main.go @@ -0,0 +1,47 @@ +package routers + +import ( + "context" + "fmt" + "net/http" + + "git.paulbsd.com/paulbsd/ipbl/src/config" + "github.com/labstack/echo/v4" +) + +// RunServer runs the main echo server +func RunServer(ctx *context.Context, cfg *config.Config) (err error) { + e := echo.New() + e.HideBanner = true + + RegisterRoutes(e, ctx, cfg) + + e.Logger.Fatal(e.Start(fmt.Sprintf(":%d", cfg.Switchs.Port))) + return +} + +// JSONResult handles returns and error management on backend api +func JSONResult(c echo.Context, inputerr error, data interface{}) (err error) { + if inputerr != nil { + if inputerr.Error() == "Not Found" { + return c.JSON(http.StatusNotFound, inputerr.Error()) + } + if inputerr.Error() == "Error when parsing body" { + return c.JSON(http.StatusBadRequest, inputerr.Error()) + } + return c.JSON(http.StatusInternalServerError, inputerr.Error()) + } + return c.JSON(http.StatusOK, data) +} + +// ConfigAccess make ip authorization to configuration +func ConfigAccess(cfg config.Config, ip string) (ret bool) { + switch ip { + case "127.0.0.1": + return true + case "::1": + return true + default: + return + } +}