mirror of
https://github.com/bolkedebruin/rdpgw.git
synced 2026-03-30 15:36:36 +00:00
Use rdp builder for generating the rdp file
This commit is contained in:
228
cmd/rdpgw/web/rdp.go
Normal file
228
cmd/rdpgw/web/rdp.go
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"log"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
crlf = "\r\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SourceNTLM int = iota
|
||||||
|
SourceSmartCard
|
||||||
|
SourceCurrent
|
||||||
|
SourceUserSelect
|
||||||
|
SourceCookie
|
||||||
|
)
|
||||||
|
|
||||||
|
type RdpConnection struct {
|
||||||
|
GatewayHostname string `rdp:"gatewayhostname"`
|
||||||
|
FullAddress string `rdp:"full address"`
|
||||||
|
AlternateFullAddress string `rdp:"alternate full address"`
|
||||||
|
Username string `rdp:"username"`
|
||||||
|
Domain string `rdp:"domain"`
|
||||||
|
GatewayCredentialSource int `rdp:"gatewaycredentialsource" default:"0"`
|
||||||
|
GatewayCredentialMethode int `rdp:"gatewayprofileusagemethod" default:"0"`
|
||||||
|
GatewayUsageMethod int `rdp:"gatewayusagemethod" default:"0"`
|
||||||
|
GatewayAccessToken string `rdp:"gatewayaccesstoken"`
|
||||||
|
PromptCredentialsOnce bool `rdp:"promptcredentialonce" default:"true"`
|
||||||
|
AuthenticationLevel int `rdp:"authentication level" default:"3"`
|
||||||
|
EnableCredSSPSupport bool `rdp:"enablecredsspsupport" default:"true"`
|
||||||
|
EnableRdsAasAuth bool `rdp:"enablerdsaadauth" default:"false"`
|
||||||
|
DisableConnectionSharing bool `rdp:"disableconnectionsharing" default:"false"`
|
||||||
|
AlternateShell string `rdp:"alternate shell"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RdpSession struct {
|
||||||
|
AutoReconnectionEnabled bool `rdp:"autoreconnectionenabled" default:"true"`
|
||||||
|
BandwidthAutodetect bool `rdp:"bandwidthautodetect" default:"true"`
|
||||||
|
NetworkAutodetect bool `rdp:"networkautodetect" default:"true"`
|
||||||
|
Compression bool `rdp:"compression" default:"true"`
|
||||||
|
VideoPlaybackMode bool `rdp:"videoplaybackmode" default:"true"`
|
||||||
|
ConnectionType int `rdp:"connection type" default:"2"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RdpDeviceRedirect struct {
|
||||||
|
AudioCaptureMode bool `rdp:"audiocapturemode" default:"false"`
|
||||||
|
EncodeRedirectedVideoCapture bool `rdp:"encode redirected video capture" default:"true"`
|
||||||
|
RedirectedVideoCaptureEncodingQuality int `rdp:"redirected video capture encoding quality" default:"0"`
|
||||||
|
AudioMode int `rdp:"audiomode" default:"0"`
|
||||||
|
CameraStoreRedirect string `rdp:"camerastoredirect" default:"false"`
|
||||||
|
DeviceStoreRedirect string `rdp:"devicestoredirect" default:"false"`
|
||||||
|
DriveStoreRedirect string `rdp:"drivestoredirect" default:"false"`
|
||||||
|
KeyboardHook int `rdp:"keyboardhook" default:"2"`
|
||||||
|
RedirectClipboard bool `rdp:"redirectclipboard" default:"true"`
|
||||||
|
RedirectComPorts bool `rdp:"redirectcomports" default:"false"`
|
||||||
|
RedirectLocation bool `rdp:"redirectlocation" default:"false"`
|
||||||
|
RedirectPrinters bool `rdp:"redirectprinters" default:"true"`
|
||||||
|
RedirectSmartcards bool `rdp:"redirectsmartcards" default:"true"`
|
||||||
|
RedirectWebAuthn bool `rdp:"redirectwebauthn" default:"true"`
|
||||||
|
UsbDeviceStoRedirect string `rdp:"usbdevicestoredirect"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RdpDisplay struct {
|
||||||
|
UseMultimon bool `rdp:"use multimon" default:"false"`
|
||||||
|
SelectedMonitors string `rdp:"selectedmonitors"`
|
||||||
|
MaximizeToCurrentDisplays bool `rdp:"maximizetocurrentdisplays" default:"false"`
|
||||||
|
SingleMonInWindowedMode bool `rdp:"singlemoninwindowedmode" default:"0"`
|
||||||
|
ScreenModeId int `rdp:"screen mode id" default:"2"`
|
||||||
|
SmartSizing bool `rdp:"smart sizing" default:"false"`
|
||||||
|
DynamicResolution bool `rdp:"dynamic resolution" default:"true"`
|
||||||
|
DesktopSizeId int `rdp:"desktop size id"`
|
||||||
|
DesktopHeight int `rdp:"desktopheight"`
|
||||||
|
DesktopWidth int `rdp:"desktopwidth"`
|
||||||
|
DesktopScaleFactor int `rdp:"desktopscalefactor"`
|
||||||
|
BitmapCacheSize int `rdp:"bitmapcachesize" default:"1500"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RdpRemoteApp struct {
|
||||||
|
RemoteApplicationCmdLine string `rdp:"remoteapplicationcmdline"`
|
||||||
|
RemoteAppExpandWorkingDir bool `rdp:"remoteapplicationexpandworkingdir" default:"true"`
|
||||||
|
RemoteApplicationFile string `rdp:"remoteapplicationfile" default:"true"`
|
||||||
|
RemoteApplicationIcon string `rdp:"remoteapplicationicon"`
|
||||||
|
RemoteApplicationMode bool `rdp:"remoteapplicationmode" default:"true"`
|
||||||
|
RemoteApplicationName string `rdp:"remoteapplicationname"`
|
||||||
|
RemoteApplicationProgram string `rdp:"remoteapplicationprogram"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RdpBuilder struct {
|
||||||
|
Connection RdpConnection
|
||||||
|
Session RdpSession
|
||||||
|
DeviceRedirect RdpDeviceRedirect
|
||||||
|
Display RdpDisplay
|
||||||
|
RemoteApp RdpRemoteApp
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRdp() *RdpBuilder {
|
||||||
|
c := RdpConnection{}
|
||||||
|
s := RdpSession{}
|
||||||
|
dr := RdpDeviceRedirect{}
|
||||||
|
disp := RdpDisplay{}
|
||||||
|
ra := RdpRemoteApp{}
|
||||||
|
|
||||||
|
initStruct(&c)
|
||||||
|
initStruct(&s)
|
||||||
|
initStruct(&dr)
|
||||||
|
initStruct(&disp)
|
||||||
|
initStruct(&ra)
|
||||||
|
|
||||||
|
return &RdpBuilder{
|
||||||
|
Connection: c,
|
||||||
|
Session: s,
|
||||||
|
DeviceRedirect: dr,
|
||||||
|
Display: disp,
|
||||||
|
RemoteApp: ra,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rb *RdpBuilder) String() string {
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
addStructToString(rb.Connection, &sb)
|
||||||
|
addStructToString(rb.Session, &sb)
|
||||||
|
addStructToString(rb.DeviceRedirect, &sb)
|
||||||
|
addStructToString(rb.Display, &sb)
|
||||||
|
addStructToString(rb.RemoteApp, &sb)
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func addStructToString(st interface{}, sb *strings.Builder) {
|
||||||
|
s := structs.New(st)
|
||||||
|
for _, f := range s.Fields() {
|
||||||
|
if isZero(f) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sb.WriteString(f.Tag("rdp"))
|
||||||
|
sb.WriteString(":")
|
||||||
|
|
||||||
|
switch f.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
sb.WriteString("s:")
|
||||||
|
sb.WriteString(f.Value().(string))
|
||||||
|
case reflect.Int:
|
||||||
|
sb.WriteString("i:")
|
||||||
|
fmt.Fprintf(sb, "%d", f.Value())
|
||||||
|
case reflect.Bool:
|
||||||
|
sb.WriteString("i:")
|
||||||
|
if f.Value().(bool) {
|
||||||
|
sb.WriteString("1")
|
||||||
|
} else {
|
||||||
|
sb.WriteString("0")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.WriteString(crlf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isZero(f *structs.Field) bool {
|
||||||
|
t := f.Tag("default")
|
||||||
|
if t == "" {
|
||||||
|
return f.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch f.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
if f.Value().(string) != t {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case reflect.Int:
|
||||||
|
i, err := strconv.Atoi(t)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("runtime error: default %s is not an integer", t)
|
||||||
|
}
|
||||||
|
if f.Value().(int) != i {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case reflect.Bool:
|
||||||
|
b := false
|
||||||
|
if t == "true" || t == "1" {
|
||||||
|
b = true
|
||||||
|
}
|
||||||
|
if f.Value().(bool) != b {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.IsZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
func initStruct(st interface{}) {
|
||||||
|
s := structs.New(st)
|
||||||
|
for _, f := range s.Fields() {
|
||||||
|
t := f.Tag("default")
|
||||||
|
if t == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch f.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
f.Set(t)
|
||||||
|
case reflect.Int:
|
||||||
|
i, err := strconv.Atoi(t)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("runtime error: default %s is not an integer", t)
|
||||||
|
}
|
||||||
|
f.Set(i)
|
||||||
|
case reflect.Bool:
|
||||||
|
b := false
|
||||||
|
if t == "true" || t == "1" {
|
||||||
|
b = true
|
||||||
|
}
|
||||||
|
err := f.Set(b)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Cannot set bool field")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
cmd/rdpgw/web/rdp_test.go
Normal file
40
cmd/rdpgw/web/rdp_test.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
GatewayHostName = "my.yahoo.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRdpBuilder(t *testing.T) {
|
||||||
|
builder := NewRdp()
|
||||||
|
builder.Connection.GatewayHostname = "my.yahoo.com"
|
||||||
|
builder.Session.AutoReconnectionEnabled = true
|
||||||
|
builder.Display.SmartSizing = true
|
||||||
|
|
||||||
|
s := builder.String()
|
||||||
|
if !strings.Contains(s, "gatewayhostname:s:"+GatewayHostName+crlf) {
|
||||||
|
t.Fatalf("%s does not contain `gatewayhostname:s:%s", s, GatewayHostName)
|
||||||
|
}
|
||||||
|
if strings.Contains(s, "autoreconnectionenabled") {
|
||||||
|
t.Fatalf("autoreconnectionenabled is in %s, but is default value", s)
|
||||||
|
}
|
||||||
|
if !strings.Contains(s, "smart sizing:i:1"+crlf) {
|
||||||
|
t.Fatalf("%s does not contain smart sizing:i:1", s)
|
||||||
|
|
||||||
|
}
|
||||||
|
log.Printf(builder.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitStruct(t *testing.T) {
|
||||||
|
conn := RdpConnection{}
|
||||||
|
initStruct(&conn)
|
||||||
|
|
||||||
|
if conn.PromptCredentialsOnce != true {
|
||||||
|
t.Fatalf("conn.PromptCredentialsOnce != true")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -197,19 +196,18 @@ func (h *Handler) HandleDownload(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Header().Set("Content-Disposition", "attachment; filename="+fn)
|
w.Header().Set("Content-Disposition", "attachment; filename="+fn)
|
||||||
w.Header().Set("Content-Type", "application/x-rdp")
|
w.Header().Set("Content-Type", "application/x-rdp")
|
||||||
|
|
||||||
data := "full address:s:" + host + "\r\n" +
|
rdp := NewRdp()
|
||||||
"gatewayhostname:s:" + h.gatewayAddress.Host + "\r\n" +
|
rdp.Connection.Username = render
|
||||||
"gatewaycredentialssource:i:5\r\n" +
|
rdp.Connection.Domain = domain
|
||||||
"gatewayusagemethod:i:1\r\n" +
|
rdp.Connection.FullAddress = host
|
||||||
"gatewayprofileusagemethod:i:1\r\n" +
|
rdp.Connection.GatewayHostname = h.gatewayAddress.Host
|
||||||
"gatewayaccesstoken:s:" + token + "\r\n" +
|
rdp.Connection.GatewayCredentialSource = SourceCookie
|
||||||
"networkautodetect:i:" + strconv.Itoa(opts.NetworkAutoDetect) + "\r\n" +
|
rdp.Connection.GatewayAccessToken = token
|
||||||
"bandwidthautodetect:i:" + strconv.Itoa(opts.BandwidthAutoDetect) + "\r\n" +
|
rdp.Session.NetworkAutodetect = opts.NetworkAutoDetect != 0
|
||||||
"connection type:i:" + strconv.Itoa(opts.ConnectionType) + "\r\n" +
|
rdp.Session.BandwidthAutodetect = opts.BandwidthAutoDetect != 0
|
||||||
"username:s:" + render + "\r\n" +
|
rdp.Session.ConnectionType = opts.ConnectionType
|
||||||
"domain:s:" + domain + "\r\n" +
|
rdp.Display.SmartSizing = true
|
||||||
"bitmapcachesize:i:32000\r\n" +
|
rdp.Display.BitmapCacheSize = 32000
|
||||||
"smart sizing:i:1\r\n"
|
|
||||||
|
|
||||||
http.ServeContent(w, r, fn, time.Now(), strings.NewReader(data))
|
http.ServeContent(w, r, fn, time.Now(), strings.NewReader(rdp.String()))
|
||||||
}
|
}
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -4,6 +4,7 @@ go 1.19
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/coreos/go-oidc/v3 v3.2.0
|
github.com/coreos/go-oidc/v3 v3.2.0
|
||||||
|
github.com/fatih/structs v1.1.0
|
||||||
github.com/go-jose/go-jose/v3 v3.0.0
|
github.com/go-jose/go-jose/v3 v3.0.0
|
||||||
github.com/gorilla/sessions v1.2.1
|
github.com/gorilla/sessions v1.2.1
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
|
|||||||
Reference in New Issue
Block a user