package elasticsearch

import (
	"fmt"
	"net/http"
	"time"
	"strings"
	"HFish/utils/is"
	"HFish/core/rpc/client"
	"HFish/core/report"
)

// Config represents the configuration information.
type Config struct {
	LogFile        string  `json:"logfile"`
	UseRemote      bool    `json:"use_remote"`
	Remote         Remote  `json:"remote"`
	HpFeeds        HpFeeds `json:"hpfeeds"`
	InstanceName   string  `json:"instance_name"`
	Anonymous      bool    `json:"anonymous"`
	SensorIP       string  `json:"honeypot_ip"`
	SpoofedVersion string  `json:"spoofed_version"`
	PublicIpUrl    string  `json:"public_ip_url"`
}

// Remote is a struct used to contain the details for a remote server connection
type Remote struct {
	URL     string `json:"url"`
	UseAuth bool   `json:"use_auth"`
	Auth    Auth   `json:"auth"`
}

type HpFeeds struct {
	Host    string `json:"host"`
	Port    int    `json:"port"`
	Channel string `json:"channel"`
	Ident   string `json:"ident"`
	Secret  string `json:"secret"`
	Enabled bool   `json:"enabled"`
}

// Auth contains the details in case basic auth is to be used when connecting
// to the remote server
type Auth struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

// Conf holds the global config
var Conf Config

// Attack is a struct that contains the details of an attack entry
type Attack struct {
	SourceIP  string    `json:"source"`
	Timestamp time.Time `json:"@timestamp"`
	URL       string    `json:"url"`
	Method    string    `json:"method"`
	Form      string    `json:"form"`
	Payload   string    `json:"payload"`
	Headers   Headers   `json:"headers"`
	Type      string    `json:"type"`
	SensorIP  string    `json:"honeypot"`
}

// Headers contains the filtered headers of the HTTP request
type Headers struct {
	UserAgent      string `json:"user_agent"`
	Host           string `json:"host"`
	ContentType    string `json:"content_type"`
	AcceptLanguage string `json:"accept_language"`
}

func FakeBanner(w http.ResponseWriter, r *http.Request) {
	printInfo(r)
	response := fmt.Sprintf(`{
        "status" : 200,
        "name" : "%s",
        "cluster_name" : "elasticsearch",
        "version" : {
            "number" : "%s",
            "build_hash" : "89d3241d670db65f994242c8e838b169779e2d4",
            "build_snapshot" : false,
            "lucene_version" : "4.10.2"
        },
        "tagline" : "You Know, for Search"
    }`, Conf.InstanceName, Conf.SpoofedVersion)
	WriteResponse(w, response)
	return
}

// FakeNodes presents a fake /_nodes result
// TODO: Change IP Address with actual server IP address
func FakeNodes(w http.ResponseWriter, r *http.Request) {
	printInfo(r)
	response := fmt.Sprintf(`
	{
        "cluster_name" : "elasticsearch",
        "nodes" : {
            "x1JG6g9PRHy6ClCOO2-C4g" : {
              "name" : "%s",
              "transport_address" : "inet[/
			%s:9300]",
              "host" : "elk",
              "ip" : "127.0.1.1",
              "version" : "%s",
              "build" : "89d3241",
              "http_address" : "inet[/%s:9200]",
              "os" : {
                "refresh_interval_in_millis" : 1000,
                "available_processors" : 12,
                "cpu" : {
                  "total_cores" : 24,
                  "total_sockets" : 48,
                  "cores_per_socket" : 2
                }
              },
              "process" : {
                "refresh_interval_in_millis" : 1000,
                "id" : 2039,
                "max_file_descriptors" : 65535,
                "mlockall" : false
              },
              "jvm" : {
                "version" : "1.7.0_65"
              },
              "network" : {
                "refresh_interval_in_millis" : 5000,
                "primary_interface" : {
                  "address" : "%s",
                  "name" : "eth0",
                  "mac_address" : "08:01:c7:3F:15:DD"
                }
              },
              "transport" : {
                "bound_address" : "inet[/0:0:0:0:0:0:0:0:9300]",
                "publish_address" : "inet[/%s:9300]"
              },
              "http" : {
                "bound_address" : "inet[/0:0:0:0:0:0:0:0:9200]",
                "publish_address" : "inet[/%s:9200]",
                "max_content_length_in_bytes" : 104857600
              }}
            }
        }`, Conf.InstanceName, Conf.SensorIP, Conf.SpoofedVersion, Conf.SensorIP, Conf.SensorIP, Conf.SensorIP, Conf.SensorIP)
	WriteResponse(w, response)
	return
}

