You've already forked http-badbots
234 lines
6.7 KiB
Go
234 lines
6.7 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"flag"
|
|
"fmt"
|
|
"html/template"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
_ "github.com/go-sql-driver/mysql"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
"github.com/oschwald/geoip2-golang"
|
|
)
|
|
|
|
var (
|
|
db = flag.String("db", "./logs.db", "Path to SQLite database")
|
|
geoip = flag.String("geoip", "", "Path to GeoIP databases")
|
|
port = flag.String("port", "8080", "Port to listen on")
|
|
mysql_user = flag.String("mysql_user", "", "MySQL username")
|
|
mysql_pass = flag.String("mysql_pass", "", "MySQL password")
|
|
mysql_host = flag.String("mysql_host", "localhost", "MySQL host")
|
|
mysql_port = flag.String("mysql_port", "3306", "MySQL port")
|
|
mysql_db = flag.String("mysql_db", "httpbadbots", "MySQL database")
|
|
)
|
|
|
|
type WpData struct {
|
|
Title string
|
|
URL string
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
fmt.Println("Listening on port", *port)
|
|
http.HandleFunc("/", handler)
|
|
http.ListenAndServe(":"+*port, nil)
|
|
}
|
|
|
|
func handler(w http.ResponseWriter, r *http.Request) {
|
|
var (
|
|
user, pass string
|
|
useragent = r.Header.Get("User-Agent")
|
|
host = removeWWW(r.Host)
|
|
path = r.URL.Path
|
|
iswp = isWp(path)
|
|
)
|
|
|
|
if path == "/favicon.ico" {
|
|
w.WriteHeader(404)
|
|
w.Write([]byte("404 Not Found"))
|
|
return
|
|
}
|
|
|
|
if strings.HasPrefix(path, "/wp-admin") {
|
|
http.Redirect(w, r, "/wp-login.php", 302)
|
|
}
|
|
|
|
if r.Method == "POST" {
|
|
r.ParseForm()
|
|
if iswp {
|
|
user = r.FormValue("log")
|
|
pass = r.FormValue("pwd")
|
|
} else {
|
|
user = r.FormValue("username")
|
|
pass = r.FormValue("password")
|
|
if user == "" || pass == "" {
|
|
w.WriteHeader(400)
|
|
w.Write([]byte("400 Bad Request"))
|
|
} else {
|
|
w.WriteHeader(401)
|
|
w.Write([]byte("401 Unauthorized"))
|
|
}
|
|
}
|
|
} else if !iswp {
|
|
user, pass, _ = r.BasicAuth()
|
|
w.Header().Set("WWW-Authenticate", `Basic realm="Please enter your credentials"`)
|
|
w.WriteHeader(401)
|
|
w.Write([]byte("401 Unauthorized"))
|
|
}
|
|
|
|
if iswp {
|
|
data := WpData{Title: host, URL: host}
|
|
t, _ := template.ParseFiles("wp-login.html")
|
|
t.Execute(w, data)
|
|
}
|
|
|
|
if *geoip != "" {
|
|
getGeoIP(r, user, pass, useragent, host, path)
|
|
} else {
|
|
registerConnection(r.RemoteAddr, user, pass, useragent, host, path)
|
|
}
|
|
}
|
|
|
|
func getGeoIP(r *http.Request, user, pass, useragent, host, path string) {
|
|
ip := getIP(r)
|
|
location, err := geoip2.Open(*geoip + "/GeoLite2-City.mmdb")
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
registerConnection(ip, user, pass, useragent, host, path)
|
|
return
|
|
}
|
|
defer location.Close()
|
|
city, err := location.City(net.ParseIP(ip))
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
registerConnection(ip, user, pass, useragent, host, path)
|
|
return
|
|
}
|
|
latitude, longitude := city.Location.Latitude, city.Location.Longitude
|
|
if latitude == 0 && longitude == 0 {
|
|
registerConnection(ip, user, pass, useragent, host, path)
|
|
return
|
|
}
|
|
ispdb, err := geoip2.Open(*geoip + "/GeoLite2-ASN.mmdb")
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
registerConnection(ip, user, pass, useragent, host, path)
|
|
return
|
|
}
|
|
defer ispdb.Close()
|
|
isp, err := ispdb.ASN(net.ParseIP(ip))
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
registerConnection(ip, user, pass, useragent, host, path)
|
|
return
|
|
}
|
|
if *mysql_user != "" {
|
|
_, err = mysqlInsert(ip, city.Country.Names["en"], city.City.Names["en"], latitude, longitude, isp.AutonomousSystemOrganization, user, pass, useragent, host, path)
|
|
} else {
|
|
_, err = sqliteInsert(ip, city.Country.Names["en"], city.City.Names["en"], latitude, longitude, isp.AutonomousSystemOrganization, user, pass, useragent, host, path)
|
|
}
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
fmt.Println("Connection from", ip, "(", city.Country.Names["en"], city.City.Names["en"], isp.AutonomousSystemOrganization, ")")
|
|
}
|
|
|
|
func registerConnection(ip, user, pass, useragent, host, path string) {
|
|
if *mysql_user != "" {
|
|
_, err := mysqlInsert(ip, "", "", 0, 0, "", user, pass, useragent, host, path)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
} else {
|
|
_, err := sqliteInsert(ip, "", "", 0, 0, "", user, pass, useragent, host, path)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
}
|
|
fmt.Println("Connection from", ip)
|
|
}
|
|
|
|
func sqliteInsert(ip, country, city string, latitude, longitude float64, isp, user, pass, useragent, host, path string) (int64, error) {
|
|
sqldb, err := sql.Open("sqlite3", *db)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return 0, err
|
|
}
|
|
defer sqldb.Close()
|
|
_, err = sqldb.Exec("CREATE TABLE IF NOT EXISTS connections (id INTEGER PRIMARY KEY AUTOINCREMENT, ip TEXT, country TEXT, city TEXT, latitude REAL, longitude REAL, isp TEXT, username TEXT, password TEXT, useragent TEXT, host TEXT, path TEXT, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP)")
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return 0, err
|
|
}
|
|
res, err := sqldb.Exec("INSERT INTO connections (ip, country, city, latitude, longitude, isp, username, password, useragent, host, path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ip, country, city, latitude, longitude, isp, user, pass, useragent, host, path)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return 0, err
|
|
}
|
|
return res.LastInsertId()
|
|
}
|
|
|
|
func mysqlInsert(ip, country, city string, latitude, longitude float64, isp, user, pass, useragent, host, path string) (int64, error) {
|
|
sqldb, err := sql.Open("mysql", *mysql_user+":"+*mysql_pass+"@tcp("+*mysql_host+":"+*mysql_port+")/"+*mysql_db)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return 0, err
|
|
}
|
|
defer sqldb.Close()
|
|
_, err = sqldb.Exec("CREATE TABLE IF NOT EXISTS connections (id INTEGER PRIMARY KEY AUTO_INCREMENT, ip TEXT, country TEXT, city TEXT, latitude REAL, longitude REAL, isp TEXT, username TEXT, password TEXT, useragent TEXT, host TEXT, path TEXT, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP)")
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return 0, err
|
|
}
|
|
res, err := sqldb.Exec("INSERT INTO connections (ip, country, city, latitude, longitude, isp, username, password, useragent, host, path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", ip, country, city, latitude, longitude, isp, user, pass, useragent, host, path)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return 0, err
|
|
}
|
|
return res.RowsAffected()
|
|
}
|
|
|
|
func getIP(r *http.Request) string {
|
|
var ip string
|
|
if ip = r.Header.Get("CF-Connecting-IP"); ip != "" {
|
|
return ip
|
|
} else if ip = r.Header.Get("True-Client-IP"); ip != "" {
|
|
return ip
|
|
} else if ip = r.Header.Get("X-Real-IP"); ip != "" {
|
|
return ip
|
|
} else if ip = r.Header.Get("X-Forwarded-For"); ip != "" {
|
|
return ip
|
|
} else {
|
|
ip, _, _ = net.SplitHostPort(r.RemoteAddr)
|
|
return ip
|
|
}
|
|
}
|
|
|
|
func isWp(path string) bool {
|
|
if strings.HasPrefix(path, "/wp-login.php") && checkfile("wp-login.html") {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func checkfile(path string) bool {
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func removeWWW(s string) string {
|
|
if strings.HasPrefix(s, "www.") {
|
|
return strings.TrimPrefix(s, "www.")
|
|
}
|
|
return s
|
|
}
|