connectivity

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package connectivity opens TCP tunnels through SAP Cloud Connector using the Connectivity Service's SOCKS5 proxy with SAP's proprietary 0x80 authentication method.

Basic usage — dial a virtual host registered in Cloud Connector:

import "github.com/bluefunda/btp-go/connectivity"

dialer := connectivity.NewDialer(connectivity.Config{
    ProxyHost:   "connectivityproxy.internal.cf.eu10.hana.ondemand.com",
    ProxyPort:   "20004",
    TokenSource: myXsuaaSource, // satisfies connectivity.TokenSource
})

conn, err := dialer.Dial(ctx, "internal-host.corp", 22, "")
if err != nil {
    // errors.As(err, &connectivity.StageError{}) reveals which stage failed
    log.Fatal(err)
}
defer conn.Close()
// conn is now a plain net.Conn ready for SSH, SFTP, or any protocol.

The package is stdlib-only and has zero third-party dependencies.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildAuthFrame

func BuildAuthFrame(jwt, locationID string) []byte

BuildAuthFrame constructs the SAP proprietary SOCKS5 sub-negotiation frame for auth method 0x80.

Wire layout:

0x01                       (1 byte)  sub-negotiation version
uint32 BE  len(jwt)        (4 bytes) JWT byte length
[]byte     jwt
uint8      len(locBase64)  (1 byte)  0 when locationID == ""
[]byte     locBase64       (variable) base64-encoded locationID

The function is exported so frame_test.go can verify the byte layout independently of the SOCKS5 handshake.

func BuildConnect

func BuildConnect(host string, port uint16) []byte

BuildConnect constructs the SOCKS5 CONNECT request frame.

Wire layout (RFC 1928 §4):

0x05              VER
0x01              CMD=CONNECT
0x00              RSV
0x03              ATYP=DOMAINNAME
uint8  len(host)
[]byte host
uint16 BE port

func REPMessage

func REPMessage(rep byte) string

REPMessage converts a SOCKS5 REP byte into a human-readable description. It covers all eight codes defined by RFC 1928 §6 and returns a formatted hex string for unrecognised codes.

Example
package main

import (
	"fmt"

	"github.com/bluefunda/btp-go/connectivity"
)

func main() {
	fmt.Println(connectivity.REPMessage(0x00)) // succeeded
	fmt.Println(connectivity.REPMessage(0x05)) // connection refused
}
Output:
succeeded
connection refused

Types

type Config

type Config struct {
	// ProxyHost is the hostname of the Connectivity Service SOCKS5 proxy,
	// taken from the connectivity binding's onpremise_proxy_host field.
	ProxyHost string

	// ProxyPort is the port of the SOCKS5 proxy (typically "20004"),
	// taken from the onpremise_socks5_proxy_port field.
	ProxyPort string

	// TokenSource provides the JWT for SAP's 0x80 auth method. When nil,
	// the dialer falls back to method 0x00 (no authentication).
	TokenSource TokenSource

	// DialTimeout is the maximum time allowed for a complete Dial call,
	// including the TCP connection and SOCKS5 handshake. Defaults to 30s.
	DialTimeout time.Duration
}

Config holds the parameters required to construct a Dialer.

type Dialer

type Dialer struct {
	// contains filtered or unexported fields
}

Dialer opens TCP tunnels through SAP Cloud Connector via the Connectivity Service's SOCKS5 proxy. It is safe for concurrent use.

func NewDialer

func NewDialer(cfg Config) *Dialer

NewDialer creates a Dialer from cfg. If cfg.DialTimeout is zero it defaults to 30 seconds.

func (*Dialer) Dial

func (d *Dialer) Dial(ctx context.Context, virtualHost string, virtualPort uint16, locationID string) (net.Conn, error)

Dial opens a tunnel to virtualHost:virtualPort on the on-premises network. virtualHost and virtualPort must match an SCC virtual mapping. Pass an empty string for locationID to use the default Cloud Connector location.

The returned net.Conn is ready for any overlaid protocol (SSH, TLS, etc.). Errors are returned as *StageError so callers can inspect which stage failed.

Example
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/bluefunda/btp-go/connectivity"
)

func main() {
	// TokenSource is typically xsuaa.NewClientCredentialsSource(...).
	// Pass nil to fall back to SOCKS5 no-auth (method 0x00).
	dialer := connectivity.NewDialer(connectivity.Config{
		ProxyHost: "connectivityproxy.internal.cf.eu10.hana.ondemand.com",
		ProxyPort: "20004",
		// TokenSource: myXsuaaSource,
	})

	conn, err := dialer.Dial(context.Background(), "internal-sftp.corp", 22, "")
	if err != nil {
		var se *connectivity.StageError
		if errors.As(err, &se) {
			fmt.Printf("failed at stage: %s\n", se.Stage)
		}
		return
	}
	defer conn.Close()
	// conn is a plain net.Conn; layer SSH, SFTP, or any protocol on top.
	_ = conn
}

type StageError

type StageError struct {
	// Stage is one of: "dial", "greeting", "auth", "connect".
	Stage string
	// Err is the underlying error.
	Err error
}

StageError carries the name of the SOCKS5 handshake stage that failed, allowing callers to use errors.As to distinguish dial/greeting/auth/connect failures.

func (*StageError) Error

func (e *StageError) Error() string

Error implements the error interface.

func (*StageError) Unwrap

func (e *StageError) Unwrap() error

Unwrap returns the underlying error for errors.Is/As chaining.

type TokenSource

type TokenSource interface {
	Token(ctx context.Context) (string, error)
}

TokenSource is the interface for obtaining a bearer JWT. It is deliberately defined here (not imported from xsuaa) so that the connectivity module remains stdlib-only. Any concrete type that implements

Token(ctx context.Context) (string, error)

satisfies it — including xsuaa.NewClientCredentialsSource().

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL