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 ¶
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 ¶
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 ¶
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 ¶
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
}
Output:
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 ¶
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().