// FakeSearch returns fake search results
func FakeSearch(w http.ResponseWriter, r *http.Request) {
	printInfo(r)
	response := fmt.Sprintf(`
	{
        "took" : 6,
        "timed_out" : false,
        "_shards" : {
            "total" : 6,
            "successful" : 6,
            "failed" : 0
        },
        "hits" : {
            "total" : 1,
            "max_score" : 1.0,
            "hits" : [ {
                "_index" : ".kibana",
                "_type" : "index-pattern",
                "_id" : "logstash-*",
                "_score" : 1.0,
                "_source":{"title":"logstash-*","timeFieldName":"@timestamp","customFormats":"{}","fields":"[{\"type\":\"string\",\"indexed\":true,\"analyzed\":true,\"doc_values\":false,\"name\":\"host\",\"count\":0},{\"type\":\"string\",\"indexed\":false,\"analyzed\":false,\"name\":\"_source\",\"count\":0},{\"type\":\"string\",\"indexed\":true,\"analyzed\":false,\"doc_values\":false,\"name\":\"message.raw\",\"count\":0},{\"type\":\"string\",\"indexed\":false,\"analyzed\":false,\"name\":\"_index\",\"count\":0},{\"type\":\"string\",\"indexed\":true,\"analyzed\":false,\"doc_values\":false,\"name\":\"@version\",\"count\":0},{\"type\":\"string\",\"indexed\":true,\"analyzed\":true,\"doc_values\":false,\"name\":\"message\",\"count\":0},{\"type\":\"date\",\"indexed\":true,\"analyzed\":false,\"doc_values\":false,\"name\":\"@timestamp\",\"count\":0},{\"type\":\"string\",\"indexed\":true,\"analyzed\":false,\"name\":\"_type\",\"count\":0},{\"type\":\"string\",\"indexed\":true,\"analyzed\":false,\"name\":\"_id\",\"count\":0},{\"type\":\"string\",\"indexed\":true,\"analyzed\":false,\"doc_values\":false,\"name\":\"host.raw\",\"count\":0},{\"type\":\"geo_point\",\"indexed\":true,\"analyzed\":false,\"doc_values\":false,\"name\":\"geoip.location\",\"count\":0}]"}
            }]
        }
    }`)
	WriteResponse(w, response)
	return
}

func WriteResponse(w http.ResponseWriter, d string) {
	w.WriteHeader(200)
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.Write([]byte(d))
	return
}

func printInfo(r *http.Request) {
	info := "URL:" + r.URL.String() + "&&Method:" + r.Method + "&&RemoteAddr:" + r.RemoteAddr

	arr := strings.Split(r.RemoteAddr, ":")

	// 判断是否为 RPC 客户端
	if is.Rpc() {
		go client.ReportResult("ES", "ES蜜罐", arr[0], info, "0")
	} else {
		go report.ReportEs("ES蜜罐", "本机", arr[0], info)
	}
}

func Start(address string) {
	// Create the handlers
	http.HandleFunc("/", FakeBanner)
	http.HandleFunc("/_nodes", FakeNodes)
	http.HandleFunc("/_search", FakeSearch)

	// Start the server
	http.ListenAndServe(address, nil)
}
