← back to dsablic__domains-cli

Function bodies 28 total

All specs Real LLM only Function bodies
FetchCertificates function · go · L18-L62 (45 LOC)
cert.go
func FetchCertificates(records []Record) {
	lookupTargets := make(map[string][]int)

	for i, r := range records {
		if shouldCheckCert(r.Type) {
			name := r.Name
			lookupTargets[name] = append(lookupTargets[name], i)
		}
	}

	results := make(map[string]CertInfo)
	var mu sync.Mutex
	var wg sync.WaitGroup

	sem := make(chan struct{}, 10)
	for name := range lookupTargets {
		wg.Add(1)
		go func(name string) {
			defer wg.Done()
			sem <- struct{}{}
			info := lookupCert(name)
			<-sem
			mu.Lock()
			results[name] = info
			mu.Unlock()
		}(name)
	}

	wg.Wait()

	for i := range records {
		if !shouldCheckCert(records[i].Type) {
			records[i].CertIssuer = "n/a"
			records[i].CertExpires = "n/a"
			records[i].TLSVersion = "n/a"
			continue
		}

		info := results[records[i].Name]
		records[i].CertIssuer = info.Issuer
		records[i].CertExpires = info.Expires
		records[i].TLSVersion = info.TLSVersion
		records[i].CertError = info.Error
	}
}
shouldCheckCert function · go · L64-L71 (8 LOC)
cert.go
func shouldCheckCert(recordType string) bool {
	switch recordType {
	case "A", "AAAA", "CNAME":
		return true
	default:
		return false
	}
}
lookupCert function · go · L73-L106 (34 LOC)
cert.go
func lookupCert(hostname string) CertInfo {
	dialer := &net.Dialer{Timeout: 5 * time.Second}
	conn, err := tls.DialWithDialer(dialer, "tcp", hostname+":443", &tls.Config{
		ServerName:         hostname,
		InsecureSkipVerify: true,
	})
	if err != nil {
		return CertInfo{Error: formatCertError(err)}
	}
	defer conn.Close()

	state := conn.ConnectionState()
	certs := state.PeerCertificates
	if len(certs) == 0 {
		return CertInfo{Error: "no certificate"}
	}

	cert := certs[0]
	issuer := cert.Issuer.Organization
	issuerStr := ""
	if len(issuer) > 0 {
		issuerStr = issuer[0]
	} else if cert.Issuer.CommonName != "" {
		issuerStr = cert.Issuer.CommonName
	} else {
		issuerStr = "unknown"
	}

	return CertInfo{
		Issuer:     issuerStr,
		Expires:    cert.NotAfter.Format("2006-01-02"),
		TLSVersion: tlsVersionName(state.Version),
	}
}
tlsVersionName function · go · L108-L121 (14 LOC)
cert.go
func tlsVersionName(version uint16) string {
	switch version {
	case tls.VersionTLS10:
		return "TLS 1.0"
	case tls.VersionTLS11:
		return "TLS 1.1"
	case tls.VersionTLS12:
		return "TLS 1.2"
	case tls.VersionTLS13:
		return "TLS 1.3"
	default:
		return fmt.Sprintf("unknown (0x%04x)", version)
	}
}
formatCertError function · go · L123-L133 (11 LOC)
cert.go
func formatCertError(err error) string {
	if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
		return "timeout"
	}
	if opErr, ok := err.(*net.OpError); ok {
		if opErr.Op == "dial" {
			return "connection refused"
		}
	}
	return fmt.Sprintf("%v", err)
}
NewCloudflareClient function · go · L17-L28 (12 LOC)
cloudflare.go
func NewCloudflareClient(cfg CloudflareConfig) (*CloudflareClient, error) {
	if cfg.APIToken == "" {
		return nil, nil
	}

	api, err := cloudflare.NewWithAPIToken(cfg.APIToken)
	if err != nil {
		return nil, fmt.Errorf("failed to create cloudflare client: %w", err)
	}

	return &CloudflareClient{api: api}, nil
}
FetchRecords method · go · L30-L51 (22 LOC)
cloudflare.go
func (c *CloudflareClient) FetchRecords(ctx context.Context, types []string) ([]Record, error) {
	zones, err := c.api.ListZones(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to list cloudflare zones: %w", err)
	}
	c.zones = zones
	c.zoneMap = make(map[string]cloudflare.Zone, len(zones))
	for _, z := range zones {
		c.zoneMap[z.Name] = z
	}

	var records []Record
	for _, zone := range zones {
		zoneRecords, err := c.fetchZoneRecords(ctx, zone, types)
		if err != nil {
			return nil, err
		}
		records = append(records, zoneRecords...)
	}

	return records, nil
}
Same scanner, your repo: https://repobility.com — Repobility
fetchZoneRecords method · go · L53-L87 (35 LOC)
cloudflare.go
func (c *CloudflareClient) fetchZoneRecords(ctx context.Context, zone cloudflare.Zone, types []string) ([]Record, error) {
	var records []Record

	dnsRecords, _, err := c.api.ListDNSRecords(ctx, cloudflare.ZoneIdentifier(zone.ID), cloudflare.ListDNSRecordsParams{})
	if err != nil {
		return nil, fmt.Errorf("failed to list dns records for zone %s: %w", zone.Name, err)
	}

	for _, r := range dnsRecords {
		if !matchesType(r.Type, types) {
			continue
		}
		records = append(records, Record{
			Domain: zone.Name,
			Name:   r.Name,
			Value:  r.Content,
			Type:   r.Type,
			Source: "cloudflare",
		})
	}

	if matchesType("NS", types) {
		for _, ns := range zone.NameServers {
			records = append(records, Record{
				Domain: zone.Name,
				Name:   zone.Name,
				Value:  ns,
				Type:   "NS",
				Source: "cloudflare",
			})
		}
	}

	return records, nil
}
IsCloudflareDNS method · go · L89-L100 (12 LOC)
cloudflare.go
func (c *CloudflareClient) IsCloudflareDNS(domain string) bool {
	zone, ok := c.zoneMap[domain]
	if !ok {
		return false
	}
	for _, ns := range zone.NameServers {
		if strings.HasSuffix(ns, ".ns.cloudflare.com") {
			return true
		}
	}
	return false
}
matchesType function · go · L102-L112 (11 LOC)
cloudflare.go
func matchesType(recordType string, types []string) bool {
	if len(types) == 0 {
		return true
	}
	for _, t := range types {
		if strings.EqualFold(recordType, t) {
			return true
		}
	}
	return false
}
LoadConfig function · go · L19-L41 (23 LOC)
config.go
func LoadConfig() (*Config, error) {
	cfg := &Config{}

	if apiToken := os.Getenv("CLOUDFLARE_API_TOKEN"); apiToken != "" {
		cfg.Cloudflare.APIToken = apiToken
		return cfg, nil
	}

	configPath := configFilePath()
	data, err := os.ReadFile(configPath)
	if err != nil {
		if os.IsNotExist(err) {
			return cfg, nil
		}
		return nil, fmt.Errorf("failed to read config file %s: %w", configPath, err)
	}

	if err := yaml.Unmarshal(data, cfg); err != nil {
		return nil, fmt.Errorf("failed to parse config file %s: %w", configPath, err)
	}

	return cfg, nil
}
configFilePath function · go · L43-L49 (7 LOC)
config.go
func configFilePath() string {
	if xdgConfig := os.Getenv("XDG_CONFIG_HOME"); xdgConfig != "" {
		return filepath.Join(xdgConfig, "domains", "config.yaml")
	}
	home, _ := os.UserHomeDir()
	return filepath.Join(home, ".config", "domains", "config.yaml")
}
main function · go · L12-L32 (21 LOC)
main.go
func main() {
	format := flag.String("format", "tsv", "Output format: tsv or json")
	flag.StringVar(format, "f", "tsv", "Output format: tsv or json (shorthand)")
	cert := flag.Bool("cert", false, "Fetch TLS certificate info (issuer, expiry)")
	flag.BoolVar(cert, "c", false, "Fetch TLS certificate info (shorthand)")
	showVersion := flag.Bool("version", false, "Print version and exit")
	flag.BoolVar(showVersion, "v", false, "Print version and exit (shorthand)")
	flag.Parse()

	if *showVersion {
		fmt.Println(Version())
		return
	}

	types := NormalizeTypes(flag.Args())

	if err := run(context.Background(), types, *format, *cert); err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
}
run function · go · L34-L110 (77 LOC)
main.go
func run(ctx context.Context, types []string, format string, fetchCert bool) error {
	cfg, err := LoadConfig()
	if err != nil {
		return err
	}

	var records []Record
	var cfClient *CloudflareClient
	var r53Client *Route53Client
	var cfErr, r53Err error
	var cfRecords, r53Records []Record

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		defer wg.Done()
		cfClient, cfErr = NewCloudflareClient(cfg.Cloudflare)
		if cfErr != nil {
			fmt.Fprintf(os.Stderr, "warning: cloudflare: %v\n", cfErr)
			return
		}
		if cfClient == nil {
			fmt.Fprintln(os.Stderr, "warning: cloudflare credentials not configured, skipping")
			return
		}
		cfRecords, cfErr = cfClient.FetchRecords(ctx, types)
		if cfErr != nil {
			fmt.Fprintf(os.Stderr, "warning: cloudflare: %v\n", cfErr)
		}
	}()

	go func() {
		defer wg.Done()
		r53Client, r53Err = NewRoute53Client(ctx)
		if r53Err != nil {
			fmt.Fprintf(os.Stderr, "warning: route53: %v\n", r53Err)
			return
		}
		r53Records, r53Err = r53Client.FetchRecords(ctx,
resolveRegistrars function · go · L112-L140 (29 LOC)
main.go
func resolveRegistrars(records []Record, r53Client *Route53Client, whoisClient *WhoisClient) {
	domains := make(map[string]struct{})
	for _, r := range records {
		domains[r.Domain] = struct{}{}
	}

	resolved := make(map[string]string, len(domains))
	var mu sync.Mutex
	var wg sync.WaitGroup
	sem := make(chan struct{}, 5)

	for domain := range domains {
		wg.Add(1)
		go func(d string) {
			defer wg.Done()
			sem <- struct{}{}
			registrar := resolveRegistrar(d, r53Client, whoisClient)
			<-sem
			mu.Lock()
			resolved[d] = registrar
			mu.Unlock()
		}(domain)
	}
	wg.Wait()

	for i := range records {
		records[i].Registrar = resolved[records[i].Domain]
	}
}
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
resolveRegistrar function · go · L142-L148 (7 LOC)
main.go
func resolveRegistrar(domain string, r53Client *Route53Client, whoisClient *WhoisClient) string {
	if r53Client != nil && r53Client.IsRoute53Registrar(domain) {
		return "route53"
	}

	return whoisClient.LookupRegistrar(domain)
}
OutputTSV function · go · L10-L26 (17 LOC)
output.go
func OutputTSV(w io.Writer, records []Record, hasCert bool) error {

	if hasCert {
		fmt.Fprintln(w, "domain\trecord\tvalue\ttype\tsource\tregistrar\tcert_issuer\tcert_expires\ttls_version\tcert_error")
		for _, r := range records {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
				r.Domain, r.Name, r.Value, r.Type, r.Source, r.Registrar, r.CertIssuer, r.CertExpires, r.TLSVersion, r.CertError)
		}
	} else {
		fmt.Fprintln(w, "domain\trecord\tvalue\ttype\tsource\tregistrar")
		for _, r := range records {
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n",
				r.Domain, r.Name, r.Value, r.Type, r.Source, r.Registrar)
		}
	}
	return nil
}
OutputJSON function · go · L28-L32 (5 LOC)
output.go
func OutputJSON(w io.Writer, records []Record) error {
	encoder := json.NewEncoder(w)
	encoder.SetIndent("", "  ")
	return encoder.Encode(records)
}
NormalizeTypes function · go · L34-L40 (7 LOC)
output.go
func NormalizeTypes(args []string) []string {
	var types []string
	for _, arg := range args {
		types = append(types, strings.ToUpper(arg))
	}
	return types
}
NewRoute53Client function · go · L19-L28 (10 LOC)
route53.go
func NewRoute53Client(ctx context.Context) (*Route53Client, error) {
	cfg, err := config.LoadDefaultConfig(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to load aws config: %w", err)
	}

	return &Route53Client{
		client: route53.NewFromConfig(cfg),
	}, nil
}
FetchRecords method · go · L30-L56 (27 LOC)
route53.go
func (c *Route53Client) FetchRecords(ctx context.Context, recordTypes []string) ([]Record, error) {
	paginator := route53.NewListHostedZonesPaginator(c.client, &route53.ListHostedZonesInput{})
	for paginator.HasMorePages() {
		page, err := paginator.NextPage(ctx)
		if err != nil {
			return nil, fmt.Errorf("failed to list route53 hosted zones: %w", err)
		}
		c.zones = append(c.zones, page.HostedZones...)
	}

	c.zoneMap = make(map[string]types.HostedZone, len(c.zones))
	for _, zone := range c.zones {
		domain := strings.TrimSuffix(*zone.Name, ".")
		c.zoneMap[domain] = zone
	}

	var records []Record
	for _, zone := range c.zones {
		zoneRecords, err := c.fetchZoneRecords(ctx, zone, recordTypes)
		if err != nil {
			return nil, err
		}
		records = append(records, zoneRecords...)
	}

	return records, nil
}
fetchZoneRecords method · go · L58-L96 (39 LOC)
route53.go
func (c *Route53Client) fetchZoneRecords(ctx context.Context, zone types.HostedZone, recordTypes []string) ([]Record, error) {
	var records []Record
	domain := strings.TrimSuffix(*zone.Name, ".")

	var allRecordSets []types.ResourceRecordSet
	input := &route53.ListResourceRecordSetsInput{HostedZoneId: zone.Id}
	for {
		out, err := c.client.ListResourceRecordSets(ctx, input)
		if err != nil {
			return nil, fmt.Errorf("failed to list record sets for zone %s: %w", domain, err)
		}
		allRecordSets = append(allRecordSets, out.ResourceRecordSets...)
		if !out.IsTruncated {
			break
		}
		input.StartRecordName = out.NextRecordName
		input.StartRecordType = out.NextRecordType
		input.StartRecordIdentifier = out.NextRecordIdentifier
	}

	for _, rs := range allRecordSets {
		if !matchesType(string(rs.Type), recordTypes) {
			continue
		}

		values := recordValues(rs)
		for _, value := range values {
			records = append(records, Record{
				Domain: domain,
				Name:   strings.TrimSuffix(*rs.Name
recordValues function · go · L98-L108 (11 LOC)
route53.go
func recordValues(rs types.ResourceRecordSet) []string {
	if rs.AliasTarget != nil {
		return []string{strings.TrimSuffix(*rs.AliasTarget.DNSName, ".")}
	}

	var values []string
	for _, rr := range rs.ResourceRecords {
		values = append(values, strings.TrimSuffix(*rr.Value, "."))
	}
	return values
}
Repobility (the analyzer behind this table) · https://repobility.com
IsRoute53Registrar method · go · L110-L119 (10 LOC)
route53.go
func (c *Route53Client) IsRoute53Registrar(domain string) bool {
	zone, ok := c.zoneMap[domain]
	if !ok {
		return false
	}
	if zone.Config != nil && zone.Config.Comment != nil {
		return strings.Contains(*zone.Config.Comment, "Route53 Registrar")
	}
	return false
}
Version function · go · L5-L7 (3 LOC)
version.go
func Version() string {
	return version
}
NewWhoisClient function · go · L18-L25 (8 LOC)
whois.go
func NewWhoisClient() *WhoisClient {
	client := whois.NewClient()
	client.SetTimeout(10 * time.Second)
	return &WhoisClient{
		client: client,
		cache:  make(map[string]string),
	}
}
LookupRegistrar method · go · L29-L44 (16 LOC)
whois.go
func (c *WhoisClient) LookupRegistrar(domain string) string {
	c.mu.Lock()
	if registrar, ok := c.cache[domain]; ok {
		c.mu.Unlock()
		return registrar
	}
	c.mu.Unlock()

	registrar := c.fetchRegistrar(domain)

	c.mu.Lock()
	c.cache[domain] = registrar
	c.mu.Unlock()

	return registrar
}
fetchRegistrar method · go · L46-L59 (14 LOC)
whois.go
func (c *WhoisClient) fetchRegistrar(domain string) string {
	result, err := c.client.Whois(domain)
	if err != nil {
		return "unknown"
	}

	for _, line := range strings.Split(result, "\n") {
		if matches := registrarPattern.FindStringSubmatch(line); len(matches) > 1 {
			return strings.ToLower(strings.TrimSpace(matches[1]))
		}
	}

	return "unknown"
}