GUACAMOLE-249: Add example for testing arbitrary SVC support.

This commit is contained in:
Michael Jumper 2019-12-15 22:29:10 -08:00
parent 233c0555c3
commit f3cef7e2f0
4 changed files with 285 additions and 0 deletions

View File

@ -0,0 +1,2 @@
!Makefile
*.exe

View File

@ -0,0 +1,43 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# NOTE: Parts of this file (Makefile.am) are automatically transcluded verbatim
# into Makefile.in. Though the build system (GNU Autotools) automatically adds
# its own license boilerplate to the generated Makefile.in, that boilerplate
# does not apply to the transcluded portions of Makefile.am which are licensed
# to you by the ASF under the Apache License, Version 2.0, as described above.
#
LDFLAGS=-lwtsapi32
# Requires at least Windows Vista (Windows Server 2008 qualifies)
WINVER=0x600
# Windows cross compiler (MinGW)
CC=i686-w64-mingw32-gcc
all: svc-example.exe
clean:
$(RM) svc-example.exe
svc-example.exe: svc-example.c
$(CC) svc-example.c $(LDFLAGS) \
-D_WIN32_WINNT=$(WINVER) \
-DWINVER=$(WINVER) -o svc-example.exe

View File

@ -0,0 +1,169 @@
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."
```

View File

@ -0,0 +1,71 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <windows.h>
#include <winbase.h>
#include <wtsapi32.h>
#include <stdio.h>
/**
* The name of the RDP static virtual channel (SVC).
*/
#define SVC_NAME "EXAMPLE"
int main() {
ULONG bytes_read;
ULONG bytes_written;
char message[4096];
/* Open SVC */
HANDLE svc = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, SVC_NAME, 0);
/* Fail if we cannot open an SVC at all */
if (svc == NULL) {
printf("Cannot open SVC \"" SVC_NAME "\"\n");
return 0;
}
printf("SVC \"" SVC_NAME "\" open. Reading...\n");
/* Continuously read from SVC */
while (WTSVirtualChannelRead(svc, INFINITE, message, sizeof(message), &bytes_read)) {
printf("Received %i bytes.\n", bytes_read);
/* Write all received data back to the SVC, possibly spreading the data
* across multiple writes */
char* current = message;
while (bytes_read > 0 && WTSVirtualChannelWrite(svc, current,
bytes_read, &bytes_written)) {
printf("Wrote %i bytes.\n", bytes_written);
bytes_read -= bytes_written;
}
}
/* Close SVC */
WTSVirtualChannelClose(svc);
printf("SVC \"" SVC_NAME "\" closed.\n");
return 1;
}