170 lines
6.2 KiB
Markdown
170 lines
6.2 KiB
Markdown
Static Virtual Channel example
|
|
==============================
|
|
|
|
Guacamole supports use of static virtual channels (SVCs) for transmission of
|
|
arbitrary data between the JavaScript client and applications running within
|
|
RDP sessions. This example is intended to demonstrate how bidirectional
|
|
communication between the Guacamole client and applications within the RDP
|
|
server can be accomplished.
|
|
|
|
Arbitrary SVCs are enabled on RDP connections by specfying their names as the
|
|
value of [the `static-channels`
|
|
parameter](http://guacamole.apache.org/doc/gug/configuring-guacamole.html#rdp-device-redirection).
|
|
Each name is limited to a maximum of 7 characters. Multiple names may be listed
|
|
by separating those names with commas.
|
|
|
|
This example consists of a single file, [`svc-example.c`](svc-example.c), which
|
|
leverages the terminal server API exposed by Windows to:
|
|
|
|
1. Open a channel called "EXAMPLE"
|
|
2. Wait for blocks of data to be received
|
|
3. Send each received block of data back, unmodified.
|
|
|
|
Building the example
|
|
--------------------
|
|
|
|
A `Makefile` is provided which uses MinGW to build the `svc-example.exe`
|
|
executable, and thus can be used to produce the example application on Linux.
|
|
The `Makefile` is not platform-independent, and changes may be needed for
|
|
`make` to succeed with your installation of MinGW. If not using MinGW, the C
|
|
source itself is standard and should compile with other tools.
|
|
|
|
To build on Linux using `make`:
|
|
|
|
```console
|
|
$ make
|
|
i686-w64-mingw32-gcc svc-example.c -lwtsapi32 \
|
|
-D_WIN32_WINNT=0x600 \
|
|
-DWINVER=0x600 -o svc-example.exe
|
|
$
|
|
```
|
|
|
|
You can then copy the resulting `svc-example.exe` to the remote desktop that
|
|
you wish to test and run it within a command prompt within the remote desktop
|
|
session.
|
|
|
|
Using the example (and SVCs in general)
|
|
---------------------------------------
|
|
|
|
On the remote desktop server side (within the Windows application leveraging
|
|
SVCs to communicate with Guacamole), the following functions are used
|
|
specifically for reading/writing to the SVC:
|
|
|
|
* [`WTSVirtualChannelOpenEx()`](https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsvirtualchannelopenex)
|
|
* [`WTSVirtualChannelRead()`](https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsvirtualchannelread)
|
|
* [`WTSVirtualChannelWrite()`](https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsvirtualchannelwrite)
|
|
* [`WTSVirtualChannelClose()`](https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsvirtualchannelclose)
|
|
|
|
On the Guacamole side, bidirectional communication is established using:
|
|
|
|
* The `static-channels` connection parameter (in the case of the example, this should be set to `EXAMPLE`).
|
|
* An [`onpipe`](http://guacamole.apache.org/doc/guacamole-common-js/Guacamole.Client.html#event:onpipe)
|
|
handler which handles inbound (server-to-client) pipe streams named identically
|
|
to the SVC. The inbound pipe stream will be received upon establishing the RDP
|
|
connection and is used to transmit any data sent along the SVC **from** within
|
|
the remote desktop session. For example:
|
|
|
|
```js
|
|
client.onpipe = function pipeReceived(stream, mimetype, name) {
|
|
|
|
// Receive output of SVC
|
|
if (name === 'EXAMPLE') {
|
|
|
|
// Log start of stream
|
|
var reader = new Guacamole.StringReader(stream);
|
|
console.log('pipe: %s: stream begins', name);
|
|
|
|
// Log each received blob of text
|
|
reader.ontext = function textReceived(text) {
|
|
console.log('pipe: %s: \"%s\"', name, text);
|
|
};
|
|
|
|
// Log end of stream
|
|
reader.onend = function streamEnded() {
|
|
console.log('pipe: %s: stream ends', name);
|
|
};
|
|
|
|
}
|
|
|
|
// All other inbound pipe streams are unsupported
|
|
else
|
|
stream.sendAck('Pipe stream not supported.',
|
|
Guacamole.Status.Code.UNSUPPORTED);
|
|
|
|
};
|
|
```
|
|
|
|
* Calls to [`createPipeStream()`](http://guacamole.apache.org/doc/guacamole-common-js/Guacamole.Client.html#createPipeStream)
|
|
as needed to establish outbound (client-to-server) pipe streams named
|
|
identically to the SVC. Outbound pipe streams with the same name as the SVC
|
|
will be automatically handled by the Guacamole server, with any received data
|
|
sent along the SVC **to** the remote desktop session. For example:
|
|
|
|
```js
|
|
var example = new Guacamole.StringWriter(client.createPipeStream('text/plain', 'EXAMPLE'));
|
|
example.sendText('This is a test.');
|
|
example.sendEnd();
|
|
```
|
|
|
|
These pipe streams may be created and destroyed as desired. As long as they
|
|
have the same name as the SVC, data sent along the pipe stream will be sent
|
|
along the SVC.
|
|
|
|
Example output
|
|
--------------
|
|
|
|
If the `static-channels` parameter is set to `EXAMPLE`, the successful creation
|
|
of the "EXAMPLE" channel should be logged by guacd when the connection is
|
|
established:
|
|
|
|
```
|
|
guacd[12057]: INFO: Created static channel "EXAMPLE"...
|
|
guacd[12057]: INFO: Static channel "EXAMPLE" connected.
|
|
```
|
|
|
|
On the client side, the `onpipe` handler should be invoked immediately. If
|
|
using the example code shown above, receipt of the pipe stream for the
|
|
"EXAMPLE" channel is logged:
|
|
|
|
```
|
|
pipe: EXAMPLE: stream begins
|
|
```
|
|
|
|
Running `svc-example.exe` within a command prompt inside the remote desktop
|
|
session, the application logs that the "EXAMPLE" channel has been successfully
|
|
opened:
|
|
|
|
```
|
|
Microsoft Windows [Version 10.0.17763.437]
|
|
(c) 2018 Microsoft Corporation. All rights reserved.
|
|
|
|
C:\Users\test>svc-example.exe
|
|
SVC "EXAMPLE" open. Reading...
|
|
```
|
|
|
|
Once `createPipeStream()` has been invoked on the Guacamole client side and
|
|
using the same name as the SVC (in this case, "EXAMPLE") guacd should log the
|
|
inbound half the channel is now connected:
|
|
|
|
```
|
|
guacd[12057]: DEBUG: Inbound half of channel "EXAMPLE" connected.
|
|
```
|
|
|
|
Sending the string `This is a test.` along the client-to-server pipe (as shown
|
|
in the example code above) results in `svc-example.exe` logging that it
|
|
received those 15 bytes and has resent the same 15 bytes back along the SVC:
|
|
|
|
```
|
|
Received 15 bytes.
|
|
Wrote 15 bytes.
|
|
```
|
|
|
|
The data sent from within the remote desktop session is received on the client
|
|
side via the server-to-client pipe stream. If using the example code shown
|
|
above, the received data is logged:
|
|
|
|
```
|
|
pipe: EXAMPLE: "This is a test."
|
|
```
|
|
|