Skip to content

Conversation

@racerxdl
Copy link
Contributor

@racerxdl racerxdl commented Dec 9, 2025

📝 Description

This implements what's necessary to connect to RDP Gateway using a IronRDP Web Client.

🔗 Related Issue

Partially fixes ENG-29

🚀 Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📚 Documentation update
  • 🎨 Style/UI update
  • ♻️ Code refactor
  • ⚡ Performance improvement
  • ✅ Test update
  • 🔧 Build configuration change
  • 🧹 Chore

📋 Changes Made

  • Decoupled a bit of the RDP handling code
  • Added CleanRDPPathPDU decoder/encoder
  • Added /iron websocket endpoint to gateway (so web client can connect to it)
  • Added "Open Web Client" button to credentials dialog.

🧪 Testing

  • Currently no way to test since I still need to commit a frontend.

Tests performed:

  • Unit tests pass
  • Integration tests pass
  • Manual testing completed

@sandromello
Copy link
Contributor

✅ Build Completed with Success, Version=1188.0.0-34866b8

@sandromello
Copy link
Contributor

✅ Build Completed with Success, Version=1188.0.0-d1e8747

@racerxdl racerxdl marked this pull request as ready for review December 24, 2025 00:11
@sandromello
Copy link
Contributor

✅ Build Completed with Success, Version=1188.0.0-9fcc390

Comment on lines +231 to +234
defer cancel()
defer func() {
s.deadline = nil
}()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a single defer statement:

defer func() {
        cancel()
		s.deadline = nil
}()

// UnmarshalContextExplicit fills 'out' (a pointer to struct) from a DER
// value that is a SEQUENCE whose elements are context-specific EXPLICIT tags.
// Struct fields must use tags like `asn1:"tag:0"` to map context tags to fields.
func UnmarshalContextExplicit(der []byte, out interface{}) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason to not parse der and set the contents of the type directly?
You could apply logic of optional by using pointers.
From what I've seen, it will require less code to properly set the values into the proper type::

func UnmarshalContextExplicit(der []byte, into *RDCleanPathPdu) error {
 // parse der and set values directly to the struct attributes
}

Try to avoid using the reflect library, you'll lose the type safety and it could lead to runtime panics.

// are emitted in struct field order. Slices are encoded as a single SEQUENCE OF
// unless the element is []byte (OCTET STRING), which is encoded as a single
// OCTET STRING wrapper.
func MarshalContextExplicit(v interface{}) ([]byte, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems this function is not used outside this package, make it private by renaming the function to marshalContextExplicit.

Is there any reason to not set the contents directly in a pointer known struct (*RDCleanPathPdu)?
From what I've seen, it will require less code to properly set the values into the proper type.

Try to avoid using the reflect library, you'll lose the type safety and it could lead to runtime panics.

}

func (r *RDCleanPathPdu) Encode() []byte {
data, _ := MarshalContextExplicit(r)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silently discard errors will make the program harder to troubleshoot, make sure to return the error for the caller to handle it properly.

}

func (r *NegotiatePdu) Encode() []byte {
data, _ := MarshalContextExplicit(r)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silently discard errors will make the program harder to troubleshoot, make sure to return the error for the caller to handle it properly.

X224ConnectionPDU: buildGenericRdpErrorPacket(),
}

_ = ws.WriteMessage(websocket.BinaryMessage, response.Encode())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least log this error, we must know when a packet is not being sent.

if server, ok := store.Pop(instanceKey).(*RDPProxy); ok {

if serverAny, ok := store.LoadAndDelete(instanceKey); ok {
server := serverAny.(*RDPProxy)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line could panic, you must cast it safely and check for the rest of the code to see if server is not nil:

server, _ := serverAny.(*RDPProxy)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants