package vnc
import (
"context"
"fmt"
"net"
"strconv"
"time"
vnclib "github.com/alexsnet/go-vnc"
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
vncplugin "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/vnc"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
stringsutil "github.com/projectdiscovery/utils/strings"
)
type (
IsVNCResponse struct {
IsVNC bool
Banner string
}
VNCClient struct{}
)
func (c *VNCClient) Connect(ctx context.Context, host string, port int, password string) (bool, error) {
executionId := ctx.Value("executionId").(string)
return connect(executionId, host, port, password)
}
func connect(executionId string, host string, port int, password string) (bool, error) {
if host == "" || port <= 0 {
return false, fmt.Errorf("invalid host or port")
}
if !protocolstate.IsHostAllowed(executionId, host) {
return false, protocolstate.ErrHostDenied.Msgf(host)
}
dialer := protocolstate.GetDialersWithId(executionId)
if dialer == nil {
return false, fmt.Errorf("dialers not initialized for %s", executionId)
}
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", net.JoinHostPort(host, strconv.Itoa(port)))
if err != nil {
return false, err
}
defer func() {
_ = conn.Close()
}()
_ = conn.SetDeadline(time.Now().Add(10 * time.Second))
vncConfig := vnclib.NewClientConfig(password)
c, err := vnclib.Connect(context.TODO(), conn, vncConfig)
if err != nil {
if isAuthError(err) {
return false, nil
}
return false, err
}
if c != nil {
_ = c.Close()
}
return true, nil
}
func isAuthError(err error) bool {
if err == nil {
return false
}
errStr := err.Error()
return stringsutil.ContainsAnyI(errStr, "authentication", "auth", "password", "invalid", "failed")
}
func IsVNC(ctx context.Context, host string, port int) (IsVNCResponse, error) {
executionId := ctx.Value("executionId").(string)
return memoizedisVNC(executionId, host, port)
}
func isVNC(executionId string, host string, port int) (IsVNCResponse, error) {
resp := IsVNCResponse{}
timeout := 5 * time.Second
dialer := protocolstate.GetDialersWithId(executionId)
if dialer == nil {
return IsVNCResponse{}, fmt.Errorf("dialers not initialized for %s", executionId)
}
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", net.JoinHostPort(host, strconv.Itoa(port)))
if err != nil {
return resp, err
}
defer func() {
_ = conn.Close()
}()
vncPlugin := vncplugin.VNCPlugin{}
service, err := vncPlugin.Run(conn, timeout, plugins.Target{Host: host})
if err != nil {
return resp, err
}
if service == nil {
return resp, nil
}
resp.Banner = service.Version
resp.IsVNC = true
return resp, nil
}