2016-02-05 03:45:37 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"bufio"
|
2016-02-09 19:28:52 +01:00
|
|
|
"strings"
|
2016-02-05 03:45:37 +01:00
|
|
|
|
2016-02-09 19:28:52 +01:00
|
|
|
"github.com/google/certificate-transparency/go"
|
2016-02-05 03:45:37 +01:00
|
|
|
"src.agwa.name/ctwatch"
|
|
|
|
"src.agwa.name/ctwatch/cmd"
|
|
|
|
)
|
|
|
|
|
2016-02-05 05:16:25 +01:00
|
|
|
var stateDir = flag.String("state_dir", cmd.DefaultStateDir("ctwatch"), "Directory for storing state")
|
2016-02-09 19:28:52 +01:00
|
|
|
var watchDomains []string
|
|
|
|
var watchDomainSuffixes []string
|
|
|
|
|
|
|
|
func setWatchDomains (domains []string) {
|
|
|
|
for _, domain := range domains {
|
|
|
|
watchDomains = append(watchDomains, strings.ToLower(domain))
|
|
|
|
watchDomainSuffixes = append(watchDomainSuffixes, "." + strings.ToLower(domain))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func dnsNameMatches (dnsName string) bool {
|
|
|
|
dnsNameLower := strings.ToLower(dnsName)
|
|
|
|
for _, domain := range watchDomains {
|
|
|
|
if dnsNameLower == domain {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, domainSuffix := range watchDomainSuffixes {
|
|
|
|
if strings.HasSuffix(dnsNameLower, domainSuffix) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func anyDnsNameMatches (dnsNames []string) bool {
|
|
|
|
for _, dnsName := range dnsNames {
|
|
|
|
if dnsNameMatches(dnsName) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func processEntry (scanner *ctwatch.Scanner, entry *ct.LogEntry) {
|
|
|
|
info := ctwatch.EntryInfo{
|
|
|
|
LogUri: scanner.LogUri,
|
|
|
|
Entry: entry,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extract DNS names
|
|
|
|
var dnsNames []string
|
|
|
|
dnsNames, info.ParseError = ctwatch.EntryDNSNames(entry)
|
|
|
|
|
|
|
|
if info.ParseError == nil {
|
|
|
|
// Match DNS names
|
|
|
|
if !anyDnsNameMatches(dnsNames) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the certificate
|
|
|
|
info.ParsedCert, info.ParseError = ctwatch.ParseEntryCertificate(entry)
|
|
|
|
if info.ParsedCert != nil {
|
|
|
|
info.CertInfo = ctwatch.MakeCertInfo(info.ParsedCert)
|
|
|
|
} else {
|
|
|
|
info.CertInfo.DnsNames = dnsNames
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.LogEntry(&info)
|
|
|
|
}
|
2016-02-05 05:16:25 +01:00
|
|
|
|
2016-02-05 03:45:37 +01:00
|
|
|
func main() {
|
|
|
|
flag.Parse()
|
|
|
|
|
2016-02-05 17:13:11 +01:00
|
|
|
if flag.NArg() == 0 {
|
|
|
|
fmt.Fprintf(os.Stderr, "Usage: %s [flags] domain ...\n", os.Args[0])
|
|
|
|
fmt.Fprintf(os.Stderr, "\n")
|
|
|
|
fmt.Fprintf(os.Stderr, "To read domain list from stdin, use '-'. To monitor all domains, use '.'.\n")
|
|
|
|
fmt.Fprintf(os.Stderr, "See '%s -help' for a list of valid flags.\n", os.Args[0])
|
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
|
2016-02-05 05:16:25 +01:00
|
|
|
if flag.NArg() == 1 && flag.Arg(0) == "-" {
|
2016-02-05 17:13:11 +01:00
|
|
|
var domains []string
|
2016-02-05 03:45:37 +01:00
|
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
|
|
for scanner.Scan() {
|
|
|
|
domains = append(domains, scanner.Text())
|
|
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
2016-02-05 05:16:25 +01:00
|
|
|
fmt.Fprintf(os.Stderr, "%s: Error reading standard input: %s\n", os.Args[0], err)
|
|
|
|
os.Exit(3)
|
2016-02-05 03:45:37 +01:00
|
|
|
}
|
2016-02-09 19:28:52 +01:00
|
|
|
setWatchDomains(domains)
|
2016-02-05 17:13:11 +01:00
|
|
|
} else if flag.NArg() == 1 && flag.Arg(0) == "." { // "." as in root zone
|
2016-02-09 19:28:52 +01:00
|
|
|
watchDomainSuffixes = []string{""}
|
2016-02-05 03:45:37 +01:00
|
|
|
} else {
|
2016-02-09 19:28:52 +01:00
|
|
|
setWatchDomains(flag.Args())
|
2016-02-05 03:45:37 +01:00
|
|
|
}
|
|
|
|
|
2016-02-09 19:28:52 +01:00
|
|
|
cmd.Main(*stateDir, processEntry)
|
2016-02-05 03:45:37 +01:00
|
|
|
}
|