diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..cb97737
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2020 yasutakatou
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a62e085
--- /dev/null
+++ b/README.md
@@ -0,0 +1,137 @@
+# goScore
+
+**Simple URL's security score check tool. (implement by Golang and Chrome Extension.)**.
+
+# solution
+
+When you're search knowledge, suddenly you may see a alert by Security Scan tool.
+You have unfortunately displayed a site that contains a *malware script*.
+But you've search a lot of for learn. I don't blame you.
+
+**Can't we at least easily check if it's suspicious before we open it?**
+
+I created this tool with that in mind *for you*!
+
+# features
+
+When you right-click and select **context menu**, it opens a **temporary tab** before URL be opened.
+In that tab, **request Golang API** on server and will check the following.
+
+- Can name **resolve on the Secure DNS server**?
+- Is SSL certificate is **not free type**?
+- Is **higher rank search results** on search engines?
+
+Check results are displayed with a **score(star rate)**.
+
+# installation
+
+If you want to put it under the path, you can use the following.
+
+```
+go get github.com/yasutakatou/goScore
+```
+
+If you want to create a binary and copy it yourself, use the following.
+
+```
+git clone https://github.com/yasutakatou/goScore
+cd goScore
+go build goScore.go
+```
+
+[or download binary from release page](https://github.com/yasutakatou/goScore/releases).
+save binary file, copy to entryed execute path directory.
+
+# uninstall
+
+delete that binary. del or rm command. (it's simple!)
+
+# usecase
+
+1. prepare server
+
+- run server and **space or enter press** will display **ip addres and port**.
+
+2. setting on Chrome extension
+
+- input server address to chrome extension.
+
+- in case of success, **it will be added**.
+
+3. Check URL
+
+- in case of checking url, **right click on link, display context menu, send to server**.
+
+- new tab will open, waiting for making score from server.
+
+- **score displayed**.
+
+note) **if url cached, server return old result**. So, it's fast.
+
+# config file
+
+This tool have config file, detail is following.
+
+note) default config file name is **"config"**.
+
+## [CACHE]
+
+cached time.
+this value format is value and unit multiplication.
+unit is
+"d", "D": 12 * 60 * 60
+ "h", "H": 60 * 60
+"m", "M": 60
+"s", "S": 1
+example)
+12 D -> 12days
+30 M -> 30minuts
+
+[DNS]
+9.9.9.9
+1.1.1.1
+
+define DNS servers to resolve.
+
+
+
+
+
+[SEARCH]
+1
+
+define google page rank.
+If your search results rank lower than this value, Return a star to the client.
+
+
+[SSL]
+DigiCert
+GlobalSign
+Google Trust Services
+GeoTrust
+SECOM Passport
+Sectigo RSA Organization
+Cybertrust
+
+define you trusted ssl certificate authority.
+
+
+
+[HISTORY]
+https://www.google.co.jp/ 1598448280 000
+
+this value is server cache.
+format
+url, cached unit time, score.
+score "0" is star, "1" is no star.
+
+
+
+# FYI (many thanks!)
+
+ - A simple Chrome extension that replaces your new tab page with the to-do list of the day along with your to-learn checklist
+https://github.com/PoojaB26/ToDoList-ChromeExtension
+
+# LICENSE
+
+MIT License
diff --git a/config b/config
new file mode 100644
index 0000000..c7b6514
--- /dev/null
+++ b/config
@@ -0,0 +1,16 @@
+[CACHE]
+1 M
+[DNS]
+9.9.9.9
+1.1.1.1
+[SEARCH]
+8
+[SSL]
+DigiCert
+GlobalSign
+Google Trust Services
+GeoTrust
+SECOM Passport
+Sectigo RSA Organization
+Cybertrust
+[HISTORY]
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..f382f96
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,10 @@
+module yasutakatou/goRate
+
+go 1.14
+
+require (
+ github.com/mattn/go-runewidth v0.0.9 // indirect
+ github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1
+ github.com/rocketlaunchr/google-search v1.0.0
+ gopkg.in/ini.v1 v1.60.1
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..b16b9a3
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,57 @@
+github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
+github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
+github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
+github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
+github.com/antchfx/htmlquery v1.0.0 h1:O5IXz8fZF3B3MW+B33MZWbTHBlYmcfw0BAxgErHuaMA=
+github.com/antchfx/htmlquery v1.0.0/go.mod h1:MS9yksVSQXls00iXkiMqXr0J+umL/AmxXKuP28SUJM8=
+github.com/antchfx/xmlquery v1.0.0 h1:YuEPqexGG2opZKNc9JU3Zw6zFXwC47wNcy6/F8oKsrM=
+github.com/antchfx/xmlquery v1.0.0/go.mod h1:/+CnyD/DzHRnv2eRxrVbieRU/FIF6N0C+7oTtyUtCKk=
+github.com/antchfx/xpath v1.0.0 h1:Q5gFgh2O40VTSwMOVbFE7nFNRBu3tS21Tn0KAWeEjtk=
+github.com/antchfx/xpath v1.0.0/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
+github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
+github.com/gocolly/colly/v2 v2.0.1 h1:GGPzBEdrEsavhzVK00FQXMMHBHRpwrbbCCcEKM/0Evw=
+github.com/gocolly/colly/v2 v2.0.1/go.mod h1:ePrRZlJcLTU2C/f8pJzXfkdBtBDHL5hOaKLcBoiJcq8=
+github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/jawher/mow.cli v1.1.0/go.mod h1:aNaQlc7ozF3vw6IJ2dHjp2ZFiA4ozMIYY6PyuRJwlUg=
+github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
+github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
+github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 h1:lh3PyZvY+B9nFliSGTn5uFuqQQJGuNrD0MLCokv09ag=
+github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rocketlaunchr/google-search v1.0.0 h1:ISYhL0OIa6NYEnJZ8dfyz83/5y1BbTgngi6aTSTONRs=
+github.com/rocketlaunchr/google-search v1.0.0/go.mod h1:WQ9uwsIFP7+LkNyTLOoSBB0o70KCkUYUGUBvLzms7dw=
+github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
+github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/temoto/robotstxt v1.1.1 h1:Gh8RCs8ouX3hRSxxK7B1mO5RFByQ4CmJZDwgom++JaA=
+github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+gopkg.in/ini.v1 v1.60.1 h1:P5y5shSkb0CFe44qEeMBgn8JLow09MP17jlJHanke5g=
+gopkg.in/ini.v1 v1.60.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
diff --git a/goScore.go b/goScore.go
new file mode 100644
index 0000000..9110a19
--- /dev/null
+++ b/goScore.go
@@ -0,0 +1,502 @@
+/*
+ * Simple URL's security score check tool.
+ *
+ * @author yasutakatou
+ * @copyright 2020 yasutakatou
+ * @license MIT License
+ */
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "regexp"
+ "gopkg.in/ini.v1"
+ "net/http"
+ "strings"
+ "strconv"
+ "github.com/rocketlaunchr/google-search"
+ "context"
+ "net"
+ "time"
+ "github.com/nsf/termbox-go"
+ "errors"
+ "log"
+ "encoding/json"
+ "encoding/base64"
+ "math/rand"
+)
+
+type responseData struct {
+ Status string `json:"status"`
+ Message string `json:"message"`
+}
+
+type scoreData struct {
+ CACHE int64
+ pCACHE string
+ DNS []string
+ SEARCH int
+ SSL []string
+}
+
+type historyData struct {
+ URL string
+ TIME int64
+ score string
+}
+
+var (
+ rs1Letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+ Debug bool
+ score scoreData
+ history []historyData
+ Token string
+)
+
+func init() {
+ rand.Seed(time.Now().UnixNano())
+ Token = RandStr(8)
+}
+
+func main() {
+ err := termbox.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer termbox.Close()
+ termbox.Flush()
+
+ _Debug := flag.Bool("debug", false, "[-debug=debug mode (true is enable)]")
+ _cert := flag.String("cert", "localhost.pem", "[-cert=ssl_certificate file path (if you don't use https, haven't to use this option)]")
+ _key := flag.String("key", "localhost-key.pem", "[-key=ssl_certificate_key file path (if you don't use https, haven't to use this option)]")
+ _port := flag.String("port", "8080", "[-port=port number]")
+ _config := flag.String("config", "config", "[-config=config file name]")
+
+ flag.Parse()
+
+ Debug = bool(*_Debug)
+
+ loadConfig(string(*_config))
+
+ go func() {
+ http.HandleFunc("/token", startHandler)
+
+ http.HandleFunc("/"+Token+"/api/",func(w http.ResponseWriter, r *http.Request) {
+ serverName,err := base64.URLEncoding.DecodeString(r.URL.Path[strings.LastIndex(r.URL.Path, "/")+1:])
+ if err == nil {
+ if Debug == true {
+ fmt.Println("api call: " + r.RemoteAddr + " " + string(serverName))
+ }
+ apiHandler(w, r, string(serverName))
+ }
+ })
+
+ err := http.ListenAndServeTLS(":"+string(*_port), string(*_cert), string(*_key), nil)
+ if err != nil {
+ log.Fatal("ListenAndServeTLS: ", err)
+ }
+ }()
+ fmt.Println("[ Press enter or space key. Server Status display. Escape is exit.]")
+ startServer(string(*_port))
+
+ saveConfig(string(*_config))
+}
+
+func startHandler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Access-Control-Allow-Origin", "*")
+ w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
+ w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
+ w.Header().Set("Content-Type", "application/json")
+
+ if Debug == true {
+ fmt.Println("token call: ", r.RemoteAddr, r.URL.Path)
+ }
+
+ data := &responseData{Status: "Success", Message: Token}
+ outputJson, err := json.Marshal(data)
+ if err != nil {
+ fmt.Println("%s")
+ return
+ }
+
+ w.Write(outputJson)
+}
+
+func saveConfig(filename string) bool {
+ if len(filename) == 0 {
+ return false
+ }
+
+ file, err := os.Create(filename)
+ if err != nil {
+ fmt.Println(err)
+ return false
+ }
+ defer file.Close()
+
+ writeFile(file, "[CACHE]")
+ writeFile(file, score.pCACHE)
+
+ writeFile(file, "[DNS]")
+ for i := 0; i < len(score.DNS); i++ {
+ writeFile(file, score.DNS[i])
+ }
+
+ writeFile(file, "[SEARCH]")
+ writeFile(file, strconv.Itoa(score.SEARCH))
+
+ writeFile(file, "[SSL]")
+ for i := 0; i < len(score.SSL); i++ {
+ writeFile(file, score.SSL[i])
+ }
+
+ writeFile(file, "[HISTORY]")
+ for i := 0; i < len(history); i++ {
+ writeFile(file, history[i].URL+" "+strconv.FormatInt(history[i].TIME, 10)+" "+history[i].score)
+ }
+
+ return true
+}
+
+func writeFile(file *os.File, strs string) bool {
+ _, err := file.WriteString(strs + "\n")
+ if err != nil {
+ fmt.Println(err)
+ return false
+ }
+ return true
+}
+
+func checkHistory(serverName string) (string) {
+ t := time.Now()
+ for i := 0; i < len(history); i++ {
+ if serverName == history[i].URL {
+ if t.Unix() < history[i].TIME {
+ return history[i].score
+ }
+ }
+ }
+ return ""
+}
+
+func printStar(strs string) (string) {
+ buff := ""
+ for _, c := range strs {
+ if string([]rune{c}) == "0" {
+ buff = buff + "★"
+ } else {
+ buff = buff + "☆"
+ }
+ }
+ return buff
+}
+
+func apiHandler(w http.ResponseWriter, r *http.Request, serverName string) {
+ w.Header().Set("Access-Control-Allow-Origin", "*")
+ w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
+ w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
+ w.Header().Set("Content-Type", "application/json")
+
+ scores := checkHistory(serverName)
+
+ if len(scores) == 0 {
+ if matchDNS(serverName) == true {
+ scores = scores + "0"
+ } else {
+ scores = scores + "1"
+ }
+
+ if matchSearch(serverName) == true {
+ scores = scores + "0"
+ } else {
+ scores = scores + "1"
+ }
+
+ if matchSSL(serverName) == true {
+ scores = scores + "0"
+ } else {
+ scores = scores + "1"
+ }
+
+ t := time.Now()
+ history = append(history, historyData{URL: serverName, TIME: (t.Unix() + score.CACHE), score: scores})
+ }
+
+ fmt.Println("Securescore: " + printStar(scores))
+
+ data := &responseData{Status: "Success", Message: scores}
+ outputJson, err := json.Marshal(data)
+ if err != nil {
+ fmt.Println("%s")
+ return
+ }
+ w.Write(outputJson)
+}
+
+
+func startServer(port string) {
+ termbox.SetInputMode(termbox.InputEsc)
+
+ for {
+ switch ev := termbox.PollEvent(); ev.Type {
+ case termbox.EventKey:
+ switch ev.Key {
+ case 13, 32: //Enter, Space
+ _, ip, err := getIFandIP()
+ if err != nil {
+ fmt.Println(err)
+ } else {
+ fmt.Println("source ip: ", ip, " port: ", port)
+ }
+ case 27: //Escape
+ termbox.Flush()
+ return
+ default:
+ }
+ }
+ }
+}
+
+// FYI: https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go
+func getIFandIP() (string, string, error) {
+ ifaces, err := net.Interfaces()
+ if err != nil {
+ return "", "", err
+ }
+ for _, iface := range ifaces {
+ if iface.Flags&net.FlagUp == 0 {
+ continue // interface down
+ }
+ if iface.Flags&net.FlagLoopback != 0 {
+ continue // loopback interface
+ }
+ addrs, err := iface.Addrs()
+ if err != nil {
+ return "", "", err
+ }
+ for _, addr := range addrs {
+ var ip net.IP
+ switch v := addr.(type) {
+ case *net.IPNet:
+ ip = v.IP
+ case *net.IPAddr:
+ ip = v.IP
+ }
+ if ip == nil || ip.IsLoopback() {
+ continue
+ }
+ ip = ip.To4()
+ if ip == nil {
+ continue // not an ipv4 address
+ }
+ return iface.Name, ip.String(), nil
+ }
+ }
+ return "", "", errors.New("are you connected to the network?")
+}
+
+func matchDNS(url string) bool {
+ for i := 0; i < len(score.DNS); i++ {
+ if DNSLookup(score.DNS[i], cleanURL(url)) == true {
+ return true
+ }
+ }
+ return false
+}
+
+func DNSLookup(DNSServer, url string) bool {
+ r := &net.Resolver{
+ PreferGo: true,
+ Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
+ d := net.Dialer{
+ Timeout: time.Millisecond * time.Duration(10000),
+ }
+ return d.DialContext(ctx, "udp", DNSServer + ":53")
+ },
+ }
+ ip, _ := r.LookupHost(context.Background(), url)
+ if ip != nil {
+ if len(ip[0]) > 6 {
+ if Debug == true {
+ fmt.Println(" -- [DNS]: " + DNSServer + " --")
+ fmt.Println(ip[0])
+ }
+ return true
+ }
+ }
+ return false
+}
+
+
+func matchSearch(url string) bool {
+ ctx := context.Background()
+ result,err := googlesearch.Search(ctx, cleanURL(url))
+
+ if err != nil {
+ fmt.Println(err)
+ return false
+ }
+
+ fmt.Println(" -- [SEARCH]: " + url + " --")
+ if Debug == true {
+ for i := 0; i < len(result); i++ {
+ fmt.Println(strconv.Itoa(result[i].Rank) + " " + result[i].URL)
+ }
+ }
+
+ for i := 0; i < len(result); i++ {
+ if strings.Index(result[i].URL, url) != -1 && result[i].Rank <= score.SEARCH {
+ return true
+ }
+ }
+ return false
+}
+
+func cleanURL(tmpStr string) (string) {
+ tmpStr = strings.Replace(tmpStr, "https://", "", -1)
+ tmpStr = strings.Replace(tmpStr, "http://", "", -1)
+ return strings.Split(tmpStr, "/")[0]
+}
+
+func matchSSL(url string) bool {
+ if strings.Index(url, "http://") != -1 {
+ return false
+ }
+
+ resp, err := http.Get(url)
+ if err != nil {
+ fmt.Println(err)
+ return false
+ }
+ defer resp.Body.Close()
+
+ strs := fmt.Sprintf("%s",resp.TLS.PeerCertificates[0].Issuer)
+ if Debug == true {
+ fmt.Println(" -- [SSL]: " + url + " --")
+ fmt.Println(strs)
+ }
+
+ for i := 0; i < len(score.SSL); i++ {
+ if strings.Index(strs, score.SSL[i]) != -1 {
+ return true
+ }
+ }
+ return false
+}
+
+func loadConfig(filename string) {
+ loadOptions := ini.LoadOptions{}
+ loadOptions.UnparseableSections = []string{"CACHE","DNS", "SEARCH", "SSL", "HISTORY"}
+
+ cfg, err := ini.LoadSources(loadOptions, filename)
+ if err != nil {
+ fmt.Printf("Fail to read file: %v", err)
+ os.Exit(1)
+ }
+
+ setCache(&score.CACHE, "CACHE", cfg.Section("CACHE").Body())
+ setSingleConfigStr(&score.DNS, "DNS", cfg.Section("DNS").Body())
+ setSingleConfigInt(&score.SEARCH, "SEARCH", cfg.Section("SEARCH").Body())
+ setSingleConfigStr(&score.SSL, "SSL", cfg.Section("SSL").Body())
+ setHistorys("HISTORY", cfg.Section("HISTORY").Body())
+}
+
+func setCache(config *int64, configType, datas string) {
+ if Debug == true {
+ fmt.Println(" -- " + configType + " --")
+ }
+ for _, v := range regexp.MustCompile("\r\n|\n\r|\n|\r").Split(datas, -1) {
+ if len(v) > 0 {
+ tabs := strings.Split(v, " ")
+ count, err := strconv.Atoi(tabs[0])
+ if err != nil {
+ return
+ }
+ unit := setUnit(tabs[1])
+ if unit != 0 {
+ *config = int64(count) * unit
+ }
+ score.pCACHE = v
+ }
+ if Debug == true {
+ fmt.Println(v)
+ }
+ }
+}
+
+func setUnit(unit string) (int64) {
+ switch unit {
+ case "d", "D":
+ return 12 * 60 * 60
+ case "h", "H":
+ return 60 * 60
+ case "m", "M":
+ return 60
+ case "s", "S":
+ return 1
+ default:
+ return 0
+ }
+ return 0
+}
+
+func setSingleConfigInt(config *int, configType, datas string) {
+ if Debug == true {
+ fmt.Println(" -- " + configType + " --")
+ }
+ for _, v := range regexp.MustCompile("\r\n|\n\r|\n|\r").Split(datas, -1) {
+ if len(v) > 0 {
+ tmp, err := strconv.Atoi(v)
+ if err == nil {
+ *config = tmp
+ }
+ }
+ if Debug == true {
+ fmt.Println(v)
+ }
+ }
+}
+
+func setSingleConfigStr(config *[]string, configType, datas string) {
+ if Debug == true {
+ fmt.Println(" -- " + configType + " --")
+ }
+ for _, v := range regexp.MustCompile("\r\n|\n\r|\n|\r").Split(datas, -1) {
+ if len(v) > 0 {
+ *config = append(*config, v)
+ }
+ if Debug == true {
+ fmt.Println(v)
+ }
+ }
+}
+
+func setHistorys(configType, datas string) {
+ t := time.Now()
+ if Debug == true {
+ fmt.Println(" -- " + configType + " --")
+ }
+ for _, v := range regexp.MustCompile("\r\n|\n\r|\n|\r").Split(datas, -1) {
+ if len(v) > 0 {
+ tabs := strings.Split(v, " ")
+
+ times, _ := strconv.ParseInt(tabs[1], 10, 64)
+ if t.Unix() < times {
+ history = append(history, historyData{URL: tabs[0], TIME: times, score: tabs[2]})
+ }
+ }
+ }
+ if Debug == true {
+ fmt.Println(history)
+ }
+}
+
+func RandStr(n int) string {
+ b := make([]rune, n)
+ for i := range b {
+ b[i] = rs1Letters[rand.Intn(len(rs1Letters))]
+ }
+ return string(b)
+}
diff --git a/localhost-key.pem b/localhost-key.pem
new file mode 100644
index 0000000..84834ca
--- /dev/null
+++ b/localhost-key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDiP/pOHWYju8ul
+WfLSQBgfiNaXnQRcdtlQ5AE8AY0akWlrjNtvIBt9Gw0iLa/08xsMdXPiFSUwztcb
+CIg7huWT0dfgMDkxfFOSD4YILSUxwWZRKpLiTtQpiWtiA6JGAcWL7QMXwdmiyvyf
+2JOYXcTTr70cPjlOrjQifazdxHDdMpS6lKiEnsau96uAEfa3oY7DE7foAY7Q+c8Z
+XFyCBgL8WvLBinMLzJTtqvfSi3a+O9IewsmaqzKDxgZA4zANO2XtHc1qz0qdL6KJ
+fy8OtYOybe40fRojGNIkU9mzdMp9mNyhxWR0lNjijGMd8kcxhwtV5DWA+hjma1sq
+dwkUO+fjAgMBAAECggEBALSY7xtVekeByw/FGf/cK6VYXDsPmgCY2eUo6xdUAac4
+dVblS++LSIX3hudGYtL7DxdF1eeci2BHawroJ56kkjMJaPFKbQzkOe04OPvj1XG+
++AZIHpFI7S/uwmmtjuWk2mRUH4jiMvAwabDKdx5Dky6KdEDMJoB4q6LwgIFRIepd
+ylGhQfHlHGu33VDf+diIzuLyiWjnXMfWe38obz6AOjlfMl3vn0xEOKmvIMO4AMGx
+jNbnPm6eqiiNO/cpNSep27N0DNYS0AZyhtOeji3AWpb5Xqi1Sp89KNEpay/xYqAg
+1utqqHH4cmK9+JyCacH3LwK20riDVKTC+sIpDAYAO2ECgYEA6PbeHENi8GgWYWRC
+4+DcOwU5/+2wMDN0DIUV7VnI3wQO2Pjd0RHe1ojjk2BcRiLeFbcCacumJSvj0Ror
+++awTzlETJ1bsZNwQF9LyBQYuK7g462H0NaS6aB6h+zAX9V+zLzEmc3NR45EfzTS
+aqvniZmSkDDpyb4Y0uOI7BfG39MCgYEA+J8lJpMMx0YcP2cUxRisxhUOv8QrycKa
+5h0RHcHLIBibmA7oW7RVfmU15d5OHX+w9EsJCHkO02WZOLx/rdxF09WmrTOm0uEg
+udza8PZ/zTHfyMVuK/0JfNgRJOehkYnx7eR1Asm8N7df+ZZHvPxRcaBWiEg6kJq2
+bt0iyYo23bECgYBy7GXBt5R1WEPCI73DwMej7f4gvNav0JItIvIgNqybJ9tTOeHj
+I1e0qa7qoxyiFM1OFQwBuM7rg21NWAXerHzJeEyzJuV3Am7bsqclupQ/dzXF23Mm
+mNO8oMgRib+VBzMYOP8biKT96bbssdgje04rWTEuJJMQTNDeKBUBsvEOtQKBgQCm
+3KDhd9YetGZnAzcEZWAn/aANgork+dBeWunO7+71Vq6u6mp2idzLXQBEi+QflkDc
+ZLPmVOFnbgvc8V48ANd4qi8Ylc8EP4ZoV9s12CrEPBH2vNMJgOVqPdWb0D424L9X
+q3oA6gaB2nf2mi8BntLGkRcill+AB+YjGBWt8C6MsQKBgQCERZNAch6RRs2Itghl
+djnRlXDto8Ay2DjVnQNLKosWiCubz97CRLIUbMmPpwtU/8sl+zrpPnlEF94rYq4H
+Z58Q2CiEdwZ+aqnnkknFELvY/Fp5Gxf7XBxF+yYZOJIH0uBVgwyhU7AwaT4xkt8W
+nrUXIdVOiH8ixaea94R/G27YzQ==
+-----END PRIVATE KEY-----
diff --git a/localhost.pem b/localhost.pem
new file mode 100644
index 0000000..56ec0c3
--- /dev/null
+++ b/localhost.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEADCCAmigAwIBAgIRALGfNI/pWwJgrlkfiMfWCnowDQYJKoZIhvcNAQELBQAw
+TTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMREwDwYDVQQLDAhyb290
+QHN2cjEYMBYGA1UEAwwPbWtjZXJ0IHJvb3RAc3ZyMB4XDTIwMDIwNzA0NTY1MFoX
+DTMwMDIwNzA0NTY1MFowPDEnMCUGA1UEChMebWtjZXJ0IGRldmVsb3BtZW50IGNl
+cnRpZmljYXRlMREwDwYDVQQLDAhyb290QHN2cjCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAOI/+k4dZiO7y6VZ8tJAGB+I1pedBFx22VDkATwBjRqRaWuM
+228gG30bDSItr/TzGwx1c+IVJTDO1xsIiDuG5ZPR1+AwOTF8U5IPhggtJTHBZlEq
+kuJO1CmJa2IDokYBxYvtAxfB2aLK/J/Yk5hdxNOvvRw+OU6uNCJ9rN3EcN0ylLqU
+qISexq73q4AR9rehjsMTt+gBjtD5zxlcXIIGAvxa8sGKcwvMlO2q99KLdr470h7C
+yZqrMoPGBkDjMA07Ze0dzWrPSp0vool/Lw61g7Jt7jR9GiMY0iRT2bN0yn2Y3KHF
+ZHSU2OKMYx3yRzGHC1XkNYD6GOZrWyp3CRQ75+MCAwEAAaNsMGowDgYDVR0PAQH/
+BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0j
+BBgwFoAUfZHA9SgoAj3ta6UfvMZQDqMwJqswFAYDVR0RBA0wC4IJbG9jYWxob3N0
+MA0GCSqGSIb3DQEBCwUAA4IBgQAtxUPpoyFeo02hmT54El2HG8gIZS/2WPfXaF3y
+/fS1o5rC4Rt10jNelDBe/gP9Pgg3En0xKbEkRqHe1/NIy2LGryf/5GQgDuMyaU/M
+E9ec9bh79+6J/s6Jmtlcbon57dlIfF1mg3H9UOpGPRv0R09gZjfk8yEnbv/5HopD
+0AViDwsYvOJpknppVGqw6k/WUMA6LT2p+e6Zf0uHfl9V674FEGOCBCOgKXV5/T+d
+C1HI85ZyDidK0eEcx3MrP2lVZqtJqMNal/W/OpcBahou0T+hr6IuvxxDpLVGfa3D
+2Lv4HDoKRRo0pDLXEft9DFX9ukQ0hZQZtIUh/FUraw3bRXtOfkrIWkIUTjKk7mbL
+xHpXZvuGE4uFiJkP1SuQftMZW4FWBsCvsvDj69bNlfA8vJDlStxArSGtgDjjRlZw
+MHL/aKVxg+sq/e41Q1g7t9NDeDv1Hn2SmbUfRXrNf9BqmFxHU9bdffv1ltC6vfQX
+ptKLF27Oi3HxUv2xko+OhVKz+dk=
+-----END CERTIFICATE-----