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-04-26 23:38:09 +02:00
"golang.org/x/net/idna"
2016-02-05 03:45:37 +01:00
"src.agwa.name/ctwatch"
2016-02-18 19:44:56 +01:00
"src.agwa.name/ctwatch/ct"
2016-02-05 03:45:37 +01:00
"src.agwa.name/ctwatch/cmd"
)
2016-03-08 16:09:26 +01:00
func DefaultStateDir ( ) string {
if envVar := os . Getenv ( "CTWATCH_STATE_DIR" ) ; envVar != "" {
return envVar
} else {
return cmd . DefaultStateDir ( "ctwatch" )
}
}
var stateDir = flag . String ( "state_dir" , DefaultStateDir ( ) , "Directory for storing state" )
2016-02-09 19:28:52 +01:00
var watchDomains [ ] string
var watchDomainSuffixes [ ] string
2016-04-26 23:38:09 +02:00
func setWatchDomains ( domains [ ] string ) error {
2016-02-09 19:28:52 +01:00
for _ , domain := range domains {
2016-02-22 23:18:56 +01:00
if domain == "." { // "." as in root zone (matches everything)
watchDomains = [ ] string { }
watchDomainSuffixes = [ ] string { "" }
break
} else {
2016-04-29 06:26:59 +02:00
asciiDomain , err := idna . ToASCII ( strings . ToLower ( domain ) )
2016-04-26 23:38:09 +02:00
if err != nil {
return fmt . Errorf ( "Invalid domain `%s': %s" , domain , err )
}
2016-04-29 06:26:59 +02:00
watchDomains = append ( watchDomains , asciiDomain )
watchDomainSuffixes = append ( watchDomainSuffixes , "." + asciiDomain )
if dot := strings . IndexRune ( asciiDomain , '.' ) ; dot != - 1 {
// also look for wildcard names that could match
// TODO: support exotic wildcards (wildcards besides "*.<DOMAIN>") in case there are CAs that issue them (there are) and clients that support them (less clear)
watchDomains = append ( watchDomains , "*" + asciiDomain [ dot : ] )
// TODO: optionally match ?.<DOMAIN> and <invalid>.<DOMAIN> also
2016-04-26 23:38:09 +02:00
}
2016-02-22 23:18:56 +01:00
}
2016-02-09 19:28:52 +01:00
}
2016-04-26 23:38:09 +02:00
return nil
2016-02-09 19:28:52 +01:00
}
func dnsNameMatches ( dnsName string ) bool {
for _ , domain := range watchDomains {
2016-04-29 06:26:59 +02:00
if dnsName == domain {
2016-02-09 19:28:52 +01:00
return true
}
}
for _ , domainSuffix := range watchDomainSuffixes {
2016-04-29 06:26:59 +02:00
if strings . HasSuffix ( dnsName , domainSuffix ) {
2016-02-09 19:28:52 +01:00
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 ,
2016-03-17 00:58:00 +01:00
IsPrecert : ctwatch . IsPrecert ( entry ) ,
FullChain : ctwatch . GetFullChain ( entry ) ,
2016-02-09 19:28:52 +01:00
}
2016-03-18 00:34:53 +01:00
info . CertInfo , info . ParseError = ctwatch . MakeCertInfoFromLogEntry ( entry )
2016-02-09 19:28:52 +01:00
2016-04-23 05:58:33 +02:00
// If there's any sort of parse error related to the identifiers, report
// the certificate because we can't say for sure it doesn't match a domain
2016-04-29 06:26:59 +02:00
// we care about (fail safe behavior).
2016-04-23 05:58:33 +02:00
if info . ParseError != nil ||
2016-04-29 06:26:59 +02:00
info . CertInfo . IdentifiersParseError != nil ||
anyDnsNameMatches ( info . CertInfo . Identifiers . DNSNames ) {
2016-04-23 05:58:33 +02:00
cmd . LogEntry ( & info )
2016-02-09 19:28:52 +01:00
}
}
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 )
2016-02-22 23:45:50 +01:00
os . Exit ( 1 )
2016-02-05 03:45:37 +01:00
}
2016-04-26 23:38:09 +02:00
if err := setWatchDomains ( domains ) ; err != nil {
fmt . Fprintf ( os . Stderr , "%s: %s\n" , os . Args [ 0 ] , err )
os . Exit ( 1 )
}
2016-02-05 03:45:37 +01:00
} else {
2016-04-26 23:38:09 +02:00
if err := setWatchDomains ( flag . Args ( ) ) ; err != nil {
fmt . Fprintf ( os . Stderr , "%s: %s\n" , os . Args [ 0 ] , err )
os . Exit ( 1 )
}
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
}