package main
import (
+ "bufio"
+ "fmt"
"net"
"net/http"
+ "regexp"
+ "time"
+
+ "github.com/prometheus/log"
)
-func probeTCP(target string, w http.ResponseWriter, module Module) (success bool) {
+func probeTCP(target string, w http.ResponseWriter, module Module) bool {
+ deadline := time.Now().Add(module.Timeout)
conn, err := net.DialTimeout("tcp", target, module.Timeout)
- if err == nil {
- success = true
- conn.Close()
+ if err != nil {
+ return false
+ }
+ defer conn.Close()
+ // Set a deadline to prevent the following code from blocking forever.
+ // If a deadline cannot be set, better fail the probe by returning an error
+ // now rather than blocking forever.
+ if err := conn.SetDeadline(deadline); err != nil {
+ return false
+ }
+ scanner := bufio.NewScanner(conn)
+ for _, qr := range module.TCP.QueryResponse {
+ log.Debugf("Processing query response entry %+v", qr)
+ send := qr.Send
+ if qr.Expect != "" {
+ re, err := regexp.Compile(qr.Expect)
+ if err != nil {
+ log.Errorf("Could not compile %q into regular expression: %v", qr.Expect, err)
+ return false
+ }
+ var match []int
+ // Read lines until one of them matches the configured regexp.
+ for scanner.Scan() {
+ log.Debugf("read %q\n", scanner.Text())
+ match = re.FindSubmatchIndex(scanner.Bytes())
+ if match != nil {
+ log.Debugf("regexp %q matched %q", re, scanner.Text())
+ break
+ }
+ }
+ if scanner.Err() != nil {
+ return false
+ }
+ if match == nil {
+ return false
+ }
+ send = string(re.Expand(nil, []byte(send), scanner.Bytes(), match))
+ }
+ if send != "" {
+ log.Debugf("Sending %q", send)
+ if _, err := fmt.Fprintf(conn, "%s\n", send); err != nil {
+ return false
+ }
+ }
}
- return
+ return true
}
package main
import (
+ "fmt"
"net"
"testing"
"time"
t.Fatalf("TCP module suceeded, expected failure.")
}
}
+
+func TestTCPConnectionQueryResponseIRC(t *testing.T) {
+ ln, err := net.Listen("tcp", "localhost:0")
+ if err != nil {
+ t.Fatalf("Error listening on socket: %s", err)
+ }
+ defer ln.Close()
+
+ module := Module{
+ Timeout: time.Second,
+ TCP: TCPProbe{
+ QueryResponse: []QueryResponse{
+ QueryResponse{Send: "NICK prober"},
+ QueryResponse{Send: "USER prober prober prober :prober"},
+ QueryResponse{Expect: "^:[^ ]+ 001"},
+ },
+ },
+ }
+
+ ch := make(chan (struct{}))
+ go func() {
+ conn, err := ln.Accept()
+ if err != nil {
+ t.Fatalf("Error accepting on socket: %s", err)
+ }
+ fmt.Fprintf(conn, ":ircd.localhost NOTICE AUTH :*** Looking up your hostname...\n")
+ var nick, user, mode, unused, realname string
+ fmt.Fscanf(conn, "NICK %s", &nick)
+ fmt.Fscanf(conn, "USER %s %s %s :%s", &user, &mode, &unused, &realname)
+ fmt.Fprintf(conn, ":ircd.localhost 001 %s :Welcome to IRC!\n", nick)
+ conn.Close()
+ ch <- struct{}{}
+ }()
+ if !probeTCP(ln.Addr().String(), nil, module) {
+ t.Fatalf("TCP module failed, expected success.")
+ }
+ <-ch
+
+ go func() {
+ conn, err := ln.Accept()
+ if err != nil {
+ t.Fatalf("Error accepting on socket: %s", err)
+ }
+ fmt.Fprintf(conn, ":ircd.localhost NOTICE AUTH :*** Looking up your hostname...\n")
+ var nick, user, mode, unused, realname string
+ fmt.Fscanf(conn, "NICK %s", &nick)
+ fmt.Fscanf(conn, "USER %s %s %s :%s", &user, &mode, &unused, &realname)
+ fmt.Fprintf(conn, "ERROR: Your IP address has been blacklisted.\n")
+ conn.Close()
+ ch <- struct{}{}
+ }()
+ if probeTCP(ln.Addr().String(), nil, module) {
+ t.Fatalf("TCP module succeeded, expected failure.")
+ }
+ <-ch
+}
+
+func TestTCPConnectionQueryResponseMatching(t *testing.T) {
+ ln, err := net.Listen("tcp", "localhost:0")
+ if err != nil {
+ t.Fatalf("Error listening on socket: %s", err)
+ }
+ defer ln.Close()
+
+ module := Module{
+ Timeout: time.Second,
+ TCP: TCPProbe{
+ QueryResponse: []QueryResponse{
+ QueryResponse{
+ Expect: "SSH-2.0-(OpenSSH_6.9p1) Debian-2",
+ Send: "CONFIRM ${1}",
+ },
+ },
+ },
+ }
+
+ ch := make(chan string)
+ go func() {
+ conn, err := ln.Accept()
+ if err != nil {
+ t.Fatalf("Error accepting on socket: %s", err)
+ }
+ conn.SetDeadline(time.Now().Add(1 * time.Second))
+ fmt.Fprintf(conn, "SSH-2.0-OpenSSH_6.9p1 Debian-2\n")
+ var version string
+ fmt.Fscanf(conn, "CONFIRM %s", &version)
+ conn.Close()
+ ch <- version
+ }()
+ if !probeTCP(ln.Addr().String(), nil, module) {
+ t.Fatalf("TCP module failed, expected success.")
+ }
+ if got, want := <-ch, "OpenSSH_6.9p1"; got != want {
+ t.Fatalf("Read unexpected version: got %q, want %q", got, want)
+ }
+}