Repository: nullt3r/udpx Branch: main Commit: 7e6ea01359d6 Files: 11 Total size: 22.8 KB Directory structure: gitextract_i9srdjvc/ ├── .github/ │ └── workflows/ │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── go.mod └── pkg/ ├── colors/ │ └── color_output.go ├── probes/ │ ├── print_probes.go │ └── probes.go ├── scan/ │ └── scanner.go └── utils/ ├── helpers.go └── opts_parser.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/release.yml ================================================ # This workflow will build a golang project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go name: Release on: push: # Sequence of patterns matched against refs/tags tags: - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 jobs: build: runs-on: ubuntu-latest strategy: matrix: goosarch: - 'linux/amd64' - 'linux/386' - 'darwin/amd64' - 'darwin/arm64' - 'windows/amd64' - 'windows/386' steps: - name: Checkout code uses: actions/checkout@v2 with: fetch-depth: 0 - uses: actions/setup-go@v2 with: go-version: '1.17' - name: Get OS and arch info run: | GOOSARCH=${{matrix.goosarch}} GOOS=${GOOSARCH%/*} GOARCH=${GOOSARCH#*/} BINARY_NAME=${{github.event.repository.name}}-$GOOS-$GOARCH echo "BINARY_NAME=$BINARY_NAME" >> $GITHUB_ENV echo "GOOS=$GOOS" >> $GITHUB_ENV echo "GOARCH=$GOARCH" >> $GITHUB_ENV - name: Build run: | go build -o "$BINARY_NAME" -v ./cmd/udpx - name: Zip bin run: zip -r ${{env.BINARY_NAME}}.zip ${{env.BINARY_NAME}} #- name: Release Notes # run: # git log $(git describe HEAD~ --tags --abbrev=0)..HEAD --pretty='format:* %h %s%n * %an <%ae>' --no-merges >> ".github/RELEASE-TEMPLATE.md" - name: Release with Notes uses: softprops/action-gh-release@v1 with: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} #body_path: ".github/RELEASE-TEMPLATE.md" draft: true files: ${{env.BINARY_NAME}}.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .gitignore ================================================ udpx .DS_Store ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2022 nullt3r@bugdelivery.com 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. ================================================ FILE: README.md ================================================ ![Alt text](screenshots/udpx_logo.png) # Fast and lightweight, UDPX is a single-packet UDP scanner written in Go that supports the discovery of over 45 services with the ability to add custom ones. It is easy to use and portable, and can be run on Linux, Mac OS, and Windows. Unlike internet-wide scanners like zgrab2 and zmap, UDPX is designed for portability and ease of use. * It is fast. It can scan whole /16 network in ~20 seconds for a single service. * You don't need to instal libpcap or any other dependencies. * Can run on Linux, Mac Os, Windows. Or your Nethunter if you built it for Arm. * Customizable. You can add your probes and test for even more protocols. * Stores results in JSONL format. * Scans also domain names. ## How it works Scanning UDP ports is very different than scanning TCP - you may, or may not get any result back from probing an UDP port as UDP is a connectionless protocol. UDPX implements a single-packet based approach. A protocol-specific packet is sent to the defined service (port) and waits for a response. The limit is set to 500 ms by default and can be changed by `-w` flag. If the service sends a packet back within this time, it is certain that it is indeed listening on that port and is reported as open. A typical technique is to send 0 byte UDP packets to each port on the target machine. If we receive an "ICMP Port Unreachable" message, then the port is closed. If an UDP response is received to the probe (unusual), the port is open. If we get no response at all, the state is open or filtered, meaning that the port is either open or packet filters are blocking the communication. This method is not implemented as there is no added value (UDPX tests only for specific protocols). ## Usage ![Alt text](screenshots/showcase.png) > :warning: **Concurrency:** By default, concurrency is set to 32 connections only (so you don't crash anything). If you have a lot of hosts to scan, you can set it to 128 or 256 connections. Based on your hardware, connection stability, and ulimit (on *nix), you can run 512 or more concurrent connections, but this is not recommended. To scan a single IP: ``` udpx -t 1.1.1.1 ``` To scan a CIDR with maximum of 128 connections and timeout of 1000 ms: ``` udpx -t 1.2.3.4/24 -c 128 -w 1000 ``` To scan targets from file with maximum of 128 connections for only specific service: ``` udpx -tf targets.txt -c 128 -s ipmi ``` Target can be: * IP address * CIDR * Domain IPv6 is supported. If you want to store the results, use flag `-o [filename]`. Output is in JSONL format, as can be seen bellow: ```jsonl {"address":"45.33.32.156","hostname":"scanme.nmap.org","port":123,"service":"ntp","response_data":"JAME6QAAAEoAAA56LU9vp+d2ZPwOYIyDxU8jS3GxUvM="} ``` ## Options ``` __ ______ ____ _ __ / / / / __ \/ __ \ |/ / / / / / / / / /_/ / / / /_/ / /_/ / ____/ | \____/_____/_/ /_/|_| v1.0.2-beta, by @nullt3r Usage of ./udpx-linux-amd64: -c int Maximum number of concurrent connections (default 32) -nr Do not randomize addresses -o string Output file to write results -s string Scan only for a specific service, one of: ard, bacnet, bacnet_rpm, chargen, citrix, coap, db, db, digi1, digi2, digi3, dns, ipmi, ldap, mdns, memcache, mssql, nat_port_mapping, natpmp, netbios, netis, ntp, ntp_monlist, openvpn, pca_nq, pca_st, pcanywhere, portmap, qotd, rdp, ripv, sentinel, sip, snmp1, snmp2, snmp3, ssdp, tftp, ubiquiti, ubiquiti_discovery_v1, ubiquiti_discovery_v2, upnp, valve, wdbrpc, wsd, wsd_malformed, xdmcp, kerberos, ike -sp Show received packets (only first 32 bytes) -t string IP/CIDR to scan -tf string File containing IPs/CIDRs to scan -w int Maximum time to wait for a response (socket timeout) in ms (default 500) ``` ## Building You can grab prebuilt binaries in the release section. If you want to build UDPX from source, follow these steps: From git: ``` git clone https://github.com/nullt3r/udpx cd udpx go build ./cmd/udpx ``` You can find the binary in the current directory. Or via go: ``` go install -v github.com/nullt3r/udpx/cmd/udpx@latest ``` After that, you can find the binary in `$HOME/go/bin/udpx`. If you want, move binary to `/usr/local/bin/` so you can call it directly. ## Supported services The UDPX supports more then 45 services. The most interesting are: * ipmi * snmp * ike * tftp * openvpn * kerberos * ldap The complete list of supported services: * ard * bacnet * bacnet_rpm * chargen * citrix * coap * db * db * digi1 * digi2 * digi3 * dns * ipmi * ldap * mdns * memcache * mssql * nat_port_mapping * natpmp * netbios * netis * ntp * ntp_monlist * openvpn * pca_nq * pca_st * pcanywhere * portmap * qotd * rdp * ripv * sentinel * sip * snmp1 * snmp2 * snmp3 * ssdp * tftp * ubiquiti * ubiquiti_discovery_v1 * ubiquiti_discovery_v2 * upnp * valve * wdbrpc * wsd * wsd_malformed * xdmcp * kerberos * ike ## How to add your own probe? Please send a feature request with protocol name and port and I will make it happen. Or add it on your own, the file `pkg/probes/probes.go` contains all available payloads. Specify the protocol name, port and packet data (hex-encoded). ```go { Name: "ike", Payloads: []string{"5b5e64c03e99b51100000000000000000110020000000000000001500000013400000001000000010000012801010008030000240101"}, Port: []int{500, 4500}, }, ``` ## Credits * [Nmap](https://nmap.org/) * [UDP Hunter](https://github.com/NotSoSecure/udp-hunter) * [ZGrab2](https://github.com/zmap/zgrab2) * [ZMap](https://github.com/zmap/zmap) ## Disclaimer I am not responsible for any damages. You are responsible for your own actions. Scanning or attacking targets without prior mutual consent can be illegal. ## License UDPX is distributed under [MIT License](https://raw.githubusercontent.com/nullt3r/udpx/main/LICENSE). ================================================ FILE: go.mod ================================================ module github.com/nullt3r/udpx go 1.17 ================================================ FILE: pkg/colors/color_output.go ================================================ package colors import "runtime" type colors struct { Cyan string Yellow string Red string Reset string } func SetColor() *colors { c := &colors{} if runtime.GOOS == "windows" { c.Cyan = "" c.Yellow = "" c.Red = "" c.Reset = "" } else { c.Cyan = "\033[36m" c.Yellow = "\033[33m" c.Red = "\033[1;31m" c.Reset = "\033[0m" } return c } ================================================ FILE: pkg/probes/print_probes.go ================================================ package probes func GetProbeNames() string { var avail_probes string for i := range Probes { avail_probes += Probes[i].Name if i != len(Probes)-1 { avail_probes += ", " } } return avail_probes } ================================================ FILE: pkg/probes/probes.go ================================================ package probes type Probe struct { Name string Payloads []string Port []int } var Probes = []Probe{ { Name: "ard", Payloads: []string{"0014000103"}, Port: []int{3283}, }, { Name: "bacnet", Payloads: []string{"810A001101040005D60C0C023FFFFF194B4C", "810A002501040205010E0C020000001E090C091C092C09380939093A0946094D097809791F"}, Port: []int{47808}, }, { Name: "chargen", Payloads: []string{"01"}, Port: []int{19}, }, { Name: "citrix", Payloads: []string{"1E00013002FDA8E300000000000000000000000000000000000000000000"}, Port: []int{1604}, }, { Name: "coap", Payloads: []string{"40017D70BB2E77656C6C2D6B6E6F776E04636F7265"}, Port: []int{5683}, }, { Name: "db", Payloads: []string{"444232474554414444520053514C303930313000", "444232474554414444520053514C303530303000"}, Port: []int{523}, }, { Name: "digi", Payloads: []string{"4449474900010006FFFFFFFFFFFF", "44564B5400010006FFFFFFFFFFFF", "4447445000010006FFFFFFFFFFFF"}, Port: []int{2362}, }, { Name: "dns", Payloads: []string{"34EF010000010000000000000756455253494F4E0442494E440000100003", "AE0D010000010000000000000377777706676F6F676C6503636F6D0000010001"}, Port: []int{53}, }, { Name: "ipmi", Payloads: []string{"0600FF07000000000000000000092018C88100388E04B5"}, Port: []int{623}, }, { Name: "ldap", Payloads: []string{"30840000002D02010163840000002404000A01000A0100020100020100010100870B6F626A656374636C617373308400000000000A"}, Port: []int{389}, }, { Name: "mdns", Payloads: []string{"000000000001000000000000095F7365727669636573075F646E732D7364045F756470056C6F63616C00000C0001"}, Port: []int{5353}, }, { Name: "memcache", Payloads: []string{"5A4D0000000100007374617473206974656D730D0A"}, Port: []int{11211}, }, { Name: "mssql", Payloads: []string{"02"}, Port: []int{1434}, }, { Name: "nat", Payloads: []string{"0000000000000000000000000000E3B3E483", "00000000"}, Port: []int{5351}, }, { Name: "netbios", Payloads: []string{"E5D80000000100000000000020434B4141414141414141414141414141414141414141414141414141414141410000210001"}, Port: []int{137}, }, { Name: "netis", Payloads: []string{"0A000000000000000000000000009E21BDAD"}, Port: []int{53413}, }, { Name: "ntp", Payloads: []string{"E30004FA000100000001000000000000000000000000000000000000000000000000000000000000C54F234B71B152F3", "1700032A0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, Port: []int{123}, }, { Name: "openvpn", Payloads: []string{"381212121212121212000000000038B126DE"}, Port: []int{1194}, }, { Name: "pca", Payloads: []string{"4E51", "5354", "4E5100000000000000000000000041F4BFA6"}, Port: []int{5632}, }, { Name: "portmap", Payloads: []string{"1AA9FFE10000000000000002000186A0000000020000000400000000000000000000000000000000"}, Port: []int{111}, }, { Name: "qotd", Payloads: []string{"0D0A"}, Port: []int{17}, }, { Name: "rdp", Payloads: []string{"00000000000000FF00000000000000005406"}, Port: []int{3389}, }, { Name: "ripv", Payloads: []string{"010100000000000000000000000000000000000000001000"}, Port: []int{520}, }, { Name: "sentinel", Payloads: []string{"7A0000000000"}, Port: []int{5093}, }, { Name: "sip", Payloads: []string{"4F5054494F4E53"}, Port: []int{5060}, }, { Name: "snmp", Payloads: []string{"302902010004067075626C6963A01C0204565ADC5D020100020100300E300C06082B060102010101000500", "302602010104067075626C6963A1190204DC63C29A020100020100300B300906052B060102010500", "303A020103300F02024A69020300FFE30401040201030410300E0400020100020100040004000400301204000400A00C020237F00201000201003000"}, Port: []int{161}, }, { Name: "ssdp", Payloads: []string{"4D2D534541524348202A20485454502F312E310D0A484F53543A3233392E3235352E3235352E3235303A313930300D0A53543A737364703A616C6C0D0A4D414E3A22737364703A646973636F766572220D0A0D0A"}, Port: []int{1900}, }, { Name: "tftp", Payloads: []string{"00012F61006E6574617363696900"}, Port: []int{69}, }, { Name: "ubiquiti", Payloads: []string{"01000000000000000000000000001F7FA366", "01000000", "02080000"}, Port: []int{10001}, }, { Name: "upnp", Payloads: []string{"4D2D534541524348202A20485454502F312E310D0A486F73743A3233392E3235352E3235352E3235303A313930300D0A53543A75706E703A726F6F746465766963650D0A4D616E3A22737364703A646973636F766572220D0A4D583A330D0A0D0A0D0A"}, Port: []int{1900}, }, { Name: "valve", Payloads: []string{"FFFFFFFF54536F7572636520456E67696E6520517565727900"}, Port: []int{27015}, }, { Name: "wdbrpc", Payloads: []string{"1A09FABA000000000000000255555555000000010000000100000000000000000000000000000000FFFF55120000003C00000001000000020000000000000000"}, Port: []int{17185}, }, { Name: "wsd", Payloads: []string{"3C3A2F3E0A", "3C3F786D6C2076657273696F6E3D22312E302220656E636F64696E673D227574662D38223F3E0A3C736F61703A456E76656C6F706520786D6C6E733A736F61703D22687474703A2F2F7777772E77332E6F72672F323030332F30352F736F61702D656E76656C6F70652220786D6C6E733A7773613D22687474703A2F2F736368656D61732E786D6C736F61702E6F72672F77732F323030342F30382F61646472657373696E672220786D6C6E733A7773643D22687474703A2F2F736368656D61732E786D6C736F61702E6F72672F77732F323030352F30342F646973636F766572792220786D6C6E733A777364703D22687474703A2F2F736368656D61732E786D6C736F61702E6F72672F77732F323030362F30322F64657670726F66223E0A3C736F61703A4865616465723E3C7773613A546F3E75726E3A736368656D61732D786D6C736F61702D6F72673A77733A323030353A30343A646973636F766572793C2F7773613A546F3E3C7773613A416374696F6E3E687474703A2F2F736368656D61732E786D6C736F61702E6F72672F77732F323030352F30342F646973636F766572792F50726F62653C2F7773613A416374696F6E3E3C7773613A4D65737361676549443E75726E3A757569643A63653034646164302D356432632D343032362D393134362D3161616266633165343131313C2F7773613A4D65737361676549443E3C2F736F61703A4865616465723E3C736F61703A426F64793E3C7773643A50726F62653E3C7773643A54797065733E777364703A4465766963653C2F7773643A54797065733E3C2F7773643A50726F62653E3C2F736F61703A426F64793E3C2F736F61703A456E76656C6F70653E0A"}, Port: []int{3702}, }, { Name: "xdmcp", Payloads: []string{"00010002000100"}, Port: []int{177}, }, { Name: "kerberos", Payloads: []string{"6A7A3078A103020105A20302010AA46C306AA00703050040000000A111300FA003020101A10830061B046E6D6170A2061B0474657374A3193017A003020102A110300E1B066B72627467741B0474657374A511180F32303232313131333231343530325AA7060204094A7681A80E300C020112020111020110020117"}, Port: []int{88}, }, { Name: "ike", Payloads: []string{"5b5e64c03e99b51100000000000000000110020000000000000001500000013400000001000000010000012801010008030000240101"}, Port: []int{500, 4500}, }, { Name: "radius", Payloads: []string{"0167005740b664dbf5d681b2adbd1769515118c8010773746576650212dbc6c4b758be14f005b3877c9e2fb6010406c0a8001c05060000007b50125f0f8647e8c89bd881364268fcd045324f0c0266000a017374657665"}, Port: []int{1645, 1812}, }, { Name: "dtls", Payloads: []string{"0d31323334353637385139393900", "16feff00000000000000000036", "0100002a000000000000002a", "fefd", "0000", "0002002f", "0100"}, Port: []int{80,443,853,3391,4433,4740,5349,5684,5868,6514,6636,8232,10161,10162,12346,12446,12546,12646,12746,12846,12946,13046}, }, } ================================================ FILE: pkg/scan/scanner.go ================================================ package scan import ( "bufio" "encoding/hex" "fmt" "log" "net" "strings" "time" "github.com/nullt3r/udpx/pkg/colors" "github.com/nullt3r/udpx/pkg/probes" ) type Scanner struct { Target string Probes []probes.Probe Arg_st int Arg_sp bool Channel chan Message } type Message struct { Address string `json:"address"` Hostname string `json:"hostname"` Port int `json:"port"` Service string `json:"service"` ResponseData []byte `json:"response_data"` Timestamp int64 `json:"timestamp"` } func (s Scanner) Run() { socketTimeout := time.Duration(s.Arg_st) * time.Millisecond target := s.Target // Check if input is a domain if net.ParseIP(target) == nil { // Resolve domain to IP ips, err := net.LookupIP(target) if err != nil { log.Printf("%s[!]%s Error resolving domain '%s': %s", colors.SetColor().Red, colors.SetColor().Reset, target, err) return } domain := target // Dial for each IP of domain for _, ip := range ips { ip := ip.String() // If IP is IPv6 if strings.Contains(ip, ":") { ip = "[" + ip + "]" } for _, probe := range probes.Probes { for _, port := range probe.Port { func() { for _, payload := range probe.Payloads { recv_Data := make([]byte, 32) c, err := net.Dial("udp", fmt.Sprint(ip, ":", port)) if err != nil { log.Printf("%s[!]%s [%s] Error connecting to host '%s': %s", colors.SetColor().Red, colors.SetColor().Reset, probe.Name, ip, err) return } defer c.Close() Data, err := hex.DecodeString(payload) if err != nil { log.Fatalf("%s[!]%s Error in decoding payload. Problem probe: '%s'", colors.SetColor().Red, colors.SetColor().Reset, probe.Name) } _, err = c.Write([]byte(Data)) if err != nil { return } c.SetReadDeadline(time.Now().Add(socketTimeout)) recv_length, err := bufio.NewReader(c).Read(recv_Data) if err != nil { return } if recv_length != 0 { s.Channel <- Message{Address: ip, Hostname: domain, Port: port, Service: probe.Name, ResponseData: recv_Data} return } } }() } } } } else { // Dial for a single IP ip := target // If IP is IPv6 if strings.Contains(ip, ":") { ip = "[" + ip + "]" } for _, probe := range probes.Probes { for _, port := range probe.Port { func() { for _, payload := range probe.Payloads { recv_Data := make([]byte, 32) now := time.Now() c, err := net.Dial("udp", fmt.Sprint(ip, ":", port)) if err != nil { log.Printf("%s[!]%s [%s] Error connecting to host '%s': %s", colors.SetColor().Red, colors.SetColor().Reset, probe.Name, ip, err) return } defer c.Close() Data, err := hex.DecodeString(payload) if err != nil { log.Fatalf("%s[!]%s Error in decoding payload. Problem probe: '%s'", colors.SetColor().Red, colors.SetColor().Reset, probe.Name) } _, err = c.Write([]byte(Data)) if err != nil { return } c.SetReadDeadline(time.Now().Add(socketTimeout)) recv_length, err := bufio.NewReader(c).Read(recv_Data) if err != nil { return } if recv_length != 0 { s.Channel <- Message{Address: ip, Port: port, Service: probe.Name, ResponseData: recv_Data, Timestamp: now.Unix()} return } } }() } } } } ================================================ FILE: pkg/utils/helpers.go ================================================ package utils import ( "bufio" "fmt" "net" "os" ) func EscapeByteArray(message []byte) []byte { var result []byte for _, b := range message { if b > 127 || b == '"' || b == '\n' || b == '\t' || (b <= ' ' && b >= 0) { result = append(result, []byte(fmt.Sprintf("\\x%02x", b))...) } else { result = append(result, b) } } return result } func IpsFromCidr(cidr string) ([]string, error) { inc := func(ip net.IP) { for j := len(ip) - 1; j >= 0; j-- { ip[j]++ if ip[j] > 0 { break } } } ip, ipnet, err := net.ParseCIDR(cidr) if err != nil { return nil, err } var ips []string for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) { ips = append(ips, ip.String()) } // If mask is /32 or /31 //if len(ips) <= 2 { // return ips, nil //} // remove network address and broadcast address //return ips[1 : len(ips)-1], nil return ips, nil } func ReadFile(path string) ([]string, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() var lines []string scanner := bufio.NewScanner(file) for scanner.Scan() { lines = append(lines, scanner.Text()) } return lines, scanner.Err() } func WriteChannel(lines chan string, path string) error { file, err := os.Create(path) if err != nil { return err } defer file.Close() w := bufio.NewWriter(file) for line := range lines { fmt.Fprintln(w, line) } return w.Flush() } func Deduplicate(stringSlice []string) []string { keys := make(map[string]bool) list := []string{} // If the key(values of the slice) is not equal // to the already present value in new slice (list) // then we append it. else we jump on another element. for _, entry := range stringSlice { if _, value := keys[entry]; !value { keys[entry] = true list = append(list, entry) } } return list } ================================================ FILE: pkg/utils/opts_parser.go ================================================ package utils import ( "flag" "fmt" "github.com/nullt3r/udpx/pkg/probes" ) type Options struct { Arg_t string Arg_tf string Arg_o string Arg_c int Arg_nr bool Arg_st int Arg_sp bool Arg_s string } func ParseOptions() *Options { opts := &Options{} flag.StringVar(&opts.Arg_t, "t", "", "IP/CIDR to scan") flag.StringVar(&opts.Arg_tf, "tf", "", "File containing IPs/CIDRs to scan") flag.StringVar(&opts.Arg_o, "o", "", "Output file to write results") flag.StringVar(&opts.Arg_s, "s", "", fmt.Sprintf("Scan only for a specific service, one of: %s", probes.GetProbeNames())) flag.IntVar(&opts.Arg_c, "c", 32, "Maximum number of concurrent connections") flag.BoolVar(&opts.Arg_nr, "nr", false, "Do not randomize addresses") flag.IntVar(&opts.Arg_st, "w", 500, "Maximum time to wait for a response (socket timeout) in ms") flag.BoolVar(&opts.Arg_sp, "sp", false, "Show received packets (only first 32 bytes)") flag.Parse() return opts }