-
Notifications
You must be signed in to change notification settings - Fork 126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pass domains as arguments similar to dig #418
Changes from 22 commits
5ede1ce
e67fb0a
8f1e24c
8086f92
a0a9854
f675d33
e60c2f4
e02d170
4fe2357
4b0b927
dc085c1
3af7d32
1203e01
44b3f60
f4cac6b
27af3c4
26eb4f8
dd66533
578e7df
81ae118
43f0b89
c15fe97
52e166a
5d015e8
6c8b235
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ import ( | |
"fmt" | ||
"net" | ||
"os" | ||
"strings" | ||
"sort" | ||
"sync" | ||
|
||
"github.com/spf13/cobra" | ||
|
@@ -29,9 +29,28 @@ import ( | |
) | ||
|
||
const ( | ||
zdnsCLIVersion = "1.1.0" | ||
zdnsCLIVersion = "1.1.0" | ||
ModulesColWidth = 14 | ||
ModulesPerRow = 6 | ||
) | ||
|
||
// Unfortunately, we can't use the moduleToLookupModule map here, as it's not available at runtime yet. | ||
// Variables get initialized and then init() gets run (where modules are registered), so we'll have to hardcode the list. | ||
var allModules = []string{ | ||
"A", "AAAA", "AFSDB", "ANY", "ATMA", "AVC", "AXFR", "BINDVERSION", "CAA", "CDNSKEY", "CDS", "CERT", | ||
"CNAME", "CSYNC", "DHCID", "DMARC", "DNAME", "DNSKEY", "DS", "EID", "EUI48", "EUI64", "GID", "GPOS", | ||
"HINFO", "HIP", "HTTPS", "ISDN", "KEY", "KX", "L32", "L64", "LOC", "LP", "MB", "MD", "MF", "MG", | ||
"MR", "MX", "MXLOOKUP", "NAPTR", "NID", "NIMLOC", "NINFO", "NS", "NSAPPTR", "NSEC", "NSEC3", "NSEC3PARAM", | ||
"NSLOOKUP", "NULL", "NXT", "OPENPGPKEY", "PTR", "PX", "RP", "RRSIG", "RT", "SMIMEA", "SOA", "SPF", "SRV", | ||
"SSHFP", "SVCB", "TALINK", "TKEY", "TLSA", "TXT", "UID", "UINFO", "UNSPEC", "URI", | ||
} | ||
|
||
// cmds is a set of commands, special "modules" with their own flags. They should not be printed as "modules" or used with --module | ||
var cmds = map[string]struct{}{ | ||
"MXLOOKUP": {}, | ||
"NSLOOKUP": {}, | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand why they shouldn't be printed as part of modules. It seems like we want the user to know they exist? They are modules. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They're modules in the way we mean it with ZDNS but in terms of Cobra/the CLI, they're commands. We register them as "commands" and they get printed by Cobra here:
So they must be called with So I'd say they are modules in the ZDNS sense but we must call them like Cobra expects commands to be called. Cobra will print out
In this way, the user will know about all available options. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason to not just define our own There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not really sure how that would solve things.
The Cobra will only initialize that flag if the Unless we define |
||
type InputHandler interface { | ||
FeedChannel(in chan<- string, wg *sync.WaitGroup) error | ||
} | ||
|
@@ -59,6 +78,7 @@ type CLIConf struct { | |
ResultVerbosity string | ||
IncludeInOutput string | ||
OutputGroups []string | ||
ModuleString string | ||
|
||
MaxDepth int | ||
CacheSize int | ||
|
@@ -105,16 +125,10 @@ var GC CLIConf | |
var rootCmd = &cobra.Command{ | ||
Use: "zdns", | ||
Short: "High-speed, low-drag DNS lookups", | ||
Long: `ZDNS is a library and CLI tool for making very fast DNS requests. It's built upon | ||
https://github.com/zmap/dns (and in turn https://github.com/miekg/dns) for constructing | ||
and parsing raw DNS packets. | ||
|
||
ZDNS also includes its own recursive resolution and a cache to further optimize performance.`, | ||
ValidArgs: GetValidLookups(), | ||
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), | ||
Long: getRootCmdLongText(), | ||
Args: cobra.MatchAll(), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
GC.Module = strings.ToUpper(args[0]) | ||
Run(GC, cmd.Flags()) | ||
Run(GC, cmd.Flags(), args) | ||
}, | ||
Version: zdnsCLIVersion, | ||
} | ||
|
@@ -182,6 +196,7 @@ func init() { | |
rootCmd.PersistentFlags().StringVar(&GC.ClientSubnetString, "client-subnet", "", "Client subnet in CIDR format for EDNS0.") | ||
rootCmd.PersistentFlags().BoolVar(&GC.Dnssec, "dnssec", false, "Requests DNSSEC records by setting the DNSSEC OK (DO) bit") | ||
rootCmd.PersistentFlags().BoolVar(&GC.UseNSID, "nsid", false, "Request NSID.") | ||
rootCmd.PersistentFlags().StringVar(&GC.ModuleString, "module", "", "DNS module to use, can be any DNS query type or one of the modules that provides extra processing (see zdns --help for a complete list). This is required if passing domains as arguments: zdns example.com google.com --module='A'.") | ||
|
||
rootCmd.PersistentFlags().Bool("ipv4-lookup", false, "Perform an IPv4 Lookup (requests A records) in modules") | ||
rootCmd.PersistentFlags().Bool("ipv6-lookup", false, "Perform an IPv6 Lookup (requests AAAA recoreds) in modules") | ||
|
@@ -214,3 +229,43 @@ func initConfig() { | |
// Bind the current command's flags to viper | ||
util.BindFlags(rootCmd, viper.GetViper(), util.EnvPrefix) | ||
} | ||
|
||
// getRootCmdLongText dynamically generates the list of available modules for the long --help text | ||
func getRootCmdLongText() string { | ||
rootCmdLongText := `ZDNS is a library and CLI tool for making very fast DNS requests. It's built upon | ||
https://github.com/zmap/dns (and in turn https://github.com/miekg/dns) for constructing | ||
and parsing raw DNS packets. | ||
|
||
ZDNS also includes its own recursive resolution and a cache to further optimize performance. | ||
|
||
ZDNS can take input (usually domains and a module name) in the following ways: | ||
- file (./zdns A --input-file=domains.txt) | ||
- stream (echo "example.com" | ./zdns A) | ||
- as arguments (./zdns --module=A example.com google.com).` | ||
|
||
modules := make([]string, 0, len(allModules)) | ||
for _, module := range allModules { | ||
if _, ok := cmds[module]; ok { | ||
// these are their own command, do not print them as a module | ||
continue | ||
} | ||
modules = append(modules, module) | ||
} | ||
// sort modules alphabetically | ||
sort.Strings(modules) | ||
rootCmdLongText += "\n\nAvailable modules:\n" | ||
// print in grid format for readability | ||
for i, module := range modules { | ||
rootCmdLongText += fmt.Sprintf("%-*s", ModulesColWidth, module) | ||
if (i+1)%ModulesPerRow == 0 { | ||
rootCmdLongText += "\n" | ||
} | ||
} | ||
|
||
// If the number of modules isn't a multiple of ModulesPerRow, ensure a final newline | ||
if len(modules)%ModulesPerRow != 0 { | ||
fmt.Println() | ||
rootCmdLongText += "\n" | ||
} | ||
return rootCmdLongText | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* ZDNS Copyright 2024 Regents of the University of Michigan | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
* use this file except in compliance with the License. You may obtain a copy | ||
* of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
* implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
package iohandlers | ||
|
||
import ( | ||
"sync" | ||
|
||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
// StringSliceInputHandler Feeds a channel with the strings in the slice. | ||
type StringSliceInputHandler struct { | ||
Names []string | ||
} | ||
|
||
func NewStringSliceInputHandler(domains []string) *StringSliceInputHandler { | ||
if len(domains) == 0 { | ||
log.Fatal("No domains provided, cannot create a string slice input handler") | ||
} | ||
return &StringSliceInputHandler{Names: domains} | ||
} | ||
|
||
func (h *StringSliceInputHandler) FeedChannel(in chan<- string, wg *sync.WaitGroup) error { | ||
defer close(in) | ||
defer wg.Done() | ||
for _, name := range h.Names { | ||
in <- name | ||
} | ||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't we just populate the variable at the time of init() running. I don't follow why this needs to be in the variable declaration. This feels like it's absolutely going to end up out of date.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot to say earlier when I replied to the other one, I took another look at this and found another way to go about it that doesn't require this. I agree that the hard-coded list was asking for trouble.