Compare commits

...

415 Commits

Author SHA1 Message Date
Virtually Nick
47b9360d46
GUACAMOLE-1714: Merge update guacenc for const parameters/values introduced in FFmpeg 5.0. 2023-02-02 20:36:32 -05:00
Mike Jumper
98c2a6adcb
GUACAMOLE-377: Merge correction ensuring users receive a proper frame boundary when joining. 2023-01-24 14:25:09 -08:00
Alex Leitner
3b0a9bac75 GUACAMOLE-377: Send a sync instruction to users when synchronizing surfaces. 2023-01-23 20:55:01 +00:00
Mike Jumper
f6893ed319 Merge 1.5.0 changes back to master. 2023-01-10 21:54:05 -08:00
James Muehlner
a5214c971a
GUACAMOLE-1604: Merge version number bumps for 1.5.0. 2023-01-10 17:16:43 -08:00
Mike Jumper
ccfcef8c0f GUACAMOLE-1604: Add explicit libtool version info for libguac-terminal. 2023-01-10 17:08:15 -08:00
Mike Jumper
1a7a57ed19 GUACAMOLE-1604: Update libtool version info for libguac (interfaces added and changed).
The only changed interface here is the guac_user_info struct, which now
has a "name" member.
2023-01-10 17:07:16 -08:00
Mike Jumper
eac064bde9 GUACAMOLE-1604: Bump version number to 1.5.0. 2023-01-10 17:02:06 -08:00
Virtually Nick
4afc1d85ce Merge 1.5.0 changes back to master. 2023-01-04 15:53:11 -05:00
Virtually Nick
818b5f79df
GUACAMOLE-1538: Merge add missing documentation for libguac-terminal. 2023-01-04 15:51:05 -05:00
Mike Jumper
8ef60bfa9d GUACAMOLE-1538: Document parameters of libguac-terminal handlers. 2023-01-04 12:40:21 -08:00
Mike Jumper
d90e0e97fe GUACAMOLE-1538: Add missing documentation for libguac-terminal functions. 2023-01-04 12:22:02 -08:00
Mike Jumper
ec7964e8fb GUACAMOLE-1538: Return number of bytes written for guac_terminal_write() and guac_terminal_printf(). 2023-01-04 12:05:02 -08:00
James Muehlner
add7ce361b Merge 1.5.0 changes back to master. 2022-11-29 03:46:57 +00:00
James Muehlner
7d16f67d6d
GUACAMOLE-1293: Merge fix for double acquisition/release of rwlock. 2022-11-28 14:31:44 -08:00
Mike Jumper
e3adb97085 GUACAMOLE-1293: Do not re-acquire __users_lock while already held for writing.
Per POSIX spec, the behavior of acquiring a read lock on a rwlock that's
already acquired for writing is undefined. From the documentation for
pthread_rwlock_rdlock():

"... Results are undefined if the calling thread holds a write lock on
rwlock at the time the call is made."
2022-11-28 13:37:41 -08:00
Mike Jumper
55941823ec Merge 1.5.0 changes back to master. 2022-11-25 23:24:08 -08:00
Mike Jumper
07acce8a76
GUACAMOLE-1293: Merge support for notifying when a user has joined/left a connection. 2022-11-25 23:23:13 -08:00
Virtually Nick
5b1677f21a GUACAMOLE-1293: Fix copy-pasta and style issues; add user ID to information passed to client. 2022-11-25 21:57:44 -05:00
Virtually Nick
623c398005 GUACAMOLE-1293: Change new user info member to simply "name" to clarify its purpose. 2022-11-24 18:13:06 -05:00
Virtually Nick
aa92239edd GUACAMOLE-1293: Rename new enum values to be more consistent with existing code. 2022-11-08 07:45:38 -05:00
Virtually Nick
897712c743 GUACAMOLE-1293: Update and add debug logging. 2022-11-08 07:45:38 -05:00
Virtually Nick
02b24d0101 GUACAMOLE-1293: Simplify the assignment of strings/constants. 2022-11-08 07:45:38 -05:00
Virtually Nick
26eadc37a3 GUACAMOLE-1293: Move to status code plus arguments for msg instruction. 2022-11-08 07:45:38 -05:00
Virtually Nick
6d7156bc70 GUACAMOLE-1293: Update struct member that stores human-readable name. 2022-11-08 07:45:38 -05:00
Virtually Nick
6312e1720d GUACAMOLE-1293: Add support for notifying owner of users joining and leaving. 2022-11-08 07:45:38 -05:00
Virtually Nick
cb7ae25177 GUACAMOLE-1293: Add support for the name handshake instruction. 2022-11-08 07:45:38 -05:00
Virtually Nick
a4adb3f5c0 GUACAMOLE-1293: Add protocol support for msg instruction. 2022-11-08 07:45:38 -05:00
Dan Fandrich
5cf408ebbb GUACAMOLE-1714: Adapt to const parameters of ffmpeg 5.0. 2022-11-07 12:16:35 -08:00
Mike Jumper
3ca6bb0a61
GUACAMOLE-1708: Merge correction to missing Czech keyboard character mapping. 2022-11-06 09:05:52 -08:00
Max
457a169c49 GUACAMOLE-1708: Added Czech keyboard keymap fix missing char 2022-11-06 12:17:21 +01:00
Mike Jumper
bad381cebe
GUACAMOLE-1708: Merge RDP support for Czech keyboard layout. 2022-11-05 08:56:56 -07:00
Max
6171da6d0b GUACAMOLE-1708: Added Czech keyboard keymap for RDP 2022-11-01 21:56:23 +01:00
Mike Jumper
067f2a91a0
GUACAMOLE-1682: Merge automatic newline normalization of terminal clipboard. 2022-10-18 12:41:48 -07:00
Alex Leitner
bc52485570 GUACAMOLE-1682: Normalize conflicting newline encodings in clipboards between Linux and Windows systems for ssh sessions. 2022-10-18 19:38:56 +00:00
Mike Jumper
b20afa275a
GUACAMOLE-1669: Merge fix for RSA key upgrade failure if FIPS mode is enabled. 2022-09-13 14:45:55 -07:00
James Muehlner
b096e47f57 GUACAMOLE-1669: Include ext-info-c in preferred KEX algorithms to ensure RSA key upgrades can happen. 2022-09-13 21:39:38 +00:00
Mike Jumper
4d211e0c9e
GUACAMOLE-1674: Merge changes removing NLA from negotiation if FIPS is enabled. 2022-09-08 09:44:47 -07:00
James Muehlner
dffbeac57a GUACAMOLE-1674: Warn about NLA mode if FIPS mode is enabled, or disable if possible. 2022-08-30 23:23:56 +00:00
Mike Jumper
0361adc01f
GUACAMOLE-1669: Merge FIPS support for SSH connections. 2022-08-24 15:29:46 -07:00
James Muehlner
1971a9dad2 GUACAMOLE-1669: Prefer FIPS compliant ciphers and algorithms when FIPS mode is enabled. 2022-08-24 22:23:46 +00:00
Virtually Nick
5dbf4820ab Merge 1.5.0 changes back to master. 2022-08-19 15:48:51 -04:00
Virtually Nick
b2eb13a178
GUACAMOLE-1540: Merge correct automated retrieval of Docker build dependencies. 2022-08-19 15:30:31 -04:00
Michael Jumper
2bc9d5ff01 GUACAMOLE-1540: Correct regex stripping of package version (major number may have multiple digits). 2022-08-19 12:12:29 -07:00
Michael Jumper
5918cc9f7c GUACAMOLE-1540: Ignore failures to find packages associated with libraries we build ourselves. 2022-08-19 12:12:29 -07:00
James Muehlner
15f6e9f678 Merge 1.5.0 changes back to master. 2022-08-16 18:48:31 +00:00
James Muehlner
b5addfe3da
GUACAMOLE-1540: Merge Alpine Linux docker base image with manual library builds. 2022-08-16 09:40:45 -07:00
Michael Jumper
7f4246b6d5 GUACAMOLE-1540: Manual build all core protocol libraries for Docker image using Alpine Linux base. 2022-08-16 08:39:54 -07:00
Virtually Nick
6ab82446bb
GUACAMOLE-1652: Merge only call SSL init functions when the library version requires it. 2022-07-30 07:36:37 -04:00
James Muehlner
9c93337d97 GUACAMOLE-1652: Migrate OpenSSL initialization to modern methods for OpenSSL >= 1.1.0. 2022-07-30 02:24:31 +00:00
James Muehlner
cdee93ae25 GUACAMOLE-1652: Only call SSL init functions when the library version requires it. 2022-07-30 02:22:36 +00:00
Mike Jumper
eee3ac092c
GUACAMOLE-1622: Merge correction to terminal resize regression. 2022-07-13 16:20:19 -07:00
Alex Leitner
5bb56ed5ba GUACAMOLE-1622: Restructured code to resolve scrollbar resizing bug where the scrollbar would clip off the side of the terminal. This fix also resolves the issue where the text would blur at certain intervals of resizing the window. 2022-07-13 21:57:47 +00:00
Mike Jumper
0aae5eeadb
GUACAMOLE-1636: Merge corrections to typos within RDP comments/documentation. 2022-07-13 13:55:05 -07:00
Jimmy
6d994db9d2 GUACAMOLE-1636: Fix a typo mistake invokved. 2022-07-13 23:47:13 +03:00
Jimmy
cba5484be0 GUACAMOLE-1636: Fix a typo mistake recieved. 2022-07-13 23:41:42 +03:00
Jimmy
4048dd4900 GUACAMOLE-1636: Fix a typo mistake assicated. 2022-07-13 23:32:12 +03:00
Jimmy
98556fbe2e GUACAMOLE-1636: Fix a typo mistake coordinare. 2022-07-13 23:24:06 +03:00
Jimmy
f438a36612 GUACAMOLE-1636: Fix a typo mistake synchonize. 2022-07-13 23:17:50 +03:00
Jimmy
e8d966aec6 GUACAMOLE-1636: Fix a typo mistake Versoin. 2022-07-13 23:10:36 +03:00
Jimmy
523532a52d GUACAMOLE-1636: Fix a typo mistake offscren. 2022-07-13 23:02:37 +03:00
Mike Jumper
51c640fdbd
GUACAMOLE-1436: Merge addition of missing FreeRDP winpr headers. 2022-07-05 12:09:00 -07:00
Virtually Nick
4cf1bfae0e
GUACAMOLE-377: Merge update unit tests for new prototype of guac_protocol_send_sync(). 2022-07-05 14:30:57 -04:00
Michael Jumper
9642afc468 GUACAMOLE-377: Update unit tests for new prototype of guac_protocol_send_sync().
The new guac_protocol_send_sync() requires an additional parameter: the
number of logical frames associated with the sync.
2022-07-05 10:58:38 -07:00
James Muehlner
ffb6c809be
GUACAMOLE-1622: Merge addition of margins to ssh sessions. 2022-06-22 09:32:00 -07:00
Alex Leitner
64ea9c4d1f GUACAMOLE-1622: Clarified comments to describe if param is a pointer. 2022-06-21 16:16:52 +00:00
Alex Leitner
a5834fd319 GUACAMOLE-1622: Separated logic into single responsibility functions. 2022-06-16 17:09:41 +00:00
Alex Leitner
1e9cd9137b GUACAMOLE-1622: Added margins to ssh sessions. 2022-06-15 16:59:20 +00:00
James Muehlner
d4cd9b3e3a
GUACAMOLE-377: Merge support for RemoteFX. 2022-06-09 17:41:23 -07:00
Michael Jumper
31f1b2c7c4 GUACAMOLE-377: Rename single-letter "e" event arguments variable to "args" for readability. 2022-06-09 09:02:11 -07:00
Michael Jumper
ce27936ed5 GUACAMOLE-377: Add frame boundaries around cursor set operations if otherwise absent. 2022-06-09 09:02:11 -07:00
Michael Jumper
b7f05b9e4f GUACAMOLE-377: Ensure backing surface of underlying FreeRDP GDI implementation is resized when desktop is resized. 2022-06-09 09:02:11 -07:00
Michael Jumper
d5761ad625 GUACAMOLE-377: Warn about required color depth only if actually overridden. 2022-06-09 09:02:11 -07:00
Michael Jumper
b26f9d64d6 GUACAMOLE-377: Clarify usage of EndPaint to detect frames. 2022-06-09 09:02:11 -07:00
Michael Jumper
da80163e24 GUACAMOLE-377: Enable graphics pipeline extension by default. 2022-06-09 09:02:11 -07:00
Michael Jumper
28396ae345 GUACAMOLE-377: Expect explicit RDP frame boundaries only after at least one frame boundary has been received. 2022-05-18 15:43:54 -07:00
Michael Jumper
a0e9f6ed9b GUACAMOLE-377: Leverage client timestamp tracking for RDP frame duration. 2022-05-18 15:43:54 -07:00
Michael Jumper
bde8cdee46 GUACAMOLE-377: Add general RDP support for frame markers. 2022-05-18 15:43:54 -07:00
Michael Jumper
669e02b4dc GUACAMOLE-377: Leverage RDPGFX to report remote frame statistics to the client. 2022-05-12 13:50:20 -07:00
Michael Jumper
52c8683bcf GUACAMOLE-377: Add protocol-level support for reporting remote frame statistics. 2022-05-12 13:50:20 -07:00
Michael Jumper
c19eab9691 GUACAMOLE-377: Revise processing lag calculations to consider cumulative processing lag. 2022-05-12 13:50:20 -07:00
Michael Jumper
dd85c54961 GUACAMOLE-377: Add handling for EndPaint required by software GDI implementation of RDPGFX. 2022-05-12 13:50:20 -07:00
Michael Jumper
c795bf9e4a GUACAMOLE-377: Control RemoteFX / GFX support with "enable-gfx" parameter. 2022-05-12 13:50:20 -07:00
Michael Jumper
c469300941 GUACAMOLE-377: Support for RDPGFX. 2022-05-12 13:50:20 -07:00
James Muehlner
81300052e0
GUACAMOLE-1595: Merge mouse mask initialization fix. 2022-05-02 17:24:58 -07:00
Michael Jumper
df4e5c6fdf GUACAMOLE-1595: Ensure all mouse buttons are initially released when terminal starts. 2022-05-03 00:20:08 +00:00
Virtually Nick
a175a3d902
GUACAMOLE-1312: Merge add Canadian French RDP keymap 2022-03-23 09:58:58 -04:00
Kaven Rousseau
9cbd768210 GUACAMOLE-1312: Added fr_ca keymap 2022-03-17 16:13:42 -04:00
Virtually Nick
c716a07abc Merge 1.5.0 changes back to master. 2022-03-17 15:24:48 -04:00
Virtually Nick
c880f02fe8
GUACAMOLE-1115: Merge ensure RDP print process does not block itself from completing. 2022-03-17 15:20:51 -04:00
Michael Jumper
ce88fa4d4a GUACAMOLE-1115: Forcibly kill any outstanding PDF filter job when cleaning up resources. 2022-03-17 18:35:38 +00:00
Michael Jumper
d734bac590 GUACAMOLE-1115: Do not hold general RDP message lock while waiting for print operations.
Holding the message lock will block handling of things like mouse and
keyboard events, as the message lock must be acquired before sending the
corresponding messages to the RDP server. If mouse and keyboard events
are blocked, then handling of further Guacamole instructions like "ack"
is also blocked. If the print job is blocked until an "ack" is received,
this results in deadlock.
2022-03-17 18:35:20 +00:00
Mike Jumper
75a11b05b2
GUACAMOLE-1543: Merge changes moving recording structures/functions to the public API. 2022-03-01 09:58:50 -08:00
James Muehlner
854b5ecbb8 GUACAMOLE-1543: Move recording functionality from common to libguac. 2022-03-01 04:01:44 +00:00
Mike Jumper
d8073f9b17
GUACAMOLE-1538: Merge corrections to libguac-terminal build and scope. 2022-02-28 16:56:04 -08:00
James Muehlner
46e813343e GUACAMOLE-1538: Only the core functionality of the terminal lib should be public. 2022-03-01 00:33:55 +00:00
James Muehlner
ad0155b5f5 GUACAMOLE-1538: Make it clear which functions are getters by adding _get_ to the name of each. 2022-02-24 12:02:36 -08:00
James Muehlner
0856e94ece GUACAMOLE-1538 Use dashes instead of underscores in filenames for consistency with libguac public API. 2022-02-24 11:12:05 -08:00
James Muehlner
1c97cdb115 GUACAMOLE-1538: Autogenerate docs for new library. 2022-02-23 13:49:32 -08:00
James Muehlner
ce2ffdf75f GUACAMOLE-1538: Improve code style and cleanliness. 2022-02-22 20:37:42 -08:00
James Muehlner
6dd33a8d90 GUACAMOLE-1538: Do not use terminal internals outside of terminal code. 2022-02-22 16:06:48 -08:00
James Muehlner
589e0ecd03 GUACAMOLE-1538 - Consolidate clipboard handling; opaque clipboard struct to avoid exposing internal guac_common_clipboard. 2022-02-22 14:04:47 -08:00
James Muehlner
037045a054 GUACAMOLE-1538: Explicitly include the common lib; ensure no undefined symbols. 2022-02-22 11:07:30 -08:00
James Muehlner
63bf5b329c GUACAMOLE-1538: Rename library to match conventions. 2022-02-22 09:41:08 -08:00
Mike Jumper
dc9dfe562f
GUACAMOLE-1540: Merge changes correcting Docker-specific search for FreeRDP install location. 2022-02-21 17:32:23 -08:00
James Muehlner
757928dfa1 GUACAMOLE-1540: Search for libfreerdp2 installation directly instead of checking links. 2022-02-21 16:57:48 -08:00
Virtually Nick
a0faa02616
GUACAMOLE-1538: Merge refactor libguac_terminal for easier extensibility, and migrate to shared library. 2022-02-21 14:30:40 -05:00
James Muehlner
44d76da21a GUACAMOLE-1538: Use an options struct instead of hardcoding params in constructor. 2022-02-21 11:27:20 -08:00
James Muehlner
05c6131cf3 GUACAMOLE-1538: Update libguac_terminal to be a shared library. 2022-02-21 11:27:13 -08:00
Mike Jumper
001347b4e8
GUACAMOLE-1540: Merge migration of guacd Docker image to Ubuntu 21.10. 2022-02-18 16:20:17 -08:00
James Muehlner
edce11fcb4 GUACAMOLE-1540: Build using Ubuntu 21.10 as a base instead of buster-slim. 2022-02-18 15:48:18 -08:00
James Muehlner
e78d589e25
GUACAMOLE-876: Merge null-check fix for RDP open file check. 2022-02-18 13:50:03 -08:00
Michael Jumper
06461cac53 GUACAMOLE-876: Test for open files only if filesystem has been allocated. 2022-02-18 13:40:15 -08:00
Virtually Nick
8e94b00587
GUACAMOLE-1495: Merge add keymap for Polish keyboard layout for RDP 2022-02-17 12:46:01 -05:00
Virtually Nick
9245d02bc5
GUACAMOLE-462: Merge create recordings/typescripts with group read permission. 2022-02-17 12:43:51 -05:00
Michael Jumper
4d41b38a24 GUACAMOLE-462: Create recordings/typescripts with group read permission.
Previously, all recordings/typescripts were strictly readable by the
service user that created them (guacd). This prevents reading by other
services like the Guacamole web application. Instead,
recordings/typescripts should at least be group-readable.
2022-02-17 09:25:28 -08:00
Mike Jumper
29535e6cb8
GUACAMOLE-876: Merge changes deferring reconnect-to-resize until active transfers are complete. 2022-02-02 09:56:41 -08:00
Virtually Nick
10126444bf GUACAMOLE-876: Avoid disrupting open files and active print jobs to update display. 2022-02-01 21:45:10 -05:00
Mike Jumper
23612720ce
GUACAMOLE-745: Merge support for OpenSSH-format private keys / Ed25519. 2022-01-12 11:38:12 -08:00
Joshua Roys
f84db7d166 GUACAMOLE-745: Support OpenSSH private keys & ED25519
Let libssh2 parse PEM and ssh-native keys. Requires libssh2 1.9.0+
compiled against a crypto backend supporting ed25519.
2022-01-12 09:02:11 -05:00
ClassicGOD
a1d0d0aea4
GUACAMOLE-1495: add entry for pl_pl_qwerty
Add pl_pl_qwerty.keymap to rdp_keymaps
2022-01-10 20:31:41 +01:00
ClassicGOD
534158c1cb
GUACAMOLE-1495: add pl_pl_qwerty keymap
Add keymap file for Polish keyboard layout
2022-01-10 20:28:11 +01:00
Mike Jumper
56dca9880d
GUACAMOLE-1435: Merge correction to FreeRDP plugin entrypoint return type. 2022-01-03 21:59:57 -08:00
Virtually Nick
703f258b06 GUACAMOLE-1435: Correctly return UINT for DVCPluginEntry 2022-01-03 20:15:11 -05:00
Virtually Nick
bce1d2a434 GUACAMOLE-1436: Add winpr file.h dependencies as required. 2021-12-27 09:42:57 -05:00
Virtually Nick
084ddaf81f Merge 1.4.0 changes back to master. 2021-12-25 10:21:40 -05:00
Virtually Nick
be9041fefd
GUACAMOLE-478: Merge add clipboard line ending normalization option for RDP. 2021-12-25 10:18:39 -05:00
Michael Jumper
09bd4af77e GUACAMOLE-478: Add optional clipboard line ending normalization for RDP. 2021-12-25 00:31:17 -08:00
Michael Jumper
7472310a03 GUACAMOLE-478: Implement encoding translation functions for normalizing newline sequences. 2021-12-25 00:07:47 -08:00
Virtually Nick
27db57f704 Merge 1.4.0 changes back to master. 2021-12-24 19:24:36 -05:00
Virtually Nick
1f6f45e6e9
GUACAMOLE-1190: Merge explicitly use "localhost" as guacd default bind host, matching default of webapp. 2021-12-24 19:23:53 -05:00
Michael Jumper
e3e75464fb GUACAMOLE-1190: Explicitly use "localhost" as guacd default bind host, matching default of webapp. 2021-12-24 15:45:28 -08:00
Virtually Nick
90322cd4d3 Merge 1.4.0 changes back to master. 2021-12-19 22:17:47 -05:00
Virtually Nick
b9cc76058b
GUACAMOLE-1047: Merge notify connecting client of invalid connection IDs. 2021-12-19 18:39:15 -05:00
Michael Jumper
daa052398e GUACAMOLE-1047: Remove unnecessary use of snprintf() in favor of guacd_log(). 2021-12-18 15:13:10 -08:00
Virtually Nick
6760974912 Merge 1.4.0 changes back to master. 2021-12-11 07:44:56 -05:00
Virtually Nick
e84317ff86
GUACAMOLE-1411: Merge bump version numbers to 1.4.0. 2021-12-11 07:44:19 -05:00
Michael Jumper
a1a758f13c GUACAMOLE-1411: Update libtool version info for libguac (interfaces added and changed). 2021-12-10 23:55:16 -08:00
Michael Jumper
8dc92e69ca GUACAMOLE-1411: Bump version numbers to 1.4.0. 2021-12-10 23:51:34 -08:00
Virtually Nick
73ac4fcdbe
GUACAMOLE-1330: Merge dynamically allocate AVPacket when possible 2021-11-08 18:47:03 -05:00
Michael Jumper
bc6b5cef25 GUACAMOLE-1330: Dynamically allocate AVPacket when supported (static allocation deprecated as of libavcodec 58.133.100). 2021-11-08 15:24:36 -08:00
Virtually Nick
491be8382a
GUACAMOLE-1416: Merge fix unreleased terminal lock in ssh_client_thread 2021-09-10 10:40:10 -04:00
ycaibb
329171a950 GUACAMOLE-1416: Fix unreleased the lock in the ssh_client_thread
GUACAMOLE-1416: Fix unreleased the lock ssh_client->term_channel_lock in the ssh_client_thread.
2021-09-10 22:26:32 +08:00
Virtually Nick
12b8eac514
GUACAMOLE-1388: Merge ensure RDP-specific resources are cleaned up after channel disconnect. 2021-07-29 19:54:35 -04:00
Michael Jumper
2524af80a9 GUACAMOLE-1388: Ensure RDP-specific resources are cleaned up after channel disconnect.
Without these changes, RDP-specific resources like the CLIPRDR and RDPEI
channels may remain from past connections if the RDP connection is
dynamically reconnected via the "Reconnect" display resize method,
resulting in assertion failures or memory errors if those stale
resources are reused after reconnect is completed.
2021-07-28 15:50:18 -07:00
Virtually Nick
91d79da49f
GUACAMOLE-1386: Merge add proper RDP mapping of "Meta" ("Windows") key. 2021-07-27 09:51:48 -04:00
Michael Jumper
eb52b4e258 GUACAMOLE-1386: Add proper RDP mapping of "Meta" ("Windows") key. 2021-07-26 19:59:12 -07:00
Mike Jumper
278745d6d8
GUACAMOLE-1064: Merge Norwegian keyboard layout for RDP. 2021-06-02 22:34:29 -07:00
Øyvind Harboe
910c87bda0 GUACAMOLE-1064: add Norwegian keyboard
Tested on top of Guacamole 1.3.0

The following works beyond a simple smoke-test:

- æøå
- \
- |
- dead acute á
- dead grave à
- dead umlaut ö
- dead cirumflex ê
- dead tilde ~
2021-06-03 07:02:05 +02:00
Mike Jumper
31b4246e18
GUACAMOLE-1350: Merge corrections for defined but unused leave_handlers. 2021-05-25 11:44:35 -07:00
Jimmy
a91c4b3869 GUACAMOLE-1350: Add code to join leave_handler when connecting in other protocols. 2021-05-25 02:03:07 +03:00
Jimmy
26d87aa5d3 GUACAMOLE-1350: Add code to join leave_handler when connecting in rdp. 2021-05-24 01:11:28 +03:00
Mike Jumper
44145f681a
GUACAMOLE-1215: Merge correction for escaping of backslashes in JSON strings. 2021-05-17 12:25:23 -07:00
Virtually Nick
0182de6d18 GUACAMOLE-1215: Add backslash to list of JSON-escaped characters. 2021-05-17 14:15:38 -04:00
Mike Jumper
df25aa9fdc
GUACAMOLE-1276: Merge correction for 32-bit truncation regression affecting RDP uploads. 2021-05-14 10:51:13 -07:00
Virtually Nick
68fc594247 GUACAMOLE-1276: Correct file upload offset type. 2021-05-14 09:19:28 -04:00
Virtually Nick
a6a7e8ac26
GUACAMOLE-1201: Merge throttle outbound audio data to avoid overflowing RDP server audio input buffer. 2021-05-01 22:10:11 -04:00
Michael Jumper
189d8fab30 GUACAMOLE-1201: Throttle outbound audio data to avoid overflowing RDP server audio input buffer.
The RDP specification for the AUDIO_INPUT channel requires that all
audio be sent in packets of a specific size. Guacamole does correctly
limit itself to sending packets of this size to the RDP server, but will
send quite a few of these packets all at once if it has received more
audio data than the RDP packet size. This is OK in principle (the
Guacamole client should be able to send audio in packets of whatever
size it chooses), but may overwhelm the software running within the RDP
server if the amount of data received exceeds the available buffer
space, resulting in dropped samples.

As there is no way to know the size of the remote audio buffer, we need
to instead ensure that audio is streamed as close to real time as
possible, with each audio packet of N bytes not being sent until roughly
the amount of time represented by those N bytes has elapsed since the
last packet. This throttling ensures that software expecting to process
audio in real time should never run out of buffer space.

That said, if we never exceed the per-packet data rate and occasionally
send a packet earlier than real time would dictate, unavoidable latency
in sending/receiving audio data would accumulate over time. For example,
if each audio packet represents 10ms of audio data, but we receive that
audio packet 10.1ms after the previous packet, we need to adjust the
timing of the next audio packet(s) to account for that additional 0.1ms.
Simply waiting 10ms after sending each packet would cause that 0.1ms to
accumulate each time it occurs, eventually resulting in noticable
latency and finally running out of buffer space.

Thus, these changes:

1. Leverage a flush thread and per-packet scheduling to ensure that each
   flushed audio packet does not exceed the equivalent real time rate.
2. Calculate the amount of additional latency from the amount of data
   received beyond the required packet size, and amortize scheduling
   corrections to account for that latency over the next several audio
   packets.

This ensures that audio is streamed exactly as it is received if the
audio matches the packet size of the RDP server, and audio that is
received in a different size or varying sizes is buffered and throttled
to keep things within the expectations of software running within the
RDP server.
2021-05-01 16:23:32 -07:00
Virtually Nick
e90e438cf6
GUACAMOLE-1283: Merge add synchronization around absolutely all outbound RDP messages. 2021-04-17 13:11:32 -04:00
Michael Jumper
bf6922b31e GUACAMOLE-1283: Remove redundant parameters from guac_rdp_audio_buffer callback.
The buffer and data parameters of the guac_rdp_audio_buffer flush
handler are redundant now that the guac_rdp_audio_buffer is being passed
to the handler. They can instead be referenced as audio_buffer->packet
and audio_buffer->data respectively.
2021-04-13 17:47:56 -07:00
Michael Jumper
27e762d06f GUACAMOLE-1283: Add synchronization around absolutely all outbound RDP messages.
The FreeRDP library is intended to be threadsafe, but is not reliably so
with respect to legacy RDP encryption and outbound messages. When
outbound messages are sent by multiple threads, the encryption key used
for legacy RDP encryption may not be updated correctly, resulting in a
fatal connection error like:

"ERRINFO_DECRYPT_FAILED (0x00001192):(a) Decryption using Standard RDP
Security mechanisms (section 5.3.6) failed. (b) Session key creation
using Standard RDP Security mechanisms (section 5.3.5) failed."
2021-04-08 15:43:15 -07:00
Virtually Nick
b2ae2fdf00
GUACAMOLE-1307: Merge use VerifyCertificateEx callback if supported. 2021-03-10 14:38:43 -05:00
Michael Jumper
1b78f611d3 GUACAMOLE-1307: Use VerifyCertificateEx callback if supported. 2021-03-09 22:53:11 -08:00
James Muehlner
e2a136f41e
GUACAMOLE-1302: Merge support for forcing lossless compression in VNC and RDP connections. 2021-03-03 19:31:25 -08:00
Michael Jumper
18a0362dab GUACAMOLE-1302: Add RDP support for forcing lossless compression. 2021-03-03 19:29:14 -08:00
Michael Jumper
c2b7e2d039 GUACAMOLE-1302: Add VNC support for forcing lossless compression. 2021-03-03 19:29:14 -08:00
Michael Jumper
27f8403178 GUACAMOLE-1302: Always use lossless compression for text-based protocols leveraging the terminal. 2021-03-03 19:29:14 -08:00
Michael Jumper
0729a6cc73 GUACAMOLE-1302: Add surface/display level support for forcing lossless compression. 2021-03-03 19:29:14 -08:00
Virtually Nick
d9d5c79644
GUACAMOLE-1305: Merge fix pt-br keyboard layout 2021-03-03 16:19:20 -05:00
Higor Cavalcanti
650e7ad90f GUACAMOLE-1305: Fix pt-br keyboard layout. Key being recognized as right shift. 2021-03-03 16:49:59 -03:00
Virtually Nick
7bbab0efdd
GUACAMOLE-1174: Merge correct handling of truncated parameters when appending to URLs. 2021-02-21 20:32:47 -05:00
Michael Jumper
a920932703 GUACAMOLE-1174: Correct logic detecting truncation of appended parameter.
The previous implementation passed `length - str_len` to `snprintf()`,
yet compared the return value to `length`. This is incorrect, as
`length` is not the buffer size provided to `snprintf()`.
2021-02-21 15:05:53 -08:00
Michael Jumper
c7935736da GUACAMOLE-1174: Add unit tests for URL utility functions. 2021-02-21 15:05:53 -08:00
Michael Jumper
7f55399304 GUACAMOLE-1174: Clarify behavior of URL parameter appending function. 2021-02-21 14:15:17 -08:00
Mike Jumper
5428ac5057
GUACAMOLE-1174: Merge support for Kubernetes "exec" API call. 2021-02-21 11:09:53 -08:00
Tomer Gabel
a8cf250c98 GUACAMOLE-1047: Changed returned status code per review 2021-02-14 16:21:54 +02:00
James Muehlner
ca1fbd5e98
GUACAMOLE-1204: Merge addition of server-side support for multi-touch events. 2021-02-11 20:53:22 -08:00
Michael Jumper
d16ba33dee GUACAMOLE-1204: Add support for including touch events within session recordings. 2021-02-11 20:12:21 -08:00
Michael Jumper
5eb2867733 GUACAMOLE-1204: Add RDP support for multi-touch events via RDPEI channel. 2021-02-11 20:12:21 -08:00
Michael Jumper
048a59310b GUACAMOLE-1204: Add support for declaring layer multi-touch capabilities. 2021-02-11 20:12:21 -08:00
Michael Jumper
c88c0d1c89 GUACAMOLE-1204: Add libguac support for processing the "touch" instruction. 2021-02-11 20:12:21 -08:00
Virtually Nick
4a38d39694
GUACAMOLE-1277: Merge unswap - and _ on fr-be-azerty keymap 2021-02-02 15:54:12 -05:00
Sander Vanheule
841dc28e9b GUACAMOLE-1277: Unswap -/_ on fr-be-azerty keymap
When using the fr-be-azerty remote keyboard layout on an RDP connection,
the dash ('-') and underscore ('_') are swapped.

Underscore and dash are located on the same key on a Belgian azerty
layout. Dash should be the normal/unshifted character, and underscore
should be the shifted character. The current mapping has this the other
way around, so let's fix this.

Signed-off-by: Sander Vanheule <Sander.Vanheule@UGent.be>
2021-02-02 19:53:51 +01:00
Mike Jumper
c122a5f14a
GUACAMOLE-1133: Merge build-time sanity check for libvncserver usage of gcrypt. 2021-01-24 14:15:35 -08:00
Nick Couchman
5cee64514f GUACAMOLE-1133: Add gcrypt build dependency for Docker image. 2021-01-23 22:16:58 -05:00
Nick Couchman
b40b7e7bf6 GUACAMOLE-1133: Add build check for headers when libvncserver includes gcrypt support. 2021-01-23 22:04:38 -05:00
Mike Jumper
6d526cb60f
GUACAMOLE-1133: Merge addition of GCrypt initialization to VNC startup process. 2021-01-22 21:33:10 -08:00
Nick Couchman
46bed49a43 GUACAMOLE-1133: initialize GCrypt in VNC protocol prior to client start-up. 2021-01-21 21:14:18 -05:00
Virtually Nick
c769d18cf6
GUACAMOLE-1263: Merge mark freed memory as freed prior to calling rfbClientCleanup(). 2021-01-15 14:51:36 -05:00
Michael Jumper
612c5eba20 GUACAMOLE-1263: Mark freed memory as freed prior to calling rfbClientCleanup().
Older versions of libvncclient did not free all memory within
rfbClientCleanup(), but this has been corrected as of their 0.9.12
release. As guacamole-server may well be built against older versions of
libvncclient, we can't simply remove the manual free() calls, but we
should be sure to set any memory that we free ourselves to NULL so that
rfbClientCleanup() does not attempt to free it again.
2021-01-15 11:46:16 -08:00
Virtually Nick
fe62300223
GUACAMOLE-1259: Merge include missing config.h header for sake of conditional Stream_Free(). 2021-01-10 13:09:39 -05:00
Michael Jumper
0b6b14b71e GUACAMOLE-1259: Include missing config.h header for sake of conditional Stream_Free().
The changes introduced by GUACAMOLE-1181 (commit 2c86e20) were made
conditional as older versions of FreeRDP will automatically free the
wStream, resulting in a double-free if we attempt to do so ourselves.

The macro controlling that conditional code is defined within config.h,
which is missing here. Without that macro, the call to Stream_Free()
always occurs, and we get a double-free with older FreeRDP.
2021-01-09 21:15:50 -08:00
Virtually Nick
1c3e86dc2c
GUACAMOLE-1191: Merge always disable the glyph cache due to stability issues. 2021-01-09 22:46:12 -05:00
Michael Jumper
9dc793b0e5 GUACAMOLE-1191: Always disable the glyph cache, as FreeRDP no longer considers the feature to be stable. 2021-01-09 17:49:32 -08:00
Virtually Nick
1e6c42594f
GUACAMOLE-1254: Merge add support for libuuid as alternative to OSSP UUID. 2021-01-07 12:21:11 -05:00
Michael Jumper
430182dce2 GUACAMOLE-1254: Add unit tests for unique ID generation. 2021-01-06 22:51:07 -08:00
Michael Jumper
f710e00d26 GUACAMOLE-1254: Use libuuid rather than OSSP UUID if available.
The libuuid library is widely available (part of util-linux) and much
more frequently updated. The OSSP UUID library works great, but was last
updated in 2008 and causes some confusion for users that have libuuid.
2021-01-06 20:58:22 -08:00
Mike Jumper
53f981f864
GUACAMOLE-1245: Merge support for specifying Wake-on-LAN port. 2020-12-30 21:06:13 -08:00
Nick Couchman
a37668e9f5 GUACAMOLE-1245: Add support for specifying Wake-on-LAN port. 2020-12-30 16:50:38 -05:00
Nick Couchman
68c5dd1730 Merge 1.3.0 changes back to master. 2020-12-25 07:31:03 -05:00
Virtually Nick
90e15cb706
GUACAMOLE-1241: Merge avoid double-free when building against FreeRDP development snapshot. 2020-12-25 07:30:06 -05:00
Michael Jumper
9475c0b6fa GUACAMOLE-1241: Build against Debian repository that has non-snapshot version of FreeRDP.
Debian stable currently provides a FreeRDP package that is a snapshot of
commit 2693389a103394e035edc2a01055ca2c9ccccb21, whereas
stable-backports provides a FreeRDP package from release 2.2.0. Only a
release or release candidate can be relied upon for consistent behavior.
2020-12-24 13:15:58 -08:00
Michael Jumper
218f8d36b1 GUACAMOLE-1241: Disable build against FreeRDP development snapshots unless *explicitly* overridden. 2020-12-22 20:53:06 -08:00
Nick Couchman
d20b385834 Merge 1.3.0 changes back to master. 2020-12-22 19:05:16 -05:00
Virtually Nick
b608bb1513
GUACAMOLE-1242: Merge move "connection closed" log message to debug level. 2020-12-22 18:02:35 -05:00
Michael Jumper
3196f9f0d0 GUACAMOLE-1242: Move "connection closed" log message to debug level.
It is expected under normal circumstances that the connection may be
abruptly closed, including during the handshake. For example, this will
commonly occur when a TCP load balancer is performing a simple service
health check.

An message noting that the connection has been closed during the
Guacamole protocol handshake is really only of benefit when debugging,
where that information may provide useful context. If not debugging, the
message amounts to log noise.
2020-12-22 13:00:48 -08:00
Virtually Nick
2fc3a43e87
GUACAMOLE-1225: Merge fix simple typo, verfying -> verifying, in code documentation 2020-12-21 08:00:29 -05:00
Tim Gates
ced24fde7d
GUACAMOLE-1225: fix simple typo, verfying -> verifying
There is a small typo in src/libguac/tests/parser/read.c, src/libguac/tests/socket/fd_send_instruction.c, src/libguac/tests/socket/nested_send_instruction.c.

Should read `verifying` rather than `verfying`.

Fixes https://issues.apache.org/jira/browse/GUACAMOLE-1225
2020-12-21 17:04:52 +11:00
Michael Jumper
c6e8ad7a38 Merge 1.3.0 changes back to master. 2020-12-09 17:29:20 -08:00
Mike Jumper
47c7450f0f
GUACAMOLE-1110: Merge changes adding a guacd service group. 2020-12-09 17:28:17 -08:00
benrubson
4a5a350f59 GUACAMOLE-1110: Create a proper dedicated service group 2020-12-07 10:35:06 +01:00
Virtually Nick
b48e34fc3e
GUACAMOLE-1227: Merge build support for generic VNC credentials only if supported by libvncclient. 2020-12-03 07:14:34 -05:00
Michael Jumper
d40db7cd3c GUACAMOLE-1227: Build support for generic VNC credentials only if supported by libvncclient. 2020-12-02 22:55:16 -08:00
Yaroslav Nikonorov
004f57e19a GUACAMOLE-1174: Add prototype and docstrings for guac_kubernetes_append_endpoint_param function. 2020-11-18 15:30:34 +02:00
Yaroslav Nikonorov
7809447c3f GUACAMOLE-1174: Add parameters to the endpoint path using function guac_kubernetes_append_endpoint_param. 2020-11-18 14:57:59 +02:00
Yaroslav Nikonorov
7a1ba51bae GUACAMOLE-1174: Determine parameter delimiter, compute the buffer string length, fix the buffer string length usage, verify buffer null terminated. 2020-11-18 14:52:24 +02:00
Michael Jumper
9df3e294c3 Merge 1.3.0 changes back to master. 2020-11-03 14:29:52 -08:00
Mike Jumper
7fcddef117
GUACAMOLE-1205: Merge version number bump to 1.3.0. 2020-11-03 14:29:21 -08:00
Nick Couchman
c867d392d0 GUACAMOLE-1205: Update Guacamole Server version numbers for 1.3.0 release 2020-11-03 14:54:55 -05:00
Nick Couchman
8faeea18f8 Merge 1.3.0 changes back to master. 2020-11-03 14:02:38 -05:00
Virtually Nick
c2c67ee34d
GUACAMOLE-221: Merge handle FREERDP_ERROR_* constants only when defined. 2020-11-03 13:54:31 -05:00
Michael Jumper
1e3fc25268 GUACAMOLE-221: Handle FREERDP_ERROR_* constants only when defined.
Only FreeRDP 2.0.0-rc3 and later support all the constants used within
src/protocols/rdp/error.c.
2020-11-03 10:14:51 -08:00
Nick Couchman
299b7b4370 Merge 1.3.0 changes back to master. 2020-11-02 22:17:33 -05:00
Virtually Nick
1e856d4e2d
GUACAMOLE-221: Merge rely on FreeRDP error code if no RDP disconnect reason is available. 2020-11-02 22:16:43 -05:00
Nick Couchman
5124e78e05 Merge 1.3.0 changes back to master. 2020-11-02 20:35:00 -05:00
Virtually Nick
4d3280e817
GUACAMOLE-221: Merge terminate keep-alive thread immediately upon guac_socket_free(). 2020-11-02 20:09:18 -05:00
Michael Jumper
8041585379 GUACAMOLE-221: Increase verbosity of logged FreeRDP-related errors. 2020-11-02 15:40:29 -08:00
Michael Jumper
2a4ecda216 GUACAMOLE-221: Terminate keep-alive thread immediately upon guac_socket_free().
The keep-alive interval is identical to the timed client free used by
guacd. This results in a race condition where there is a random chance
that guacd will assume that the client has failed to terminate in a
timely manner simply because guac_socket_free() is waiting for the
keep-alive thread to finish.
2020-11-02 14:57:43 -08:00
Nick Couchman
e3df475bda Merge 1.3.0 changes back to master. 2020-11-02 16:16:22 -05:00
Virtually Nick
c48feb30f5
GUACAMOLE-221: Merge ensure guacd always enables broadcast socket keep-alive. 2020-11-02 16:00:59 -05:00
Michael Jumper
1e550b58d9 GUACAMOLE-221: Ensure guacd always enables broadcast socket keep-alive. 2020-11-02 12:51:58 -08:00
Michael Jumper
1e8d9d92a5 GUACAMOLE-221: Rely on FreeRDP error code if no RDP disconnect reason is available. 2020-11-02 12:38:06 -08:00
Nick Couchman
7563402631 Merge 1.3.0 changes back to master. 2020-10-30 10:13:25 -04:00
Virtually Nick
0be71a8c67
GUACAMOLE-1182: Merge ensure converted clipboard data is freed after being sent. 2020-10-30 10:12:25 -04:00
Nick Couchman
4e6ad1939f Merge 1.3.0 changes back to master. 2020-10-30 08:34:16 -04:00
Virtually Nick
c7d3814450
GUACAMOLE-1181: Merge rely on automatic freeing of wStream only for FreeRDP 2.0.0-rc3 through 2.0.0-rc4. 2020-10-29 22:23:35 -04:00
Michael Jumper
c1ad6115a2 GUACAMOLE-1181: Warn users if the internal behavior of their version of FreeRDP cannot be tested and may be unreliable. 2020-10-29 18:29:47 -07:00
Michael Jumper
256487c95a GUACAMOLE-1181: Only free wStream after send if FreeRDP requires this. 2020-10-29 18:29:47 -07:00
Michael Jumper
2c86e20ab9 GUACAMOLE-1181: Free wStream after send is complete/cancelled. 2020-10-28 20:23:37 -07:00
Michael Jumper
683ef1722e GUACAMOLE-1182: Ensure converted clipboard data is freed after being sent. 2020-10-28 16:36:50 -07:00
Yaroslav Nikonorov
79239e3be0 GUACAMOLE-1174: Create function for appending endpoint params, fix endpoint params overwriting. 2020-10-20 12:44:38 +03:00
Yaroslav Nikonorov
6b58e2e5a9 GUACAMOLE-1174: Free exec_command setting. 2020-10-20 12:44:38 +03:00
Yaroslav Nikonorov
164f792b86 GUACAMOLE-1174: Remove option use-exec, add snprintf result validation, fix code formatting. 2020-10-08 13:18:58 +03:00
Mike Jumper
2aa0218cc7
GUACAMOLE-1190: Merge guacd support for listening on IPv6 addresses. 2020-10-08 00:32:32 -07:00
Jonas Zeiger
8d683b560c GUACAMOLE-1190: Specify correct address family when creating socket 2020-10-07 10:02:28 +02:00
Virtually Nick
c449d83790
GUACAMOLE-1185: Merge fix typo in RDP audio input comment. 2020-10-03 07:45:38 -04:00
Jimmy
d6a817f58c GUACAMOLE-1185: Fixed one typo mistake. 2020-09-28 10:35:05 +03:00
Michael Jumper
ceabd68e28 Merge 1.3.0 changes back to master. 2020-09-27 16:36:00 -07:00
Mike Jumper
558eb149f4
GUACAMOLE-1031: Merge correction to handling of RDP SFTP upload directory. 2020-09-27 16:34:56 -07:00
Michael Jumper
3e1e1bd268 Merge 1.3.0 changes back to master. 2020-09-20 20:35:26 -07:00
Mike Jumper
aa870debad
GUACAMOLE-221: Merge server-side support for parameter prompting. 2020-09-20 20:31:34 -07:00
Nick Couchman
3e19583b29 GUACAMOLE-221: Switch VNC credentials to NULL when parameter is not passed 2020-09-20 14:41:29 -04:00
Nick Couchman
9e1dada14b GUACAMOLE-221: Add CUnit tests for guac_strdup() 2020-09-20 14:41:29 -04:00
Nick Couchman
3b4007c9fa GUACAMOLE-221: Tweak logic for when RDP domain is requested. 2020-09-20 14:41:29 -04:00
Nick Couchman
bfb54f72a0 GUACAMOLE-221: Clean up libguac, protocol changes, and documentation. 2020-09-20 14:41:29 -04:00
Nick Couchman
6605f217c5 GUACAMOLE-221: Rollback changes to SSH protocol prompting 2020-09-17 21:12:33 -04:00
Nick Couchman
b6d3edb749 GUACAMOLE-221: Move VNC and RDP argv to new callback. 2020-09-17 21:12:33 -04:00
Nick Couchman
f70fdfc612 GUACAMOLE-221: Add back in SSH credential argv support; fix style and comments. 2020-09-15 14:48:15 -04:00
Nick Couchman
ec3cdfd17b GUACAMOLE-221: We need to flush the socket after sending required. 2020-09-15 14:48:15 -04:00
Nick Couchman
26b9850d87 GUACAMOLE-221: Remove bad rebase code. 2020-09-15 14:48:15 -04:00
Nick Couchman
98dbf15d0b GUACAMOLE-221: Fix up SSH terminal prompt fallback. 2020-09-15 14:48:15 -04:00
Nick Couchman
e8feeabfef GUACAMOLE-221: Implement CUnit tests for protocol version comparison and conversion. 2020-09-15 14:48:15 -04:00
Nick Couchman
0db61198e9 GUACAMOLE-221: Fix up lots of comments, streamline code, and fix SSH mutex lock. 2020-09-15 14:48:15 -04:00
Nick Couchman
bc8ed4e104 GUACAMOLE-221: Implement guacd support for verifying that client can accept the required instruction. 2020-09-15 14:48:15 -04:00
Nick Couchman
b00b629b96 GUACAMOLE-221: Clean up VNC mutex; update comments. 2020-09-15 14:48:15 -04:00
Nick Couchman
0761908a77 GUACAMOLE-221: Sockets start keep alive by default. 2020-09-15 14:48:15 -04:00
Nick Couchman
c579e7337f GUACAMOLE-221: Implement function for sending required to client owner. 2020-09-15 14:48:15 -04:00
Nick Couchman
5ec2551761 GUACAMOLE-221: Use constants for parameters updated via argv or required instructions. 2020-09-15 14:48:15 -04:00
Nick Couchman
5881209f12 GUACAMOLE-221: Move keep-alives to protocol implementation and only send required instruction to owner. 2020-09-15 14:48:15 -04:00
Nick Couchman
5c309f5cb1 GUACAMOLE-221: Move away from reserved function names. 2020-09-15 14:48:15 -04:00
Nick Couchman
7759f9b1c0 GUACAMOLE-221: Add socket keepalive when sending required fields. 2020-09-15 14:48:15 -04:00
Nick Couchman
51b9c9c103 GUACAMOLE-221: Remove manual addition of null terminator 2020-09-15 14:48:15 -04:00
Nick Couchman
4318083511 GUACAMOLE-221: Fix up style, comments, and variable names. 2020-09-15 14:48:15 -04:00
Nick Couchman
939d954810 GUACAMOLE-221: Extract array writing in protocol into common function and document. 2020-09-15 14:48:15 -04:00
Nick Couchman
76ef6332cc GUACAMOLE-221: Make lock, condition, and flags specific to credentials. 2020-09-15 14:48:15 -04:00
Nick Couchman
7369bed22c GUACAMOLE-221: Add support for sending multiple params in required. 2020-09-15 14:48:15 -04:00
Nick Couchman
21a5d9ee62 GUACAMOLE-221: Add protocol functions for sending prompt to client. 2020-09-15 14:48:15 -04:00
Yaroslav Nikonorov
7683a17d69 GUACAMOLE-1174: Added exec call implementation for kubernetes protocol 2020-09-10 19:59:03 +03:00
Virtually Nick
382d72a26a
GUACAMOLE-1158: Merge handle received clipboard data only if copy has not been disabled. 2020-08-24 15:47:42 -04:00
Michael Jumper
df33cd0874 GUACAMOLE-1158: Handle received clipboard data only if copy has not been disabled. 2020-08-24 12:23:01 -07:00
Mike Jumper
be01e7e93a
GUACAMOLE-1151: Merge exclusion of NetBeans project directory via .gitignore. 2020-08-08 23:45:46 -07:00
Nick Couchman
7ee9c64c04 GUACAMOLE-1151: Add .gitignore entry for NetBeans project directory 2020-08-05 16:43:43 -04:00
Virtually Nick
a2fb09021b
GUACAMOLE-221: Merge add convenience API for automatically handling received "argv" streams. 2020-07-08 08:30:47 -04:00
Michael Jumper
0cdc51acd1 GUACAMOLE-221: Correct faulty double-increment of args (should advance ONE at a time). 2020-07-06 18:19:08 -07:00
Michael Jumper
aa3a9cde6c GUACAMOLE-221: Migrate Kubernetes handling of "argv" to guac_argv_*() convenience API. 2020-07-06 18:19:08 -07:00
Michael Jumper
08a57d3375 GUACAMOLE-221: Migrate telnet handling of "argv" to guac_argv_*() convenience API. 2020-07-06 18:19:08 -07:00
Michael Jumper
2f6de25418 GUACAMOLE-221: Migrate SSH handling of "argv" to guac_argv_*() convenience API. 2020-07-06 18:19:08 -07:00
Michael Jumper
f8f2c7f747 GUACAMOLE-221: Allow callers to request that argument values be automatically echoed to all connected users. 2020-07-06 18:19:08 -07:00
Michael Jumper
a8151c40c4 GUACAMOLE-221: Implement libguac convenience API for awaiting and processing argument streams. 2020-07-06 18:19:08 -07:00
Mike Jumper
f0dee00d33
GUACAMOLE-1110: Merge support for running the "guacd" Docker image as a specific service user. 2020-07-02 22:34:35 -07:00
Mike Jumper
3fc3880f0e
GUACAMOLE-1122: Merge fix for build failure due to conditionally-missing variable declaration. 2020-07-02 22:30:58 -07:00
Nick Couchman
d35a97d28e GUACAMOLE-1122: Correct scope of settings variable for RDP user file handler." 2020-07-02 16:50:23 -04:00
Mathias
7d06113cbe
GUACAMOLE-1110: Replace user nobody with guacd 2020-06-30 12:41:35 +02:00
Virtually Nick
c10ceab7e8
GUACAMOLE-1114: Merge fix the destruction of some thread mutexes 2020-06-26 14:19:39 -05:00
Jimmy
d34745a40b GUACAMOLE-1114: Clean up clipboard mutex lock. 2020-06-26 23:19:23 +03:00
Jimmy
264192fd25 GUACAMOLE-1114: Clean up print job mutex lock 2020-06-26 23:18:02 +03:00
Nick Couchman
79c6e5787d Merge staging/1.2.0 changes back to master. 2020-06-25 17:56:43 -04:00
Virtually Nick
6042222d44
GUACAMOLE-474: Merge properly enforce file new upload/download restrictions. 2020-06-25 17:52:11 -04:00
Michael Jumper
7de6ba7ea9 GUACAMOLE-474: Do not allow RDPDR file downloads via "get" instructions if downloads are disabled. 2020-06-25 14:43:37 -07:00
Michael Jumper
630798503c GUACAMOLE-474: Ensure RDPDR "Download" folder behaves as a normal folder if downloads are disabled. 2020-06-25 14:43:37 -07:00
Michael Jumper
f4ff5f337c GUACAMOLE-474: Enforce upload disable option at low level, warning if not blocked at higher level as expected. 2020-06-25 14:43:37 -07:00
Michael Jumper
d8c32b1e82 GUACAMOLE-474: Enforce download disable option at low level, warning if not blocked at higher level as expected. 2020-06-25 14:41:39 -07:00
Mathias
86176ff770
GUACAMOLE-1110: Shrinks container footprint, higher security and reliability check
Dockage image size will reduced by install only package dependencies (without recommendations). The guacd process runs as a non-privileged user and will be checked by netcat.
2020-06-25 17:51:29 +02:00
Nick Couchman
45a0cd943b Merge staging/1.2.0 changes back to master. 2020-06-24 20:19:10 -04:00
Virtually Nick
99fce8fa19
GUACAMOLE-465: Merge correct usage of printf-like guacenc_log(). 2020-06-24 20:16:58 -04:00
Michael Jumper
af2c109079 GUACAMOLE-465: Correct printf-style format string (should be "%s" for strings, not "%d"). 2020-06-24 17:14:25 -07:00
Michael Jumper
4ac0940e81 GUACAMOLE-465: Remove redundant newline character from calls to guacenc_log(). 2020-06-24 17:14:23 -07:00
Virtually Nick
3a87dd0c96
GUACAMOLE-465: Merge remove superfluous access check prior to attempting file deletion. 2020-06-24 20:10:34 -04:00
Nick Couchman
37965a961e Merge staging/1.2.0 changes back to master. 2020-06-24 20:09:05 -04:00
Virtually Nick
9ee956f765
GUACAMOLE-465: Merge correct possible leak of malloc'd video structure. 2020-06-24 20:08:20 -04:00
Michael Jumper
096a067b1f GUACAMOLE-465: Correct possible leak of malloc'd video structure. 2020-06-24 13:56:16 -07:00
Michael Jumper
8d9049942d GUACAMOLE-465: Remove superfluous access check prior to attempting file deletion. 2020-06-24 13:41:03 -07:00
Nick Couchman
708769b4c3 Merge staging/1.2.0 changes back to master. 2020-06-24 13:11:17 -04:00
Virtually Nick
614f38767e
GUACAMOLE-465: Merge produce MPEG-4 output within a proper container. 2020-06-24 13:10:32 -04:00
Nick Couchman
f1f1b0d438 Merge staging/1.2.0 changes back to master. 2020-06-24 12:59:34 -04:00
Virtually Nick
4815f62358
GUACAMOLE-966: Merge bump libguac version number for 1.2.0. 2020-06-24 12:58:40 -04:00
Nick Couchman
025525f93a Merge staging/1.2.0 changes back to master. 2020-06-24 12:55:34 -04:00
Virtually Nick
debd888cf5
GUACAMOLE-518: Merge correct behavior of keys typed with AltGr. 2020-06-24 12:54:37 -04:00
Michael Jumper
3e73e392a0 GUACAMOLE-966: Bump libguac version number for 1.2.0 (interfaces added, none changed or removed). 2020-06-24 02:37:35 -07:00
Michael Jumper
fb94ef9e9a GUACAMOLE-495: Add whitespace and reflow as necessary to improve readability. 2020-06-24 02:25:02 -07:00
Michael Jumper
628f2fd815 GUACAMOLE-518: Ensure all keys are released even if the key pressed client-side is unknown except through dead keys. 2020-06-24 00:54:39 -07:00
Michael Jumper
3798d85bd1 GUACAMOLE-518: Count client-side pressed keys independently of server-side keys. 2020-06-24 00:54:39 -07:00
Michael Jumper
2407157d00 GUACAMOLE-518: Handle modifier status correctly when multiple modifier keys are involved. 2020-06-24 00:54:39 -07:00
Michael Jumper
7d17e6898a GUACAMOLE-518: Remove unused GUAC_KEYSYMS_* constants. 2020-06-24 00:54:39 -07:00
Michael Jumper
337f3bbff2 GUACAMOLE-518: Map both Right Alt and AltGr to Windows' Right Alt.
Windows expects the Right Alt key to be sent for AltGr.
2020-06-23 22:05:18 -07:00
Michael Jumper
9b891e2360 GUACAMOLE-495: Update guacenc mangpage - the MPEG-4 output is no longer a raw stream. 2020-06-23 15:48:41 -07:00
Michael Jumper
81601d99fe GUACAMOLE-465: Reformat/reflow comments to match established style. 2020-06-23 15:40:04 -07:00
Nick Couchman
e8deeeae97 Merge staging/1.2.0 changes back to master. 2020-06-23 14:19:37 -04:00
Virtually Nick
5f5080994f
GUACAMOLE-518: Merge correct Caps Lock and Shift behavior within RDP keymap translation system. 2020-06-23 14:18:58 -04:00
Michael Jumper
60944f1092 GUACAMOLE-518: Keep locally-tracked keyboard lock status in sync with remote changes to keyboard locks. 2020-06-23 11:11:19 -07:00
Michael Jumper
a246403137 GUACAMOLE-518: Update German keymap to define behavior of Caps Lock.
On German keyboards, Caps Lock behaves like Shift, affects all
characters except dead keys, "°", "<", ">", "-", "_", and any keys
requiring AltGr, and is undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
3f375a4501 GUACAMOLE-518: Update Swiss German keymap to define behavior of Caps Lock.
On Swiss German keyboards, Caps Lock turns all letters uppercase and is
undone by Shift, except for letters which instead rely on Shift to
determine their identity (accented letters).

For example, the key directly to the right of "P" produces "ü" if no
modifiers or locks are active. With Shift pressed, the key changes
identity to "è", with the state of Caps Lock determining the case:

| Shift | Caps  | Result |
|-------|-------|--------|
|       |       | "ü"    |
|       |   X   | "Ü"    |
|   X   |       | "è"    |
|   X   |   X   | "È"    |

This goes for all accented characters in the Swiss German layout.
2020-06-23 11:11:19 -07:00
Michael Jumper
e8153f9002 GUACAMOLE-518: Update French keymap to define behavior of Caps Lock.
On French keyboards, Caps Lock behaves like Shift, affects all
characters including dead keys but excluding "<", ">", "²", and any keys
requiring AltGr, and is undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
568e037012 GUACAMOLE-518: Update Swiss French keymap to define behavior of Caps Lock.
On Swiss French keyboards, Caps Lock affects only non-accented letters
and is undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
31d05de72a GUACAMOLE-518: Update Belgian French keymap to define behavior of Caps Lock.
On Belgian French keyboards, Caps Lock behaves like Shift, affects all
characters including dead keys but excluding "<", ">", "²", "³", and any
keys requiring AltGr, and is undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
f884ab76b1 GUACAMOLE-518: Update Italian keymap to define behavior of Caps Lock.
On Italian keyboards, Caps Lock affects only non-accented letters and is
undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
1117cf052c GUACAMOLE-518: Update Turkish-Q keymap to define behavior of Caps Lock.
On Turkish-Q keyboards, Caps Lock affects only letters (including
accented letters) except for "é", and is undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
b69248048c GUACAMOLE-518: Update Swedish keymap to define behavior of Caps Lock.
On Swedish keyboards, Caps Lock affects only letters (including accented
letters) and is undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
38737a8353 GUACAMOLE-518: Update Brazilian Portuguese keymap to define behavior of Caps Lock.
On Brazilian Portuguese keyboards, Caps Lock affects only letters
(including accented letters) and is undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
5c1a2fc44c GUACAMOLE-518: Update Latin American keymap to define behavior of Caps Lock.
On Latin American keyboards, Caps Lock affects only letters (including
accented letters) and is undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
ba3d1de3bb GUACAMOLE-518: Update Spanish keymap to define behavior of Caps Lock.
On Spanish keyboards, Caps Lock affects only letters (including accented
letters) and is undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
2cec040b9e GUACAMOLE-518: Update Danish keymap to define behavior of Caps Lock.
On Danish keyboards, Caps Lock affects only letters (including accented
letters) and is undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
e9652becfd GUACAMOLE-518: Update Hungarian keymap to define behavior of Caps Lock.
On Hungarian keyboards, Caps Lock affects only letters (including
accented letters) except those requiring AltGr, and is undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
31a415cc59 GUACAMOLE-518: Update UK English keymap to define behavior of Caps Lock.
On UK English keyboards, Caps Lock affects only letters (including
accented letters) and is undone by Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
024e281252 GUACAMOLE-518: Update US English keymap to define behavior of Caps Lock.
On US English keyboards, Caps Lock affects only letters and is undone by
Shift.
2020-06-23 11:11:19 -07:00
Michael Jumper
48b3d5038f GUACAMOLE-518: Automatically release any automatically-pressed keys after user has released all keys on the client side. 2020-06-23 11:11:19 -07:00
Michael Jumper
cb6ffd06e6 GUACAMOLE-518: Allow multiple possible definitions (means of typing a particular key) for each keysym. 2020-06-23 11:11:19 -07:00
Michael Jumper
67450d89f3 GUACAMOLE-518: Correct signedness of keyboard flag variables. 2020-06-22 10:05:37 -07:00
Michael Jumper
96c4c208b4 GUACAMOLE-518: Ensure keyboard state is always updated for all keys pressed/released. 2020-06-22 10:05:37 -07:00
Michael Jumper
7fd54c56a8 GUACAMOLE-518: Modifications to generate.pl should result in regeneration of all keymaps. 2020-06-21 21:05:43 -07:00
Michael Jumper
ce0982fefd GUACAMOLE-518: Track and update modifier states based on overall flags, not keysyms. 2020-06-21 21:05:43 -07:00
Nick Couchman
0c784d434c Merge staging/1.2.0 changes back to master. 2020-06-20 20:53:18 -04:00
Virtually Nick
120be65dbc
GUACAMOLE-1076: Merge correct RemoteApp launch behavior. 2020-06-20 20:51:21 -04:00
Michael Jumper
7598f5a95a GUACAMOLE-1076: Use proper constants to represent RemoteApp high contrast support flags. 2020-06-20 16:09:43 -07:00
Michael Jumper
d5608fb8a2 GUACAMOLE-1076: Send same System Parameters Update PDUs as Apache Guacamole 1.0.0 and older. 2020-06-20 15:09:00 -07:00
Michael Jumper
a7732e72be GUACAMOLE-1076: Ensure Client Information PDU is sent during RAIL handshake (required by spec). 2020-06-20 14:32:53 -07:00
Michael Jumper
6a50d3076c GUACAMOLE-1076: Set RAIL flag for expanding environment variables within RemoteApp arguments (match v1.0.0 behavior). 2020-06-20 14:14:29 -07:00
Michael Jumper
29b055b511 Merge 1.2.0 changes back to master. 2020-06-18 12:52:30 -07:00
Mike Jumper
f899fe0b2f
GUACAMOLE-1053: Merge restoration of proper locking on RDP structures. 2020-06-18 12:51:34 -07:00
Grigory Trenin
d76502d169 GUACAMOLE-1053: guacd segfaults when user actively presses keys at RDP disconnect time 2020-06-18 05:57:33 -04:00
Michael Jumper
787ae317fc Merge 1.2.0 changes back to master. 2020-06-17 14:12:12 -07:00
Mike Jumper
e82eb45a74
GUACAMOLE-513: Merge improvements to Wake-on-LAN error handling and wait behavior. 2020-06-17 14:11:27 -07:00
Nick Couchman
db4b155c51 GUACAMOLE-513: Update default WoL boot wait time to 0. 2020-06-17 15:27:12 -04:00
Virtually Nick
72489d5690
GUACAMOLE-1103: Merge fix the incorrect comment on enable_sftp variable in RDP 2020-06-17 14:10:16 -04:00
Jimmy
2c2f372def GUACAMOLE-1103: Fixed the incorrect comment of the enable_sftp variable of the guac_rdp_settings structure. 2020-06-17 23:01:13 +03:00
Nick Couchman
e51c269a51 GUACAMOL-513: Correctly handle unknown address families. 2020-06-17 06:50:43 -04:00
Nick Couchman
b0bcb30346 GUACAMOLE-513: Merge socket calls and handle IP family determination correctly. 2020-06-16 16:48:51 -04:00
Nick Couchman
ec305903d0 GUACAMOLE-513: Add guac_error handling for WoL 2020-06-16 15:06:51 -04:00
Nick Couchman
57c4dbf454 GUACAMOLE-513: No need to close socket when socket was never opened. 2020-06-16 11:50:36 -04:00
Nick Couchman
30bbb892db GUACAMOLE-513: Handle cases where socket open fails. 2020-06-09 19:29:22 -04:00
Michael Jumper
b8ecfe2425 Merge 1.2.0 changes back to master. 2020-06-09 10:43:10 -07:00
Mike Jumper
9ca382e2aa
GUACAMOLE-513: Merge addition of new defaults.h header to Makefile.am. 2020-06-09 10:42:37 -07:00
Nick Couchman
0c59897da9 GUACAMOLE-513: New defaults header needs to be in Makefile.am 2020-06-09 13:06:40 -04:00
Michael Jumper
b6568d11b3 Merge 1.2.0 changes back to master. 2020-06-09 01:15:43 -07:00
Mike Jumper
65c07b75cc
GUACAMOLE-513: Merge support for Wake-on-LAN. 2020-06-09 01:15:11 -07:00
Michael Jumper
a5a89bcf1d Merge 1.2.0 changes back to master. 2020-06-08 10:42:40 -07:00
Mike Jumper
32723d229f
GUACAMOLE-474: Merge corrections to file upload/download build failures. 2020-06-08 10:40:05 -07:00
Nick Couchman
2aa2ccc90c GUACAMOLE-513: Properly close WOL socket. 2020-06-07 20:33:19 -04:00
Nick Couchman
df8030d9bb GUACAMOLE-513: Implement defaults header for protocol constants. 2020-06-07 20:27:42 -04:00
Nick Couchman
42e223f4a6 GUACAMOLE-474: Correct variable name and scope errors in RDP fs code. 2020-06-07 19:51:02 -04:00
Michael Jumper
4dce306a04 Merge 1.2.0 changes back to master. 2020-06-07 16:31:08 -07:00
Mike Jumper
e526174009
GUACAMOLE-474: Merge support for selectively disabling file uploads and downloads. 2020-06-07 16:30:37 -07:00
Nick Couchman
af89f828eb Merge staging/1.2.0 changes back to master. 2020-06-07 07:22:05 -04:00
Virtually Nick
d03d45fbf0
GUACAMOLE-1059: Merge correct verification of sound format index. 2020-06-07 07:20:22 -04:00
Michael Jumper
4184a52c98 GUACAMOLE-1059: Sound format index should be checked against maximum number of formats, not byte size of array. 2020-06-06 23:50:28 -07:00
Nick Couchman
ec093d3cea GUACAMOLE-474: Minor style and debug message tweaks. 2020-05-29 07:39:39 -04:00
Michael Jumper
029563a4b9 Merge 1.2.0 changes back to master. 2020-05-25 02:43:01 -07:00
Mike Jumper
8d895f13c7
GUACAMOLE-1059: Merge explicit non-fatal check for invalid RDP audio format request. 2020-05-25 02:41:51 -07:00
Nick Couchman
ff34146f57 GUACAMOLE-1059: Log array boundary violation for sound formats. 2020-05-24 08:55:58 -04:00
Nick Couchman
234f5aff1a GUACAMOLE-1059: Check array boundary for sound formats. 2020-05-16 21:12:53 -04:00
Michael Jumper
a0e11dc817 Merge staging/1.2.0 changes back to master. 2020-05-07 14:10:33 -07:00
Mike Jumper
0bf65ce8b0
GUACAMOLE-1059: Merge changes leveraging appropriate FreeRDP checks to verify input stream length. 2020-05-07 14:09:47 -07:00
Nick Couchman
557e2f5944 GUACAMOLE-1059: Fine tune comments and log messages. 2020-05-06 10:17:20 -04:00
Nick Couchman
47bf3ab672 GUACAMOLE-1059: Verify correct number of bytes for incoming wave. 2020-05-05 17:15:47 -04:00
Nick Couchman
315a8a7179 GUACAMOLE-1059: Correctly handle issues processing audio input formats. 2020-05-05 16:53:13 -04:00
Nick Couchman
e761e47cd0 GUACAMOLE-1059: Add missing checks and fix up warning messages. 2020-05-05 16:33:59 -04:00
Nick Couchman
71769b9715 GUACAMOLE-1059: Add missing check for manually copied buffer. 2020-05-04 19:49:15 -04:00
Nick Couchman
ac9e5e91f6 GUACAMOLE-1059: Remove bad check of audio stream against body_size. 2020-05-04 17:53:38 -04:00
Nick Couchman
98f0c271fb GUACAMOLE-1059: Add explanatory comments and additional logging. 2020-05-04 17:45:17 -04:00
Nick Couchman
8560ff9718 GUACAMOLE-1059: Move rdpsnd body size check to correct location. 2020-05-04 12:11:37 -04:00
Nick Couchman
ce28575b3a GUACAMOLE-1059: Use FreeRDP function for verifying Stream length before reading. 2020-05-04 08:48:40 -04:00
Tomer Gabel
8838199f5c GUACAMOLE-1047: Notify connecting client on unrecognized connection ID 2020-04-23 16:53:17 +03:00
Nick Couchman
f910d29083 GUACAMOLE-1031: Add call for RDP to pay attention to upload directory 2020-04-16 12:20:58 -04:00
Nick Couchman
02a7291742 GUACAMOLE-513: Adjust names of constants and fix style. 2020-04-15 09:42:22 -04:00
Nick Couchman
b077013c30 GUACAMOLE-474: Hide Download folder if downloads are disabled. 2020-04-02 14:58:06 -04:00
Nick Couchman
a0d4bacbc6 GUACAMOLE-513: Support determining IPv4 or IPv6. 2020-03-19 09:12:15 -04:00
Nick Couchman
0feda1fa2f GUACAMOLE-513: Make packet size a constant. 2020-03-19 09:12:15 -04:00
Nick Couchman
45e46bd245 GUACAMOLE-513: Move sleep to protocol implementations; update comments and headers. 2020-03-19 09:12:15 -04:00
Nick Couchman
3dc2591517 GUACAMOLE-513: Add debug logging for sending WoL. 2020-03-19 09:12:15 -04:00
Nick Couchman
3d4a27607d GUACAMOLE-513: Implement settings and code for Wake-on-LAN support. 2020-03-18 13:29:58 -04:00
Sean Reid
fe0658dd36 GUACAMOLE-465: resolved issues brought up in PR159 and added compatibility with recent versions of libavcodec 2020-02-02 16:34:40 -05:00
Sean Reid
fb237d4fc9 GUACAMOLE-465: resolved issues brought up in PR159 (unneeded dynamic mem allocation, style guide violations) 2020-02-02 16:34:40 -05:00
Sean Reid
bb825de73b GUACAMOLE-465: added dependency to libavformat as first step to supporting other types of codecs and containers in guacenc. migrated existing functionality to use the libavformat library for writing the files. there is not differnce to the user with this patch, but it provides a good base to finish this new feature from later 2020-02-02 16:34:40 -05:00
Nick Couchman
1a699686b9 GUACAMOLE-474: Implement logic to disable file transfers in each protocol. 2020-01-26 03:33:08 -05:00
Nick Couchman
42e382062c GUACAMOLE-474: Add parameter processing for file upload/download disable. 2020-01-26 03:33:08 -05:00
242 changed files with 11458 additions and 3470 deletions

View File

@ -56,5 +56,5 @@ tests/test_*
!tests/test_*.[ch]
# Generated docs
doc/doxygen-output
doc/*/doxygen-output

4
.gitignore vendored
View File

@ -44,5 +44,7 @@ configure
stamp-h1
# Generated docs
doc/doxygen-output
doc/*/doxygen-output
# IDE metadata
nbproject/

View File

@ -21,93 +21,173 @@
# Dockerfile for guacamole-server
#
# The Alpine Linux image that should be used as the basis for the guacd image
ARG ALPINE_BASE_IMAGE=latest
FROM alpine:${ALPINE_BASE_IMAGE} AS builder
# Use Debian as base for the build
ARG DEBIAN_VERSION=stable
FROM debian:${DEBIAN_VERSION} AS builder
# Base directory for installed build artifacts.
# Due to limitations of the Docker image build process, this value is
# duplicated in an ARG in the second stage of the build.
#
ARG PREFIX_DIR=/usr/local/guacamole
# Build arguments
ARG BUILD_DIR=/tmp/guacd-docker-BUILD
ARG BUILD_DEPENDENCIES=" \
# Install build dependencies
RUN apk add --no-cache \
autoconf \
automake \
freerdp2-dev \
gcc \
libcairo2-dev \
libjpeg62-turbo-dev \
libossp-uuid-dev \
libpango1.0-dev \
libpulse-dev \
libssh2-1-dev \
libssl-dev \
libtelnet-dev \
build-base \
cairo-dev \
cmake \
git \
grep \
libjpeg-turbo-dev \
libpng-dev \
libtool \
libvncserver-dev \
libwebsockets-dev \
libwebp-dev \
make"
# Bring build environment up to date and install build dependencies
RUN apt-get update && \
apt-get install -y $BUILD_DEPENDENCIES && \
rm -rf /var/lib/apt/lists/*
# Add configuration scripts
COPY src/guacd-docker/bin "${PREFIX_DIR}/bin/"
make \
openssl-dev \
pango-dev \
pulseaudio-dev \
util-linux-dev
# Copy source to container for sake of build
COPY . "$BUILD_DIR"
ARG BUILD_DIR=/tmp/guacamole-server
COPY . ${BUILD_DIR}
# Build guacamole-server from local source
RUN ${PREFIX_DIR}/bin/build-guacd.sh "$BUILD_DIR" "$PREFIX_DIR"
#
# Base directory for installed build artifacts.
#
# NOTE: Due to limitations of the Docker image build process, this value is
# duplicated in an ARG in the second stage of the build.
#
ARG PREFIX_DIR=/opt/guacamole
#
# Automatically select the latest versions of each core protocol support
# library (these can be overridden at build time if a specific version is
# needed)
#
ARG WITH_FREERDP='2(\.\d+)+'
ARG WITH_LIBSSH2='libssh2-\d+(\.\d+)+'
ARG WITH_LIBTELNET='\d+(\.\d+)+'
ARG WITH_LIBVNCCLIENT='LibVNCServer-\d+(\.\d+)+'
ARG WITH_LIBWEBSOCKETS='v\d+(\.\d+)+'
#
# Default build options for each core protocol support library, as well as
# guacamole-server itself (these can be overridden at build time if different
# options are needed)
#
ARG FREERDP_OPTS="\
-DBUILTIN_CHANNELS=OFF \
-DCHANNEL_URBDRC=OFF \
-DWITH_ALSA=OFF \
-DWITH_CAIRO=ON \
-DWITH_CHANNELS=ON \
-DWITH_CLIENT=ON \
-DWITH_CUPS=OFF \
-DWITH_DIRECTFB=OFF \
-DWITH_FFMPEG=OFF \
-DWITH_GSM=OFF \
-DWITH_GSSAPI=OFF \
-DWITH_IPP=OFF \
-DWITH_JPEG=ON \
-DWITH_LIBSYSTEMD=OFF \
-DWITH_MANPAGES=OFF \
-DWITH_OPENH264=OFF \
-DWITH_OPENSSL=ON \
-DWITH_OSS=OFF \
-DWITH_PCSC=OFF \
-DWITH_PULSE=OFF \
-DWITH_SERVER=OFF \
-DWITH_SERVER_INTERFACE=OFF \
-DWITH_SHADOW_MAC=OFF \
-DWITH_SHADOW_X11=OFF \
-DWITH_SSE2=ON \
-DWITH_WAYLAND=OFF \
-DWITH_X11=OFF \
-DWITH_X264=OFF \
-DWITH_XCURSOR=ON \
-DWITH_XEXT=ON \
-DWITH_XI=OFF \
-DWITH_XINERAMA=OFF \
-DWITH_XKBFILE=ON \
-DWITH_XRENDER=OFF \
-DWITH_XTEST=OFF \
-DWITH_XV=OFF \
-DWITH_ZLIB=ON"
ARG GUACAMOLE_SERVER_OPTS="\
--disable-guaclog"
ARG LIBSSH2_OPTS="\
-DBUILD_EXAMPLES=OFF \
-DBUILD_SHARED_LIBS=ON"
ARG LIBTELNET_OPTS="\
--disable-static \
--disable-util"
ARG LIBVNCCLIENT_OPTS=""
ARG LIBWEBSOCKETS_OPTS="\
-DDISABLE_WERROR=ON \
-DLWS_WITHOUT_SERVER=ON \
-DLWS_WITHOUT_TESTAPPS=ON \
-DLWS_WITHOUT_TEST_CLIENT=ON \
-DLWS_WITHOUT_TEST_PING=ON \
-DLWS_WITHOUT_TEST_SERVER=ON \
-DLWS_WITHOUT_TEST_SERVER_EXTPOLL=ON \
-DLWS_WITH_STATIC=OFF"
# Build guacamole-server and its core protocol library dependencies
RUN ${BUILD_DIR}/src/guacd-docker/bin/build-all.sh
# Record the packages of all runtime library dependencies
RUN ${PREFIX_DIR}/bin/list-dependencies.sh \
${PREFIX_DIR}/sbin/guacd \
${PREFIX_DIR}/lib/libguac-client-*.so \
${PREFIX_DIR}/lib/freerdp2/guac*.so \
RUN ${BUILD_DIR}/src/guacd-docker/bin/list-dependencies.sh \
${PREFIX_DIR}/sbin/guacd \
${PREFIX_DIR}/lib/libguac-client-*.so \
${PREFIX_DIR}/lib/freerdp2/*guac*.so \
> ${PREFIX_DIR}/DEPENDENCIES
# Use same Debian as the base for the runtime image
FROM debian:${DEBIAN_VERSION}-slim
# Use same Alpine version as the base for the runtime image
FROM alpine:${ALPINE_BASE_IMAGE}
# Base directory for installed build artifacts.
# Due to limitations of the Docker image build process, this value is
# duplicated in an ARG in the first stage of the build. See also the
#
# Base directory for installed build artifacts. See also the
# CMD directive at the end of this build stage.
#
ARG PREFIX_DIR=/usr/local/guacamole
# NOTE: Due to limitations of the Docker image build process, this value is
# duplicated in an ARG in the first stage of the build.
#
ARG PREFIX_DIR=/opt/guacamole
# Runtime environment
ENV LC_ALL=C.UTF-8
ENV LD_LIBRARY_PATH=${PREFIX_DIR}/lib
ENV GUACD_LOG_LEVEL=info
ARG RUNTIME_DEPENDENCIES=" \
ca-certificates \
ghostscript \
fonts-liberation \
fonts-dejavu \
xfonts-terminus"
# Copy build artifacts into this stage
COPY --from=builder ${PREFIX_DIR} ${PREFIX_DIR}
# Bring runtime environment up to date and install runtime dependencies
RUN apt-get update && \
apt-get install -y $RUNTIME_DEPENDENCIES && \
apt-get install -y $(cat "${PREFIX_DIR}"/DEPENDENCIES) && \
rm -rf /var/lib/apt/lists/*
RUN apk add --no-cache \
ca-certificates \
ghostscript \
netcat-openbsd \
shadow \
terminus-font \
ttf-dejavu \
ttf-liberation \
util-linux-login && \
xargs apk add --no-cache < ${PREFIX_DIR}/DEPENDENCIES
# Link FreeRDP plugins into proper path
RUN ${PREFIX_DIR}/bin/link-freerdp-plugins.sh \
${PREFIX_DIR}/lib/freerdp2/libguac*.so
# Checks the operating status every 5 minutes with a timeout of 5 seconds
HEALTHCHECK --interval=5m --timeout=5s CMD nc -z 127.0.0.1 4822 || exit 1
# Create a new user guacd
ARG UID=1000
ARG GID=1000
RUN groupadd --gid $GID guacd
RUN useradd --system --create-home --shell /sbin/nologin --uid $UID --gid $GID guacd
# Run with user guacd
USER guacd
# Expose the default listener port
EXPOSE 4822
@ -117,5 +197,5 @@ EXPOSE 4822
# Note the path here MUST correspond to the value specified in the
# PREFIX_DIR build argument.
#
CMD /usr/local/guacamole/sbin/guacd -b 0.0.0.0 -L $GUACD_LOG_LEVEL -f
CMD /opt/guacamole/sbin/guacd -b 0.0.0.0 -L $GUACD_LOG_LEVEL -f

View File

@ -89,14 +89,15 @@ if ENABLE_GUACLOG
SUBDIRS += src/guaclog
endif
EXTRA_DIST = \
.dockerignore \
CONTRIBUTING \
Dockerfile \
LICENSE \
NOTICE \
bin/guacctl \
doc/Doxyfile.in \
src/guacd-docker \
EXTRA_DIST = \
.dockerignore \
CONTRIBUTING \
Dockerfile \
LICENSE \
NOTICE \
bin/guacctl \
doc/libguac/Doxyfile.in \
doc/libguac-terminal/Doxyfile.in \
src/guacd-docker \
util/generate-test-runner.pl

View File

@ -117,7 +117,7 @@ error() {
##
usage() {
cat >&2 <<END
guacctl 1.2.0, Apache Guacamole terminal session control utility.
guacctl 1.5.0, Apache Guacamole terminal session control utility.
Usage: guacctl [OPTION] [FILE or NAME]...
-d, --download download each of the files listed.

View File

@ -18,7 +18,7 @@
#
AC_PREREQ([2.61])
AC_INIT([guacamole-server], [1.2.0])
AC_INIT([guacamole-server], [1.5.0])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
AM_SILENT_RULES([yes])
@ -75,21 +75,50 @@ AC_CHECK_LIB([dl], [dlopen],
AC_MSG_ERROR("libdl is required on systems which do not otherwise provide dlopen()"),
[#include <dlfcn.h>])])
# OSSP UUID
AC_CHECK_LIB([ossp-uuid], [uuid_make], [UUID_LIBS=-lossp-uuid],
AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid],
AC_MSG_ERROR("The OSSP UUID library is required")))
#
# libuuid
#
# Check for and validate OSSP uuid.h header
AC_CHECK_HEADERS([ossp/uuid.h])
AC_CHECK_DECL([uuid_make],,
AC_MSG_ERROR("No OSSP uuid.h found in include path"),
[#ifdef HAVE_OSSP_UUID_H
#include <ossp/uuid.h>
#else
#include <uuid.h>
#endif
])
have_libuuid=disabled
AC_ARG_WITH([libuuid],
[AS_HELP_STRING([--with-libuuid],
[use libuuid to generate unique identifiers @<:@default=check@:>@])],
[],
[with_libuuid=check])
if test "x$with_libuuid" != "xno"
then
have_libuuid=yes
AC_CHECK_LIB([uuid], [uuid_generate],
[UUID_LIBS=-luuid]
[AC_DEFINE([HAVE_LIBUUID],, [Whether libuuid is available])],
[have_libuuid=no])
fi
# OSSP UUID (if libuuid is unavilable)
if test "x${have_libuuid}" != "xyes"
then
AC_CHECK_LIB([ossp-uuid], [uuid_make], [UUID_LIBS=-lossp-uuid],
AC_CHECK_LIB([uuid], [uuid_make], [UUID_LIBS=-luuid],
AC_MSG_ERROR([
--------------------------------------------
Unable to find libuuid or the OSSP UUID library.
Either libuuid (from util-linux) or the OSSP UUID library is required for
guacamole-server to be built.
--------------------------------------------])))
# Check for and validate OSSP uuid.h header
AC_CHECK_HEADERS([ossp/uuid.h])
AC_CHECK_DECL([uuid_make],,
AC_MSG_ERROR("No OSSP uuid.h found in include path"),
[#ifdef HAVE_OSSP_UUID_H
#include <ossp/uuid.h>
#else
#include <uuid.h>
#endif
])
fi
# cunit
AC_CHECK_LIB([cunit], [CU_run_test], [CUNIT_LIBS=-lcunit])
@ -131,6 +160,11 @@ AC_CHECK_DECL([strlcat],
[Whether strlcat() is defined])],,
[#include <string.h>])
AC_CHECK_DECL([strnstr],
[AC_DEFINE([HAVE_STRNSTR],,
[Whether strnstr() is defined])],,
[#include <string.h>])
# Typedefs
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
@ -151,12 +185,16 @@ AC_SUBST([PULSE_INCLUDE], '-I$(top_srcdir)/src/pulse')
AC_SUBST([COMMON_SSH_LTLIB], '$(top_builddir)/src/common-ssh/libguac_common_ssh.la')
AC_SUBST([COMMON_SSH_INCLUDE], '-I$(top_srcdir)/src/common-ssh')
# Kubernetes support
AC_SUBST([LIBGUAC_CLIENT_KUBERNETES_LTLIB], '$(top_builddir)/src/protocols/kubernetes/libguac-client-kubernetes.la')
AC_SUBST([LIBGUAC_CLIENT_KUBERNETES_INCLUDE], '-I$(top_srcdir)/src/protocols/kubernetes')
# RDP support
AC_SUBST([LIBGUAC_CLIENT_RDP_LTLIB], '$(top_builddir)/src/protocols/rdp/libguac-client-rdp.la')
AC_SUBST([LIBGUAC_CLIENT_RDP_INCLUDE], '-I$(top_srcdir)/src/protocols/rdp')
# Terminal emulator
AC_SUBST([TERMINAL_LTLIB], '$(top_builddir)/src/terminal/libguac_terminal.la')
AC_SUBST([TERMINAL_LTLIB], '$(top_builddir)/src/terminal/libguac-terminal.la')
AC_SUBST([TERMINAL_INCLUDE], '-I$(top_srcdir)/src/terminal $(PANGO_CFLAGS) $(PANGOCAIRO_CFLAGS) $(COMMON_INCLUDE)')
# Init directory
@ -202,6 +240,24 @@ fi
AM_CONDITIONAL([ENABLE_AVCODEC], [test "x${have_libavcodec}" = "xyes"])
#
# libavformat
#
have_libavformat=disabled
AC_ARG_WITH([libavformat],
[AS_HELP_STRING([--with-libavformat],
[use libavformat when encoding video @<:@default=check@:>@])],
[].
[with_libavformat=check])
if test "x$with_libavformat" != "xno"
then
have_libavformat=yes
PKG_CHECK_MODULES([AVFORMAT], [libavformat],, [have_libavformat=no]);
fi
AM_CONDITIONAL([ENABLE_AVFORMAT], [test "x${have_libavformat}" = "xyes"])
#
# libavutil
#
@ -269,30 +325,6 @@ then
else
AC_DEFINE([ENABLE_SSL],, [Whether SSL-related support is enabled])
# OpenSSL 1.1 accessor function for DSA signature values
AC_CHECK_DECL([DSA_SIG_get0],
[AC_DEFINE([HAVE_DSA_SIG_GET0],,
[Whether libssl provides DSA_SIG_get0()])],,
[#include <openssl/dsa.h>])
# OpenSSL 1.1 accessor function for DSA public key parameters
AC_CHECK_DECL([DSA_get0_pqg],
[AC_DEFINE([HAVE_DSA_GET0_PQG],,
[Whether libssl provides DSA_get0_pqg()])],,
[#include <openssl/dsa.h>])
# OpenSSL 1.1 accessor function for DSA public/private key values
AC_CHECK_DECL([DSA_get0_key],
[AC_DEFINE([HAVE_DSA_GET0_KEY],,
[Whether libssl provides DSA_get0_key()])],,
[#include <openssl/dsa.h>])
# OpenSSL 1.1 accessor function for RSA public/private key values
AC_CHECK_DECL([RSA_get0_key],
[AC_DEFINE([HAVE_RSA_GET0_KEY],,
[Whether libssl provides RSA_get0_key()])],,
[#include <openssl/rsa.h>])
# OpenSSL 1.1 does away with explicit threading callbacks
AC_MSG_CHECKING([whether libssl requires threading callbacks])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
@ -472,6 +504,27 @@ then
AC_CHECK_LIB([vncclient], [rfbInitClient], [VNC_LIBS="$VNC_LIBS -lvncclient"], [have_libvncserver=no])
fi
#
# Underlying libvncserver usage of gcrypt
#
if test "x${have_libvncserver}" = "xyes"
then
# Whether libvncserver was built against libgcrypt
AC_CHECK_DECL([LIBVNCSERVER_WITH_CLIENT_GCRYPT],
[AC_CHECK_HEADER(gcrypt.h,,
[AC_MSG_WARN([
--------------------------------------------
libvncserver appears to be built against
libgcrypt, but the libgcrypt headers
could not be found. VNC will be disabled.
--------------------------------------------])
have_libvncserver=no])],,
[[#include <rfb/rfbconfig.h>]])
fi
AM_CONDITIONAL([ENABLE_VNC], [test "x${have_libvncserver}" = "xyes"])
AC_SUBST(VNC_LIBS)
@ -554,6 +607,34 @@ then
fi
#
# Generic credential support within libVNCServer (authentication beyond
# basic, standard VNC passwords)
#
if test "x${have_libvncserver}" = "xyes"
then
have_vnc_creds=yes
AC_CHECK_MEMBERS([rfbClient.GetCredential],
[], [have_vnc_creds=no],
[[#include <rfb/rfbclient.h>]])
if test "x${have_vnc_creds}" = "xno"
then
AC_MSG_WARN([
--------------------------------------------
No generic credential support found in libvncclient.
VNC authentication support will be limited to passwords.
--------------------------------------------])
else
AC_DEFINE([ENABLE_VNC_GENERIC_CREDENTIALS],,
[Whether support for generic VNC credentials is available.])
fi
fi
#
# FreeRDP 2 (libfreerdp2, libfreerdp-client2, and libwinpr2)
#
@ -605,7 +686,44 @@ then
fi
# Variation in memory internal allocation/free behavior
# It is difficult or impossible to test for variations in FreeRDP behavior in
# between releases, as the change in behavior may not (yet) be associated with
# a corresponding change in version number and may not have any detectable
# effect on the FreeRDP API
AC_ARG_ENABLE(allow_freerdp_snapshots,
[AS_HELP_STRING([--enable-allow-freerdp-snapshots],
[allow building against unknown development snapshots of FreeRDP])
],allow_freerdp_snapshots=yes)
if test "x${have_freerdp2}" = "xyes" -a "x${allow_freerdp_snapshots}" != "xyes"
then
AC_MSG_CHECKING([whether FreeRDP appears to be a development version])
AC_EGREP_CPP([\"[0-9]+\\.[0-9]+\\.[0-9]+(-rc[0-9]+)?\"], [
#include <freerdp/version.h>
FREERDP_VERSION_FULL
],
[AC_MSG_RESULT([no])],
[AC_MSG_RESULT([yes])]
[AC_MSG_ERROR([
--------------------------------------------
You are building against a development version of FreeRDP. Non-release
versions of FreeRDP may have differences in behavior that are impossible to
check for at build time. This may result in memory leaks or other strange
behavior.
*** PLEASE USE A RELEASED VERSION OF FREERDP IF POSSIBLE ***
If you are ABSOLUTELY CERTAIN that building against this version of FreeRDP
is OK, rerun configure with the --enable-allow-freerdp-snapshots
--------------------------------------------])])
fi
# Variation in memory internal allocation/free behavior for bitmaps
if test "x${have_freerdp2}" = "xyes"
then
@ -629,6 +747,29 @@ then
fi
# Variation in memory internal allocation/free behavior for channel streams
if test "x${have_freerdp2}" = "xyes"
then
# FreeRDP 2.0.0-rc3 through 2.0.0-rc4 automatically free the wStream
# provided to pVirtualChannelWriteEx(). This changed in commit 1b78b59, and
# implementations must manually free the wStream after it is no longer
# needed (after the write is complete or cancelled).
AC_MSG_CHECKING([whether pVirtualChannelWriteEx() frees the wStream upon completion])
AC_EGREP_CPP([\"2\\.0\\.0-(rc|dev)[3-4]\"], [
#include <freerdp/version.h>
FREERDP_VERSION_FULL
],
[AC_MSG_RESULT([yes])]
[AC_DEFINE([FREERDP_SVC_CORE_FREES_WSTREAM],,
[Whether pVirtualChannelWriteEx() frees the wStream upon completion])],
[AC_MSG_RESULT([no])])
fi
# Glyph callback variants
if test "x${have_freerdp2}" = "xyes"
then
@ -737,6 +878,14 @@ then
[[#include <freerdp/freerdp.h>]])
fi
# Updated certificate verification callback (introduced with 2.0.0, not present
# in 2.0.0-rc4 or earlier)
if test "x${have_freerdp2}" = "xyes"
then
AC_CHECK_MEMBERS([freerdp.VerifyCertificateEx],,,
[[#include <freerdp/freerdp.h>]])
fi
# Restore CPPFLAGS, removing FreeRDP-specific options needed for testing
CPPFLAGS="$OLDCPPFLAGS"
@ -764,7 +913,7 @@ if test "x$with_ssh" != "xno"
then
have_libssh2=yes
AC_CHECK_LIB([ssh2], [libssh2_session_init_ex],
AC_CHECK_LIB([ssh2], [libssh2_userauth_publickey_frommemory],
[SSH_LIBS="$SSH_LIBS -lssh2"],
[have_libssh2=no])
fi
@ -995,10 +1144,11 @@ AC_ARG_ENABLE([guacenc],
[],
[enable_guacenc=yes])
AM_CONDITIONAL([ENABLE_GUACENC], [test "x${enable_guacenc}" = "xyes" \
-a "x${have_libavcodec}" = "xyes" \
-a "x${have_libavutil}" = "xyes" \
-a "x${have_libswscale}" = "xyes"])
AM_CONDITIONAL([ENABLE_GUACENC], [test "x${enable_guacenc}" = "xyes" \
-a "x${have_libavcodec}" = "xyes" \
-a "x${have_libavutil}" = "xyes" \
-a "x${have_libswscale}" = "xyes" \
-a "x${have_libavformat}" = "xyes"])
#
# guaclog
@ -1017,7 +1167,8 @@ AM_CONDITIONAL([ENABLE_GUACLOG], [test "x${enable_guaclog}" = "xyes"])
#
AC_CONFIG_FILES([Makefile
doc/Doxyfile
doc/libguac/Doxyfile
doc/libguac-terminal/Doxyfile
src/common/Makefile
src/common/tests/Makefile
src/common-ssh/Makefile
@ -1034,6 +1185,7 @@ AC_CONFIG_FILES([Makefile
src/guaclog/man/guaclog.1
src/pulse/Makefile
src/protocols/kubernetes/Makefile
src/protocols/kubernetes/tests/Makefile
src/protocols/rdp/Makefile
src/protocols/rdp/tests/Makefile
src/protocols/ssh/Makefile
@ -1091,6 +1243,7 @@ $PACKAGE_NAME version $PACKAGE_VERSION
freerdp2 ............ ${have_freerdp2}
pango ............... ${have_pango}
libavcodec .......... ${have_libavcodec}
libavformat.......... ${have_libavformat}
libavutil ........... ${have_libavutil}
libssh2 ............. ${have_libssh2}
libssl .............. ${have_ssl}

View File

@ -1,4 +1,3 @@
#!/bin/sh -e
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
@ -18,32 +17,42 @@
# under the License.
#
##
## @fn build-guacd.sh
##
## Builds the source of guacamole-server, automatically creating any required
## symbolic links for the proper loading of FreeRDP plugins.
##
## @param BUILD_DIR
## The directory which currently contains the guacamole-server source and
## in which the build should be performed.
##
## @param PREFIX_DIR
## The directory prefix into which the build artifacts should be installed
## in which the build should be performed. This is passed to the --prefix
## option of `configure`.
##
BUILD_DIR="$1"
PREFIX_DIR="$2"
#
# Build guacamole-server
# Project name / version
#
cd "$BUILD_DIR"
autoreconf -fi
./configure --prefix="$PREFIX_DIR" --disable-guaclog --with-freerdp-plugin-dir="$PREFIX_DIR/lib/freerdp2"
make
make install
ldconfig
PROJECT_NAME = libguac-terminal
PROJECT_NUMBER = @PACKAGE_VERSION@
#
# Warn about undocumented parameters and return values, but do not fill output
# with verbose progress info.
#
QUIET = YES
WARN_NO_PARAMDOC = YES
#
# Output format
#
ALPHABETICAL_INDEX = YES
GENERATE_HTML = YES
GENERATE_LATEX = NO
OPTIMIZE_OUTPUT_FOR_C = YES
OUTPUT_DIRECTORY = doxygen-output
RECURSIVE = YES
SHOW_INCLUDE_FILES = NO
#
# Input format
#
CASE_SENSE_NAMES = YES
FILE_PATTERNS = *.h
STRIP_FROM_PATH = ../../src/terminal
INPUT = ../../src/terminal/terminal/terminal.h
JAVADOC_AUTOBRIEF = YES
TAB_SIZE = 4
TYPEDEF_HIDES_STRUCT = YES

View File

@ -52,9 +52,9 @@ SHOW_INCLUDE_FILES = NO
CASE_SENSE_NAMES = YES
EXCLUDE_SYMBOLS = __* guac_palette*
FILE_PATTERNS = *.h
INPUT = ../src/libguac/guacamole
INPUT = ../../src/libguac/guacamole
JAVADOC_AUTOBRIEF = YES
STRIP_FROM_PATH = ../src/libguac
STRIP_FROM_PATH = ../../src/libguac
TAB_SIZE = 4
TYPEDEF_HIDES_STRUCT = YES

View File

@ -31,8 +31,6 @@ SUBDIRS = . tests
libguac_common_ssh_la_SOURCES = \
buffer.c \
dsa-compat.c \
rsa-compat.c \
sftp.c \
ssh.c \
key.c \
@ -40,8 +38,6 @@ libguac_common_ssh_la_SOURCES = \
noinst_HEADERS = \
common-ssh/buffer.h \
common-ssh/dsa-compat.h \
common-ssh/rsa-compat.h \
common-ssh/key.h \
common-ssh/sftp.h \
common-ssh/ssh.h \

View File

@ -1,61 +0,0 @@
/*
* 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.
*/
#ifndef GUAC_COMMON_SSH_DSA_COMPAT_H
#define GUAC_COMMON_SSH_DSA_COMPAT_H
#include "config.h"
#include <openssl/bn.h>
#include <openssl/dsa.h>
#ifndef HAVE_DSA_GET0_PQG
/**
* DSA_get0_pqg() implementation for versions of OpenSSL which lack this
* function (pre 1.1).
*
* See: https://www.openssl.org/docs/man1.1.0/crypto/DSA_get0_pqg.html
*/
void DSA_get0_pqg(const DSA* dsa_key, const BIGNUM** p,
const BIGNUM** q, const BIGNUM** g);
#endif
#ifndef HAVE_DSA_GET0_KEY
/**
* DSA_get0_key() implementation for versions of OpenSSL which lack this
* function (pre 1.1).
*
* See: https://www.openssl.org/docs/man1.1.0/crypto/DSA_get0_key.html
*/
void DSA_get0_key(const DSA* dsa_key, const BIGNUM** pub_key,
const BIGNUM** priv_key);
#endif
#ifndef HAVE_DSA_SIG_GET0
/**
* DSA_SIG_get0() implementation for versions of OpenSSL which lack this
* function (pre 1.1).
*
* See: https://www.openssl.org/docs/man1.1.0/crypto/DSA_SIG_get0.html
*/
void DSA_SIG_get0(const DSA_SIG* dsa_sig, const BIGNUM** r, const BIGNUM** s);
#endif
#endif

View File

@ -25,75 +25,27 @@
#include <guacamole/client.h>
#include <libssh2.h>
#include <openssl/ossl_typ.h>
/**
* OpenSSH v1 private keys are PEM-wrapped base64-encoded blobs. The encoded data begins with:
* "openssh-key-v1\0"
*/
#define OPENSSH_V1_KEY_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEA"
/**
* The expected header of RSA private keys.
* The base64-encoded prefix indicating an OpenSSH v1 private key is NOT protected by a
* passphrase. Specifically, it is the following data fields and values:
* pascal string: cipher name ("none")
* pascal string: kdf name ("none")
* pascal string: kdf params (NULL)
* 32-bit int: number of keys (1)
*/
#define SSH_RSA_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----"
/**
* The expected header of DSA private keys.
*/
#define SSH_DSA_KEY_HEADER "-----BEGIN DSA PRIVATE KEY-----"
/**
* The size of single number within a DSA signature, in bytes.
*/
#define DSA_SIG_NUMBER_SIZE 20
/**
* The size of a DSA signature, in bytes.
*/
#define DSA_SIG_SIZE DSA_SIG_NUMBER_SIZE*2
/**
* The type of an SSH key.
*/
typedef enum guac_common_ssh_key_type {
/**
* RSA key.
*/
SSH_KEY_RSA,
/**
* DSA key.
*/
SSH_KEY_DSA
} guac_common_ssh_key_type;
#define OPENSSH_V1_UNENCRYPTED_KEY "AAAABG5vbmUAAAAEbm9uZQAAAAAAAAAB"
/**
* Abstraction of a key used for SSH authentication.
*/
typedef struct guac_common_ssh_key {
/**
* The type of this key.
*/
guac_common_ssh_key_type type;
/**
* Underlying RSA private key, if any.
*/
RSA* rsa;
/**
* Underlying DSA private key, if any.
*/
DSA* dsa;
/**
* The associated public key, encoded as necessary for SSH.
*/
char* public_key;
/**
* The length of the public key, in bytes.
*/
int public_key_length;
/**
* The private key, encoded as necessary for SSH.
*/
@ -104,6 +56,11 @@ typedef struct guac_common_ssh_key {
*/
int private_key_length;
/**
* The private key's passphrase, if any.
*/
char *passphrase;
} guac_common_ssh_key;
/**
@ -144,31 +101,6 @@ const char* guac_common_ssh_key_error();
*/
void guac_common_ssh_key_free(guac_common_ssh_key* key);
/**
* Signs the given data using the given key, returning the length of the
* signature in bytes, or a value less than zero on error.
*
* @param key
* The key to use when signing the given data.
*
* @param data
* The arbitrary data to sign.
*
* @param length
* The length of the arbitrary data being signed, in bytes.
*
* @param sig
* The buffer into which the signature should be written. The buffer must
* be at least DSA_SIG_SIZE for DSA keys. For RSA keys, the signature size
* is dependent only on key size, and is equal to the length of the
* modulus, in bytes.
*
* @return
* The number of bytes in the resulting signature.
*/
int guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data,
int length, unsigned char* sig);
/**
* Verifies the host key for the given hostname/port combination against
* one or more known_hosts entries. The known_host entries can either be a

View File

@ -69,6 +69,16 @@ typedef struct guac_common_ssh_sftp_filesystem {
* instruction.
*/
char upload_path[GUAC_COMMON_SSH_SFTP_MAX_PATH];
/**
* If downloads from SFTP to the local browser should be disabled.
*/
int disable_download;
/**
* If uploads from the local browser to SFTP should be disabled.
*/
int disable_upload;
} guac_common_ssh_sftp_filesystem;
@ -122,13 +132,20 @@ typedef struct guac_common_ssh_sftp_ls_state {
* The name to send as the name of the filesystem whenever it is exposed
* to a user, or NULL to automatically generate a name from the provided
* root_path.
*
* @param disable_download
* Whether downloads from the SFTP share to the local browser should be
* disabled.
*
* @param disable_upload
* Whether uploads from the local browser to SFTP should be disabled.
*
* @return
* A new SFTP filesystem object, not yet exposed to users.
*/
guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem(
guac_common_ssh_session* session, const char* root_path,
const char* name);
const char* name, int disable_download, int disable_upload);
/**
* Destroys the given filesystem object, disconnecting from SFTP and freeing

View File

@ -1,59 +0,0 @@
/*
* 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 "config.h"
#include <openssl/bn.h>
#include <openssl/dsa.h>
#include <stdlib.h>
#ifndef HAVE_DSA_GET0_PQG
void DSA_get0_pqg(const DSA* dsa_key, const BIGNUM** p,
const BIGNUM** q, const BIGNUM** g) {
/* Retrieve all requested internal values */
if (p != NULL) *p = dsa_key->p;
if (q != NULL) *q = dsa_key->q;
if (g != NULL) *g = dsa_key->g;
}
#endif
#ifndef HAVE_DSA_GET0_KEY
void DSA_get0_key(const DSA* dsa_key, const BIGNUM** pub_key,
const BIGNUM** priv_key) {
/* Retrieve all requested internal values */
if (pub_key != NULL) *pub_key = dsa_key->pub_key;
if (priv_key != NULL) *priv_key = dsa_key->priv_key;
}
#endif
#ifndef HAVE_DSA_SIG_GET0
void DSA_SIG_get0(const DSA_SIG* dsa_sig, const BIGNUM** r, const BIGNUM** s) {
/* Retrieve all requested internal values */
if (r != NULL) *r = dsa_sig->r;
if (s != NULL) *s = dsa_sig->s;
}
#endif

View File

@ -20,9 +20,9 @@
#include "config.h"
#include "common-ssh/buffer.h"
#include "common-ssh/dsa-compat.h"
#include "common-ssh/key.h"
#include "common-ssh/rsa-compat.h"
#include <guacamole/string.h>
#include <openssl/bio.h>
#include <openssl/bn.h>
@ -33,119 +33,118 @@
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/**
* Check for a PKCS#1/PKCS#8 ENCRYPTED marker.
*
* @param data
* The buffer to scan.
* @param length
* The length of the buffer.
*
* @return
* True if the buffer contains the marker, false otherwise.
*/
static bool is_pkcs_encrypted_key(char* data, int length) {
return guac_strnstr(data, "ENCRYPTED", length) != NULL;
}
/**
* Check for a PEM header & initial base64-encoded data indicating this is an
* OpenSSH v1 key.
*
* @param data
* The buffer to scan.
* @param length
* The length of the buffer.
*
* @return
* True if the buffer contains a private key, false otherwise.
*/
static bool is_ssh_private_key(char* data, int length) {
if (length < sizeof(OPENSSH_V1_KEY_HEADER) - 1) {
return false;
}
return !strncmp(data, OPENSSH_V1_KEY_HEADER, sizeof(OPENSSH_V1_KEY_HEADER) - 1);
}
/**
* Assuming an offset into a key past the header, check for the base64-encoded
* data indicating this key is not protected by a passphrase.
*
* @param data
* The buffer to scan.
* @param length
* The length of the buffer.
*
* @return
* True if the buffer contains an unencrypted key, false otherwise.
*/
static bool is_ssh_key_unencrypted(char* data, int length) {
if (length < sizeof(OPENSSH_V1_UNENCRYPTED_KEY) - 1) {
return false;
}
return !strncmp(data, OPENSSH_V1_UNENCRYPTED_KEY, sizeof(OPENSSH_V1_UNENCRYPTED_KEY) - 1);
}
/**
* A passphrase is needed if the key is an encrypted PKCS#1/PKCS#8 key OR if
* the key is both an OpenSSH v1 key AND there isn't a marker indicating the
* key is unprotected.
*
* @param data
* The buffer to scan.
* @param length
* The length of the buffer.
*
* @return
* True if the buffer contains a key needing a passphrase, false otherwise.
*/
static bool is_passphrase_needed(char* data, int length) {
/* Is this an encrypted PKCS#1/PKCS#8 key? */
if (is_pkcs_encrypted_key(data, length)) {
return true;
}
/* Is this an OpenSSH v1 key? */
if (is_ssh_private_key(data, length)) {
/* This is safe due to the check in is_ssh_private_key. */
data += sizeof(OPENSSH_V1_KEY_HEADER) - 1;
length -= sizeof(OPENSSH_V1_KEY_HEADER) - 1;
/* If this is NOT unprotected, we need a passphrase. */
if (!is_ssh_key_unencrypted(data, length)) {
return true;
}
}
return false;
}
guac_common_ssh_key* guac_common_ssh_key_alloc(char* data, int length,
char* passphrase) {
guac_common_ssh_key* key;
BIO* key_bio;
/* Because libssh2 will do the actual key parsing (to let it deal with
* different key algorithms) we need to perform a heuristic here to check
* if a passphrase is needed. This could allow junk keys through that
* would never be able to auth. libssh2 should display errors to help
* admins track down malformed keys and delete or replace them.
*/
char* public_key;
char* pos;
/* Create BIO for reading key from memory */
key_bio = BIO_new_mem_buf(data, length);
/* If RSA key, load RSA */
if (length > sizeof(SSH_RSA_KEY_HEADER)-1
&& memcmp(SSH_RSA_KEY_HEADER, data,
sizeof(SSH_RSA_KEY_HEADER)-1) == 0) {
RSA* rsa_key;
const BIGNUM* key_e;
const BIGNUM* key_n;
/* Read key */
rsa_key = PEM_read_bio_RSAPrivateKey(key_bio, NULL, NULL, passphrase);
if (rsa_key == NULL)
return NULL;
/* Allocate key */
key = malloc(sizeof(guac_common_ssh_key));
key->rsa = rsa_key;
/* Set type */
key->type = SSH_KEY_RSA;
/* Allocate space for public key */
public_key = malloc(4096);
pos = public_key;
/* Retrieve public key */
RSA_get0_key(rsa_key, &key_n, &key_e, NULL);
/* Send public key formatted for SSH */
guac_common_ssh_buffer_write_string(&pos, "ssh-rsa", sizeof("ssh-rsa")-1);
guac_common_ssh_buffer_write_bignum(&pos, key_e);
guac_common_ssh_buffer_write_bignum(&pos, key_n);
/* Save public key to structure */
key->public_key = public_key;
key->public_key_length = pos - public_key;
}
/* If DSA key, load DSA */
else if (length > sizeof(SSH_DSA_KEY_HEADER)-1
&& memcmp(SSH_DSA_KEY_HEADER, data,
sizeof(SSH_DSA_KEY_HEADER)-1) == 0) {
DSA* dsa_key;
const BIGNUM* key_p;
const BIGNUM* key_q;
const BIGNUM* key_g;
const BIGNUM* pub_key;
/* Read key */
dsa_key = PEM_read_bio_DSAPrivateKey(key_bio, NULL, NULL, passphrase);
if (dsa_key == NULL)
return NULL;
/* Allocate key */
key = malloc(sizeof(guac_common_ssh_key));
key->dsa = dsa_key;
/* Set type */
key->type = SSH_KEY_DSA;
/* Allocate space for public key */
public_key = malloc(4096);
pos = public_key;
/* Retrieve public key */
DSA_get0_pqg(dsa_key, &key_p, &key_q, &key_g);
DSA_get0_key(dsa_key, &pub_key, NULL);
/* Send public key formatted for SSH */
guac_common_ssh_buffer_write_string(&pos, "ssh-dss", sizeof("ssh-dss")-1);
guac_common_ssh_buffer_write_bignum(&pos, key_p);
guac_common_ssh_buffer_write_bignum(&pos, key_q);
guac_common_ssh_buffer_write_bignum(&pos, key_g);
guac_common_ssh_buffer_write_bignum(&pos, pub_key);
/* Save public key to structure */
key->public_key = public_key;
key->public_key_length = pos - public_key;
}
/* Otherwise, unsupported type */
else {
BIO_free(key_bio);
if (is_passphrase_needed(data, length) && (passphrase == NULL || *passphrase == '\0'))
return NULL;
}
guac_common_ssh_key* key = malloc(sizeof(guac_common_ssh_key));
/* Copy private key to structure */
key->private_key_length = length;
key->private_key = malloc(length);
memcpy(key->private_key, data, length);
key->passphrase = strdup(passphrase);
BIO_free(key_bio);
return key;
}
@ -159,93 +158,11 @@ const char* guac_common_ssh_key_error() {
void guac_common_ssh_key_free(guac_common_ssh_key* key) {
/* Free key-specific data */
if (key->type == SSH_KEY_RSA)
RSA_free(key->rsa);
else if (key->type == SSH_KEY_DSA)
DSA_free(key->dsa);
free(key->private_key);
free(key->public_key);
free(key->passphrase);
free(key);
}
int guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data,
int length, unsigned char* sig) {
const EVP_MD* md;
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int dlen, len;
/* Get SHA1 digest */
if ((md = EVP_get_digestbynid(NID_sha1)) == NULL)
return -1;
/* Allocate digest context */
EVP_MD_CTX* md_ctx = EVP_MD_CTX_create();
if (md_ctx == NULL)
return -1;
/* Digest data */
EVP_DigestInit(md_ctx, md);
EVP_DigestUpdate(md_ctx, data, length);
EVP_DigestFinal(md_ctx, digest, &dlen);
/* Digest context no longer needed */
EVP_MD_CTX_destroy(md_ctx);
/* Sign with key */
switch (key->type) {
case SSH_KEY_RSA:
if (RSA_sign(NID_sha1, digest, dlen, sig, &len, key->rsa) == 1)
return len;
break;
case SSH_KEY_DSA: {
DSA_SIG* dsa_sig = DSA_do_sign(digest, dlen, key->dsa);
if (dsa_sig != NULL) {
const BIGNUM* sig_r;
const BIGNUM* sig_s;
/* Retrieve DSA signature values */
DSA_SIG_get0(dsa_sig, &sig_r, &sig_s);
/* Compute size of each half of signature */
int rlen = BN_num_bytes(sig_r);
int slen = BN_num_bytes(sig_s);
/* Ensure each number is within the required size */
if (rlen > DSA_SIG_NUMBER_SIZE || slen > DSA_SIG_NUMBER_SIZE)
return -1;
/* Init to all zeroes */
memset(sig, 0, DSA_SIG_SIZE);
/* Add R at the end of the first block of the signature */
BN_bn2bin(sig_r, sig + DSA_SIG_SIZE
- DSA_SIG_NUMBER_SIZE - rlen);
/* Add S at the end of the second block of the signature */
BN_bn2bin(sig_s, sig + DSA_SIG_SIZE - slen);
/* Done */
DSA_SIG_free(dsa_sig);
return DSA_SIG_SIZE;
}
}
}
return -1;
}
int guac_common_ssh_verify_host_key(LIBSSH2_SESSION* session, guac_client* client,
const char* host_key, const char* hostname, int port, const char* remote_hostkey,
const size_t remote_hostkey_len) {

View File

@ -376,6 +376,18 @@ int guac_common_ssh_sftp_handle_file_stream(
char fullpath[GUAC_COMMON_SSH_SFTP_MAX_PATH];
LIBSSH2_SFTP_HANDLE* file;
/* Ignore upload if uploads have been disabled */
if (filesystem->disable_upload) {
guac_user_log(user, GUAC_LOG_WARNING, "A upload attempt has "
"been blocked due to uploads being disabled, however it "
"should have been blocked at a higher level. This is likely "
"a bug.");
guac_protocol_send_ack(user->socket, stream, "SFTP: Upload disabled",
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
guac_socket_flush(user->socket);
return 0;
}
/* Concatenate filename with path */
if (!guac_ssh_append_filename(fullpath, filesystem->upload_path,
filename)) {
@ -429,7 +441,7 @@ int guac_common_ssh_sftp_handle_file_stream(
/**
* Handler for ack messages which continue an outbound SFTP data transfer
* (download), signalling the current status and requesting additional data.
* (download), signaling the current status and requesting additional data.
* The data associated with the given stream is expected to be a pointer to an
* open LIBSSH2_SFTP_HANDLE for the file from which the data is to be read.
*
@ -516,6 +528,15 @@ guac_stream* guac_common_ssh_sftp_download_file(
guac_stream* stream;
LIBSSH2_SFTP_HANDLE* file;
/* Ignore download if downloads have been disabled */
if (filesystem->disable_download) {
guac_user_log(user, GUAC_LOG_WARNING, "A download attempt has "
"been blocked due to downloads being disabled, however it "
"should have been blocked at a higher level. This is likely "
"a bug.");
return NULL;
}
/* Attempt to open file for reading */
file = libssh2_sftp_open(filesystem->sftp_session, filename,
LIBSSH2_FXF_READ, 0);
@ -786,6 +807,14 @@ static int guac_common_ssh_sftp_get_handler(guac_user* user,
/* Otherwise, send file contents */
else {
/* If downloads are disabled, log and return. */
if (filesystem->disable_download) {
guac_user_log(user, GUAC_LOG_INFO,
"Unable to download file \"%s\", "
"file downloads have been disabled.", fullpath);
return 0;
}
/* Open as normal file */
LIBSSH2_SFTP_HANDLE* file = libssh2_sftp_open(sftp, fullpath,
LIBSSH2_FXF_READ, 0);
@ -842,6 +871,18 @@ static int guac_common_ssh_sftp_put_handler(guac_user* user,
guac_common_ssh_sftp_filesystem* filesystem =
(guac_common_ssh_sftp_filesystem*) object->data;
/* Ignore upload if uploads have been disabled */
if (filesystem->disable_upload) {
guac_user_log(user, GUAC_LOG_WARNING, "A upload attempt has "
"been blocked due to uploads being disabled, however it "
"should have been blocked at a higher level. This is likely "
"a bug.");
guac_protocol_send_ack(user->socket, stream, "SFTP: Upload disabled",
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
guac_socket_flush(user->socket);
return 0;
}
LIBSSH2_SFTP* sftp = filesystem->sftp_session;
/* Translate stream name into filesystem path */
@ -902,7 +943,11 @@ guac_object* guac_common_ssh_alloc_sftp_filesystem_object(
/* Init filesystem */
guac_object* fs_object = guac_user_alloc_object(user);
fs_object->get_handler = guac_common_ssh_sftp_get_handler;
fs_object->put_handler = guac_common_ssh_sftp_put_handler;
/* Only handle uploads if not disabled. */
if (!filesystem->disable_upload)
fs_object->put_handler = guac_common_ssh_sftp_put_handler;
fs_object->data = filesystem;
/* Send filesystem to user */
@ -915,7 +960,7 @@ guac_object* guac_common_ssh_alloc_sftp_filesystem_object(
guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem(
guac_common_ssh_session* session, const char* root_path,
const char* name) {
const char* name, int disable_download, int disable_upload) {
/* Request SFTP */
LIBSSH2_SFTP* sftp_session = libssh2_sftp_init(session->session);
@ -929,6 +974,10 @@ guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem(
/* Associate SSH session with SFTP data and user */
filesystem->ssh_session = session;
filesystem->sftp_session = sftp_session;
/* Copy over disable flags */
filesystem->disable_download = disable_download;
filesystem->disable_upload = disable_upload;
/* Normalize and store the provided root path */
if (!guac_common_ssh_sftp_normalize_path(filesystem->root_path,

View File

@ -22,6 +22,7 @@
#include "common-ssh/user.h"
#include <guacamole/client.h>
#include <guacamole/fips.h>
#include <libssh2.h>
#ifdef LIBSSH2_USES_GCRYPT
@ -46,6 +47,20 @@
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif
/**
* A list of all key exchange algorithms that are both FIPS-compliant, and
* OpenSSL-supported. Note that "ext-info-c" is also included. While not a key
* exchange algorithm per se, it must be in the list to ensure that the server
* will send a SSH_MSG_EXT_INFO response, which is required to perform RSA key
* upgrades.
*/
#define FIPS_COMPLIANT_KEX_ALGORITHMS "diffie-hellman-group-exchange-sha256,ext-info-c"
/**
* A list of ciphers that are both FIPS-compliant, and OpenSSL-supported.
*/
#define FIPS_COMPLIANT_CIPHERS "aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,aes192-cbc,aes256-cbc"
#ifdef OPENSSL_REQUIRES_THREADING_CALLBACKS
/**
* Array of mutexes, used by OpenSSL.
@ -140,11 +155,21 @@ static void guac_common_ssh_openssl_free_locks(int count) {
int guac_common_ssh_init(guac_client* client) {
#ifdef LIBSSH2_USES_GCRYPT
/* Init threadsafety in libgcrypt */
gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
if (!gcry_check_version(GCRYPT_VERSION)) {
guac_client_log(client, GUAC_LOG_ERROR, "libgcrypt version mismatch.");
return 1;
if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
/* Init threadsafety in libgcrypt */
gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
/* Initialize GCrypt */
if (!gcry_check_version(GCRYPT_VERSION)) {
guac_client_log(client, GUAC_LOG_ERROR, "libgcrypt version mismatch.");
return 1;
}
/* Mark initialization as completed. */
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
#endif
@ -155,9 +180,11 @@ int guac_common_ssh_init(guac_client* client) {
CRYPTO_set_locking_callback(guac_common_ssh_openssl_locking_callback);
#endif
/* Init OpenSSL */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
/* Init OpenSSL - only required for OpenSSL Versions < 1.1.0 */
SSL_library_init();
ERR_load_crypto_strings();
#endif
/* Init libssh2 */
libssh2_init(0);
@ -173,55 +200,6 @@ void guac_common_ssh_uninit() {
#endif
}
/**
* Callback invoked by libssh2 when libssh2_userauth_publickkey() is invoked.
* This callback must sign the given data, returning the signature as newly-
* allocated buffer space.
*
* @param session
* The SSH session for which the signature is being generated.
*
* @param sig
* A pointer to the buffer space containing the signature. This callback
* MUST allocate and assign this space.
*
* @param sig_len
* The length of the signature within the allocated buffer space, in bytes.
* This value must be set to the size of the signature after the signing
* operation completes.
*
* @param data
* The arbitrary data that must be signed.
*
* @param data_len
* The length of the arbitrary data to be signed, in bytes.
*
* @param abstract
* The value of the abstract parameter provided with the corresponding call
* to libssh2_userauth_publickey().
*
* @return
* Zero on success, non-zero if the signing operation failed.
*/
static int guac_common_ssh_sign_callback(LIBSSH2_SESSION* session,
unsigned char** sig, size_t* sig_len,
const unsigned char* data, size_t data_len, void **abstract) {
guac_common_ssh_key* key = (guac_common_ssh_key*) abstract;
int length;
/* Allocate space for signature */
*sig = malloc(4096);
/* Sign with key */
length = guac_common_ssh_key_sign(key, (const char*) data, data_len, *sig);
if (length < 0)
return 1;
*sig_len = length;
return 0;
}
/**
* Callback for the keyboard-interactive authentication method. Currently
* supports just one prompt for the password. This callback is invoked as
@ -314,8 +292,9 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
}
/* Get list of supported authentication methods */
size_t username_len = strlen(user->username);
char* user_authlist = libssh2_userauth_list(session, user->username,
strlen(user->username));
username_len);
/* If auth list is NULL, then authentication has succeeded with NONE */
if (user_authlist == NULL) {
@ -339,9 +318,9 @@ static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session)
}
/* Attempt public key auth */
if (libssh2_userauth_publickey(session, user->username,
(unsigned char*) key->public_key, key->public_key_length,
guac_common_ssh_sign_callback, (void**) key)) {
if (libssh2_userauth_publickey_frommemory(session, user->username,
username_len, NULL, 0, key->private_key,
key->private_key_length, key->passphrase)) {
/* Abort on failure */
char* error_message;
@ -522,6 +501,17 @@ guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client,
return NULL;
}
/*
* If FIPS mode is enabled, prefer only FIPS-compatible algorithms and
* ciphers that are also supported by libssh2. For more info, see:
* https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp2906.pdf
*/
if (guac_fips_enabled()) {
libssh2_session_method_pref(session, LIBSSH2_METHOD_KEX, FIPS_COMPLIANT_KEX_ALGORITHMS);
libssh2_session_method_pref(session, LIBSSH2_METHOD_CRYPT_CS, FIPS_COMPLIANT_CIPHERS);
libssh2_session_method_pref(session, LIBSSH2_METHOD_CRYPT_SC, FIPS_COMPLIANT_CIPHERS);
}
/* Perform handshake */
if (libssh2_session_handshake(session, fd)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,

View File

@ -34,6 +34,7 @@ noinst_HEADERS = \
common/blank_cursor.h \
common/clipboard.h \
common/cursor.h \
common/defaults.h \
common/display.h \
common/dot_cursor.h \
common/ibar_cursor.h \
@ -41,7 +42,6 @@ noinst_HEADERS = \
common/json.h \
common/list.h \
common/pointer_cursor.h \
common/recording.h \
common/rect.h \
common/string.h \
common/surface.h
@ -58,7 +58,6 @@ libguac_common_la_SOURCES = \
json.c \
list.c \
pointer_cursor.c \
recording.c \
rect.c \
string.c \
surface.c

View File

@ -29,15 +29,15 @@
#include <string.h>
#include <stdlib.h>
guac_common_clipboard* guac_common_clipboard_alloc(int size) {
guac_common_clipboard* guac_common_clipboard_alloc() {
guac_common_clipboard* clipboard = malloc(sizeof(guac_common_clipboard));
/* Init clipboard */
clipboard->mimetype[0] = '\0';
clipboard->buffer = malloc(size);
clipboard->buffer = malloc(GUAC_COMMON_CLIPBOARD_MAX_LENGTH);
clipboard->available = GUAC_COMMON_CLIPBOARD_MAX_LENGTH;
clipboard->length = 0;
clipboard->available = size;
pthread_mutex_init(&(clipboard->lock), NULL);
@ -46,7 +46,14 @@ guac_common_clipboard* guac_common_clipboard_alloc(int size) {
}
void guac_common_clipboard_free(guac_common_clipboard* clipboard) {
/* Destroy lock */
pthread_mutex_destroy(&(clipboard->lock));
/* Free buffer */
free(clipboard->buffer);
/* Free base structure */
free(clipboard);
}

View File

@ -31,6 +31,11 @@
*/
#define GUAC_COMMON_CLIPBOARD_BLOCK_SIZE 4096
/**
* The maximum number of bytes to allow within the clipboard.
*/
#define GUAC_COMMON_CLIPBOARD_MAX_LENGTH 262144
/**
* Generic clipboard structure.
*/
@ -66,12 +71,9 @@ typedef struct guac_common_clipboard {
} guac_common_clipboard;
/**
* Creates a new clipboard having the given initial size.
*
* @param size The maximum number of bytes to allow within the clipboard.
* @return A newly-allocated clipboard.
* Creates a new clipboard.
*/
guac_common_clipboard* guac_common_clipboard_alloc(int size);
guac_common_clipboard* guac_common_clipboard_alloc();
/**
* Frees the given clipboard.

View File

@ -17,22 +17,15 @@
* under the License.
*/
#include "config.h"
#ifndef GUAC_COMMON_DEFAULTS_H
#define GUAC_COMMON_DEFAULTS_H
#include <openssl/bn.h>
#include <openssl/rsa.h>
/**
* The default number of seconds to wait after sending the Wake-on-LAN packet
* for the destination host to start responding.
*/
#define GUAC_WOL_DEFAULT_BOOT_WAIT_TIME 0
#include <stdlib.h>
#ifndef HAVE_RSA_GET0_KEY
void RSA_get0_key(const RSA* rsa_key, const BIGNUM** n,
const BIGNUM** e, const BIGNUM**d) {
/* Retrieve all requested internal values */
if (n != NULL) *n = rsa_key->n;
if (e != NULL) *e = rsa_key->e;
if (d != NULL) *d = rsa_key->d;
}
#endif
#endif /* GUAC_COMMON_DEFAULTS_H */

View File

@ -99,6 +99,13 @@ typedef struct guac_common_display {
*/
guac_common_display_layer* buffers;
/**
* Non-zero if all graphical updates for this display should use lossless
* compression, 0 otherwise. By default, newly-created displays will use
* lossy compression when heuristics determine it is appropriate.
*/
int lossless;
/**
* Mutex which is locked internally when access to the display must be
* synchronized. All public functions of guac_common_display should be
@ -228,5 +235,27 @@ void guac_common_display_free_layer(guac_common_display* display,
void guac_common_display_free_buffer(guac_common_display* display,
guac_common_display_layer* display_buffer);
/**
* Sets the overall lossless compression policy of the given display to the
* given value, affecting all current and future layers/buffers maintained by
* the display. By default, newly-created displays will use lossy compression
* for graphical updates when heuristics determine that doing so is
* appropriate. Specifying a non-zero value here will force all graphical
* updates to always use lossless compression, whereas specifying zero will
* restore the default policy.
*
* Note that this can also be adjusted on a per-layer / per-buffer basis with
* guac_common_surface_set_lossless().
*
* @param display
* The display to modify.
*
* @param lossless
* Non-zero if all graphical updates for this display should use lossless
* compression, 0 otherwise.
*/
void guac_common_display_set_lossless(guac_common_display* display,
int lossless);
#endif

View File

@ -76,6 +76,30 @@ guac_iconv_read GUAC_READ_CP1252;
*/
guac_iconv_read GUAC_READ_ISO8859_1;
/**
* Read function for UTF-8 which normalizes newline character sequences like
* "\r\n" to Unix-style newlines ('\n').
*/
guac_iconv_read GUAC_READ_UTF8_NORMALIZED;
/**
* Read function for UTF-16 which normalizes newline character sequences like
* "\r\n" to Unix-style newlines ('\n').
*/
guac_iconv_read GUAC_READ_UTF16_NORMALIZED;
/**
* Read function for CP-1252 which normalizes newline character sequences like
* "\r\n" to Unix-style newlines ('\n').
*/
guac_iconv_read GUAC_READ_CP1252_NORMALIZED;
/**
* Read function for ISO 8859-1 which normalizes newline character sequences
* like "\r\n" to Unix-style newlines ('\n').
*/
guac_iconv_read GUAC_READ_ISO8859_1_NORMALIZED;
/**
* Write function for UTF8.
*/
@ -96,5 +120,29 @@ guac_iconv_write GUAC_WRITE_CP1252;
*/
guac_iconv_write GUAC_WRITE_ISO8859_1;
/**
* Write function for UTF-8 which writes newline characters ('\n') as
* Windows-style newlines ("\r\n").
*/
guac_iconv_write GUAC_WRITE_UTF8_CRLF;
/**
* Write function for UTF-16 which writes newline characters ('\n') as
* Windows-style newlines ("\r\n").
*/
guac_iconv_write GUAC_WRITE_UTF16_CRLF;
/**
* Write function for CP-1252 which writes newline characters ('\n') as
* Windows-style newlines ("\r\n").
*/
guac_iconv_write GUAC_WRITE_CP1252_CRLF;
/**
* Write function for ISO 8859-1 which writes newline characters ('\n') as
* Windows-style newlines ("\r\n").
*/
guac_iconv_write GUAC_WRITE_ISO8859_1_CRLF;
#endif

View File

@ -120,6 +120,19 @@ typedef struct guac_common_surface {
*/
guac_socket* socket;
/**
* The number of simultaneous touches that this surface can accept, where 0
* indicates that the surface does not support touch events at all.
*/
int touches;
/**
* Non-zero if all graphical updates for this surface should use lossless
* compression, 0 otherwise. By default, newly-created surfaces will use
* lossy compression when heuristics determine it is appropriate.
*/
int lossless;
/**
* The X coordinate of the upper-left corner of this layer, in pixels,
* relative to its parent layer. This is only applicable to visible
@ -486,5 +499,41 @@ void guac_common_surface_flush(guac_common_surface* surface);
void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
guac_socket* socket);
/**
* Declares that the given surface should receive touch events. By default,
* surfaces are assumed to not expect touch events. This value is advisory, and
* the client is not required to honor the declared level of touch support.
* Implementations are expected to safely handle or ignore any received touch
* events, regardless of the level of touch support declared. regardless of
* the level of touch support declared.
*
* @param surface
* The surface to modify.
*
* @param touches
* The number of simultaneous touches that this surface can accept, where 0
* indicates that the surface does not support touch events at all.
*/
void guac_common_surface_set_multitouch(guac_common_surface* surface,
int touches);
/**
* Sets the lossless compression policy of the given surface to the given
* value. By default, newly-created surfaces will use lossy compression for
* graphical updates when heuristics determine that doing so is appropriate.
* Specifying a non-zero value here will force all graphical updates to always
* use lossless compression, whereas specifying zero will restore the default
* policy.
*
* @param surface
* The surface to modify.
*
* @param lossless
* Non-zero if all graphical updates for this surface should use lossless
* compression, 0 otherwise.
*/
void guac_common_surface_set_lossless(guac_common_surface* surface,
int lossless);
#endif

View File

@ -166,6 +166,8 @@ void guac_common_display_free(guac_common_display* display) {
void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_socket* socket) {
guac_client* client = user->client;
pthread_mutex_lock(&display->_lock);
/* Sunchronize shared cursor */
@ -178,6 +180,33 @@ void guac_common_display_dup(guac_common_display* display, guac_user* user,
guac_common_display_dup_layers(display->layers, user, socket);
guac_common_display_dup_layers(display->buffers, user, socket);
/* Sends a sync instruction to mark the boundary of the first frame */
guac_protocol_send_sync(socket, client->last_sent_timestamp, 1);
pthread_mutex_unlock(&display->_lock);
}
void guac_common_display_set_lossless(guac_common_display* display,
int lossless) {
pthread_mutex_lock(&display->_lock);
/* Update lossless setting to be applied to all newly-allocated
* layers/buffers */
display->lossless = lossless;
/* Update losslessness of all allocated layers/buffers */
guac_common_display_layer* current = display->layers;
while (current != NULL) {
guac_common_surface_set_lossless(current->surface, lossless);
current = current->next;
}
/* Update losslessness of default display layer (not included within layers
* list) */
guac_common_surface_set_lossless(display->default_surface, lossless);
pthread_mutex_unlock(&display->_lock);
}
@ -287,6 +316,9 @@ guac_common_display_layer* guac_common_display_alloc_layer(
guac_common_surface* surface = guac_common_surface_alloc(display->client,
display->client->socket, layer, width, height);
/* Apply current display losslessness */
guac_common_surface_set_lossless(surface, display->lossless);
/* Add layer and surface to list */
guac_common_display_layer* display_layer =
guac_common_display_add_layer(&display->layers, layer, surface);
@ -308,6 +340,9 @@ guac_common_display_layer* guac_common_display_alloc_buffer(
guac_common_surface* surface = guac_common_surface_alloc(display->client,
display->client->socket, buffer, width, height);
/* Apply current display losslessness */
guac_common_surface_set_lossless(surface, display->lossless);
/* Add buffer and surface to list */
guac_common_display_layer* display_layer =
guac_common_display_add_layer(&display->buffers, buffer, surface);
@ -354,4 +389,3 @@ void guac_common_display_free_buffer(guac_common_display* display,
pthread_mutex_unlock(&display->_lock);
}

View File

@ -138,6 +138,70 @@ int GUAC_READ_ISO8859_1(const char** input, int remaining) {
}
/**
* Invokes the given reader function, automatically normalizing newline
* sequences as Unix-style newline characters ('\n'). All other charaters are
* read verbatim.
*
* @param reader
* The reader to use to read the given character.
*
* @param input
* Pointer to the location within the input buffer that the next character
* should be read from.
*
* @param remaining
* The number of bytes remaining in the input buffer.
*
* @return
* The codepoint that was read, or zero if the end of the input string has
* been reached.
*/
static int guac_iconv_read_normalized(guac_iconv_read* reader,
const char** input, int remaining) {
/* Read requested character */
const char* input_start = *input;
int value = reader(input, remaining);
/* Automatically translate CRLF pairs to simple newlines */
if (value == '\r') {
/* Peek ahead by one character, adjusting remaining bytes relative to
* last read */
int peek_remaining = remaining - (*input - input_start);
const char* peek_input = *input;
int peek_value = reader(&peek_input, peek_remaining);
/* Consider read value to be a newline if we have encountered a "\r\n"
* (CRLF) pair */
if (peek_value == '\n') {
value = '\n';
*input = peek_input;
}
}
return value;
}
int GUAC_READ_UTF8_NORMALIZED(const char** input, int remaining) {
return guac_iconv_read_normalized(GUAC_READ_UTF8, input, remaining);
}
int GUAC_READ_UTF16_NORMALIZED(const char** input, int remaining) {
return guac_iconv_read_normalized(GUAC_READ_UTF16, input, remaining);
}
int GUAC_READ_CP1252_NORMALIZED(const char** input, int remaining) {
return guac_iconv_read_normalized(GUAC_READ_CP1252, input, remaining);
}
int GUAC_READ_ISO8859_1_NORMALIZED(const char** input, int remaining) {
return guac_iconv_read_normalized(GUAC_READ_ISO8859_1, input, remaining);
}
void GUAC_WRITE_UTF8(char** output, int remaining, int value) {
*output += guac_utf8_write(value, *output, remaining);
}
@ -190,3 +254,53 @@ void GUAC_WRITE_ISO8859_1(char** output, int remaining, int value) {
(*output)++;
}
/**
* Invokes the given writer function, automatically writing newline characters
* ('\n') as CRLF ("\r\n"). All other charaters are written verbatim.
*
* @param writer
* The writer to use to write the given character.
*
* @param output
* Pointer to the location within the output buffer that the next character
* should be written.
*
* @param remaining
* The number of bytes remaining in the output buffer.
*
* @param value
* The codepoint of the character to write.
*/
static void guac_iconv_write_crlf(guac_iconv_write* writer, char** output,
int remaining, int value) {
if (value != '\n') {
writer(output, remaining, value);
return;
}
char* output_start = *output;
writer(output, remaining, '\r');
remaining -= *output - output_start;
if (remaining > 0)
writer(output, remaining, '\n');
}
void GUAC_WRITE_UTF8_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_UTF8, output, remaining, value);
}
void GUAC_WRITE_UTF16_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_UTF16, output, remaining, value);
}
void GUAC_WRITE_CP1252_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_CP1252, output, remaining, value);
}
void GUAC_WRITE_ISO8859_1_CRLF(char** output, int remaining, int value) {
guac_iconv_write_crlf(GUAC_WRITE_ISO8859_1, output, remaining, value);
}

View File

@ -97,15 +97,15 @@ int guac_common_json_write_string(guac_user* user,
const char* current = str;
for (; *current != '\0'; current++) {
/* Escape all quotes */
if (*current == '"') {
/* Escape all quotes and back-slashes */
if (*current == '"' || *current == '\\') {
/* Write any string content up to current character */
if (current != str)
blob_written |= guac_common_json_write(user, stream,
json_state, str, current - str);
/* Escape the quote that was just read */
/* Escape the character that was just read */
blob_written |= guac_common_json_write(user, stream,
json_state, "\\", 1);

View File

@ -103,6 +103,28 @@
*/
#define GUAC_SURFACE_WEBP_BLOCK_SIZE 8
void guac_common_surface_set_multitouch(guac_common_surface* surface,
int touches) {
pthread_mutex_lock(&surface->_lock);
surface->touches = touches;
guac_protocol_send_set_int(surface->socket, surface->layer,
GUAC_PROTOCOL_LAYER_PARAMETER_MULTI_TOUCH, touches);
pthread_mutex_unlock(&surface->_lock);
}
void guac_common_surface_set_lossless(guac_common_surface* surface,
int lossless) {
pthread_mutex_lock(&surface->_lock);
surface->lossless = lossless;
pthread_mutex_unlock(&surface->_lock);
}
void guac_common_surface_move(guac_common_surface* surface, int x, int y) {
pthread_mutex_lock(&surface->_lock);
@ -521,6 +543,10 @@ static int __guac_common_surface_png_optimality(guac_common_surface* surface,
static int __guac_common_surface_should_use_jpeg(guac_common_surface* surface,
const guac_common_rect* rect) {
/* Do not use JPEG if lossless quality is required */
if (surface->lossless)
return 0;
/* Calculate the average framerate for the given rect */
int framerate = __guac_common_surface_calculate_framerate(surface, rect);
@ -1793,7 +1819,8 @@ static void __guac_common_surface_flush_to_webp(guac_common_surface* surface,
/* Send WebP for rect */
guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer,
surface->dirty_rect.x, surface->dirty_rect.y, rect,
guac_common_surface_suggest_quality(surface->client), 0);
guac_common_surface_suggest_quality(surface->client),
surface->lossless ? 1 : 0);
cairo_surface_destroy(rect);
surface->realized = 1;
@ -1981,6 +2008,11 @@ void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
guac_protocol_send_move(socket, surface->layer,
surface->parent, surface->x, surface->y, surface->z);
/* Synchronize multi-touch support level */
guac_protocol_send_set_int(surface->socket, surface->layer,
GUAC_PROTOCOL_LAYER_PARAMETER_MULTI_TOUCH,
surface->touches);
}
/* Sync size to new socket */

View File

@ -33,8 +33,12 @@ ACLOCAL_AMFLAGS = -I m4
check_PROGRAMS = test_common
TESTS = $(check_PROGRAMS)
noinst_HEADERS = \
iconv/convert-test-data.h
test_common_SOURCES = \
iconv/convert.c \
iconv/convert-test-data.c \
rect/clip_and_split.c \
rect/constrain.c \
rect/expand_to_grid.c \

View File

@ -0,0 +1,153 @@
/*
* 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 "common/iconv.h"
#include "convert-test-data.h"
encoding_test_parameters test_params[NUM_SUPPORTED_ENCODINGS] = {
/*
* UTF-8
*/
{
"UTF-8",
GUAC_READ_UTF8, GUAC_READ_UTF8_NORMALIZED,
GUAC_WRITE_UTF8, GUAC_WRITE_UTF8_CRLF,
.test_mixed = TEST_STRING(
"pap\xC3\xA0 \xC3\xA8 bello\n"
"pap\xC3\xA0 \xC3\xA8 bello\r\n"
"pap\xC3\xA0 \xC3\xA8 bello\n"
"pap\xC3\xA0 \xC3\xA8 bello\r\n"
"pap\xC3\xA0 \xC3\xA8 bello"
),
.test_unix = TEST_STRING(
"pap\xC3\xA0 \xC3\xA8 bello\n"
"pap\xC3\xA0 \xC3\xA8 bello\n"
"pap\xC3\xA0 \xC3\xA8 bello\n"
"pap\xC3\xA0 \xC3\xA8 bello\n"
"pap\xC3\xA0 \xC3\xA8 bello"
),
.test_windows = TEST_STRING(
"pap\xC3\xA0 \xC3\xA8 bello\r\n"
"pap\xC3\xA0 \xC3\xA8 bello\r\n"
"pap\xC3\xA0 \xC3\xA8 bello\r\n"
"pap\xC3\xA0 \xC3\xA8 bello\r\n"
"pap\xC3\xA0 \xC3\xA8 bello"
)
},
/*
* UTF-16
*/
{
"UTF-16",
GUAC_READ_UTF16, GUAC_READ_UTF16_NORMALIZED,
GUAC_WRITE_UTF16, GUAC_WRITE_UTF16_CRLF,
.test_mixed = TEST_STRING(
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\r\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\r\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00"
"\x00"
),
.test_unix = TEST_STRING(
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00"
"\x00"
),
.test_windows = TEST_STRING(
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\r\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\r\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\r\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00" "\r\x00" "\n\x00"
"p\x00" "a\x00" "p\x00" "\xE0\x00" " \x00" "\xE8\x00" " \x00" "b\x00" "e\x00" "l\x00" "l\x00" "o\x00"
"\x00"
)
},
/*
* ISO 8859-1
*/
{
"ISO 8859-1",
GUAC_READ_ISO8859_1, GUAC_READ_ISO8859_1_NORMALIZED,
GUAC_WRITE_ISO8859_1, GUAC_WRITE_ISO8859_1_CRLF,
.test_mixed = TEST_STRING(
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello"
),
.test_unix = TEST_STRING(
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello"
),
.test_windows = TEST_STRING(
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello"
)
},
/*
* CP-1252
*/
{
"CP-1252",
GUAC_READ_CP1252, GUAC_READ_CP1252_NORMALIZED,
GUAC_WRITE_CP1252, GUAC_WRITE_CP1252_CRLF,
.test_mixed = TEST_STRING(
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello"
),
.test_unix = TEST_STRING(
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello\n"
"pap\xE0 \xE8 bello"
),
.test_windows = TEST_STRING(
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello\r\n"
"pap\xE0 \xE8 bello"
)
}
};

View File

@ -0,0 +1,121 @@
/*
* 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 "common/iconv.h"
/**
* Representation of test string data and its length in bytes.
*/
typedef struct test_string {
/**
* The raw content of the test string.
*/
unsigned char* buffer;
/**
* The number of bytes within the test string, including null terminator.
*/
int size;
} test_string;
/**
* Convenience macro which statically-initializes a test_string with the given
* string value, automatically calculating its size in bytes.
*
* @param value
* The string value.
*/
#define TEST_STRING(value) { \
.buffer = (unsigned char*) (value), \
.size = sizeof(value) \
}
/**
* The parameters applicable to a unit test for a particular encoding supported
* by guac_iconv().
*/
typedef struct encoding_test_parameters {
/**
* The human-readable name of this encoding. This will be logged to the
* test suite log to assist with debugging test failures.
*/
const char* name;
/**
* Reader function which reads using this encoding and does not perform any
* transformation on newline characters.
*/
guac_iconv_read* reader;
/**
* Reader function which reads using this encoding and automatically
* normalizes newline sequences to Unix-style newline characters.
*/
guac_iconv_read* reader_normalized;
/**
* Writer function which writes using this encoding and does not perform
* any transformation on newline characters.
*/
guac_iconv_write* writer;
/**
* Writer function which writes using this encoding, but writes newline
* characters as CRLF sequences.
*/
guac_iconv_write* writer_crlf;
/**
* A test string having both Windows- and Unix-style line endings. Except
* for the line endings, the characters represented within this test string
* must be identical to all other test strings.
*/
test_string test_mixed;
/**
* A test string having only Unix-style line endings. Except for the line
* endings, the characters represented within this test string must be
* identical to all other test strings.
*/
test_string test_unix;
/**
* A test string having only Windows-style line endings. Except for the
* line endings, the characters represented within this test string must be
* identical to all other test strings.
*/
test_string test_windows;
} encoding_test_parameters;
/**
* The total number of encodings supported by guac_iconv().
*/
#define NUM_SUPPORTED_ENCODINGS 4
/**
* Test parameters for each supported encoding. The test strings included each
* consist of five repeated lines of "papà è bello", omitting the line ending
* of the final line.
*/
extern encoding_test_parameters test_params[NUM_SUPPORTED_ENCODINGS];

View File

@ -18,48 +18,10 @@
*/
#include "common/iconv.h"
#include "convert-test-data.h"
#include <CUnit/CUnit.h>
/**
* UTF8 for "papà è bello".
*/
unsigned char test_string_utf8[] = {
'p', 'a', 'p', 0xC3, 0xA0, ' ',
0xC3, 0xA8, ' ',
'b', 'e', 'l', 'l', 'o',
0x00
};
/**
* UTF16 for "papà è bello".
*/
unsigned char test_string_utf16[] = {
'p', 0x00, 'a', 0x00, 'p', 0x00, 0xE0, 0x00, ' ', 0x00,
0xE8, 0x00, ' ', 0x00,
'b', 0x00, 'e', 0x00, 'l', 0x00, 'l', 0x00, 'o', 0x00,
0x00, 0x00
};
/**
* ISO-8859-1 for "papà è bello".
*/
unsigned char test_string_iso8859_1[] = {
'p', 'a', 'p', 0xE0, ' ',
0xE8, ' ',
'b', 'e', 'l', 'l', 'o',
0x00
};
/**
* CP1252 for "papà è bello".
*/
unsigned char test_string_cp1252[] = {
'p', 'a', 'p', 0xE0, ' ',
0xE8, ' ',
'b', 'e', 'l', 'l', 'o',
0x00
};
#include <stdio.h>
/**
* Tests that conversion between character sets using the given guac_iconv_read
@ -69,25 +31,20 @@ unsigned char test_string_cp1252[] = {
* The guac_iconv_read implementation to use to read the input string.
*
* @param in_string
* A pointer to the beginning of the input string.
*
* @param in_length
* The size of the input string in bytes.
* A pointer to the test_string structure describing the input string being
* tested.
*
* @param writer
* The guac_iconv_write implementation to use to write the output string
* (the converted input string).
*
* @param out_string
* A pointer to the beginning of a string which contains the expected
* result of the conversion.
*
* @param out_length
* The size of the expected result in bytes.
* A pointer to the test_string structure describing the expected result of
* the conversion.
*/
static void verify_conversion(
guac_iconv_read* reader, unsigned char* in_string, int in_length,
guac_iconv_write* writer, unsigned char* out_string, int out_length) {
guac_iconv_read* reader, test_string* in_string,
guac_iconv_write* writer, test_string* out_string) {
char output[4096];
char input[4096];
@ -95,91 +52,78 @@ static void verify_conversion(
const char* current_input = input;
char* current_output = output;
memcpy(input, in_string, in_length);
memcpy(input, in_string->buffer, in_string->size);
guac_iconv(reader, &current_input, sizeof(input),
writer, &current_output, sizeof(output));
/* Verify output length */
CU_ASSERT_EQUAL(out_length, current_output - output);
CU_ASSERT_EQUAL(out_string->size, current_output - output);
/* Verify entire input read */
CU_ASSERT_EQUAL(in_length, current_input - input);
CU_ASSERT_EQUAL(in_string->size, current_input - input);
/* Verify output content */
CU_ASSERT_EQUAL(0, memcmp(output, out_string, out_length));
CU_ASSERT_EQUAL(0, memcmp(output, out_string->buffer, out_string->size));
}
/**
* Tests which verifies conversion of UTF-8 to itself.
* Test which verifies that every supported encoding can be correctly converted
* to every other supported encoding, with all line endings preserved verbatim
* (not normalized).
*/
void test_iconv__utf8_to_utf8() {
verify_conversion(
GUAC_READ_UTF8, test_string_utf8, sizeof(test_string_utf8),
GUAC_WRITE_UTF8, test_string_utf8, sizeof(test_string_utf8));
void test_iconv__preserve() {
for (int i = 0; i < NUM_SUPPORTED_ENCODINGS; i++) {
for (int j = 0; j < NUM_SUPPORTED_ENCODINGS; j++) {
encoding_test_parameters* from = &test_params[i];
encoding_test_parameters* to = &test_params[j];
printf("# \"%s\" -> \"%s\" ...\n", from->name, to->name);
verify_conversion(from->reader, &from->test_mixed,
to->writer, &to->test_mixed);
}
}
}
/**
* Tests which verifies conversion of UTF-16 to UTF-8.
* Test which verifies that every supported encoding can be correctly converted
* to every other supported encoding, normalizing all line endings to
* Unix-style line endings.
*/
void test_iconv__utf8_to_utf16() {
verify_conversion(
GUAC_READ_UTF8, test_string_utf8, sizeof(test_string_utf8),
GUAC_WRITE_UTF16, test_string_utf16, sizeof(test_string_utf16));
void test_iconv__normalize_unix() {
for (int i = 0; i < NUM_SUPPORTED_ENCODINGS; i++) {
for (int j = 0; j < NUM_SUPPORTED_ENCODINGS; j++) {
encoding_test_parameters* from = &test_params[i];
encoding_test_parameters* to = &test_params[j];
printf("# \"%s\" -> \"%s\" ...\n", from->name, to->name);
verify_conversion(from->reader_normalized, &from->test_mixed,
to->writer, &to->test_unix);
}
}
}
/**
* Tests which verifies conversion of UTF-16 to itself.
* Test which verifies that every supported encoding can be correctly converted
* to every other supported encoding, normalizing all line endings to
* Windows-style line endings.
*/
void test_iconv__utf16_to_utf16() {
verify_conversion(
GUAC_READ_UTF16, test_string_utf16, sizeof(test_string_utf16),
GUAC_WRITE_UTF16, test_string_utf16, sizeof(test_string_utf16));
}
/**
* Tests which verifies conversion of UTF-8 to UTF-16.
*/
void test_iconv__utf16_to_utf8() {
verify_conversion(
GUAC_READ_UTF16, test_string_utf16, sizeof(test_string_utf16),
GUAC_WRITE_UTF8, test_string_utf8, sizeof(test_string_utf8));
}
/**
* Tests which verifies conversion of UTF-16 to ISO 8859-1.
*/
void test_iconv__utf16_to_iso8859_1() {
verify_conversion(
GUAC_READ_UTF16, test_string_utf16, sizeof(test_string_utf16),
GUAC_WRITE_ISO8859_1, test_string_iso8859_1, sizeof(test_string_iso8859_1));
}
/**
* Tests which verifies conversion of UTF-16 to CP1252.
*/
void test_iconv__utf16_to_cp1252() {
verify_conversion(
GUAC_READ_UTF16, test_string_utf16, sizeof(test_string_utf16),
GUAC_WRITE_CP1252, test_string_cp1252, sizeof(test_string_cp1252));
}
/**
* Tests which verifies conversion of CP1252 to UTF-8.
*/
void test_iconv__cp1252_to_utf8() {
verify_conversion(
GUAC_READ_CP1252, test_string_cp1252, sizeof(test_string_cp1252),
GUAC_WRITE_UTF8, test_string_utf8, sizeof(test_string_utf8));
}
/**
* Tests which verifies conversion of ISO 8859-1 to UTF-8.
*/
void test_iconv__iso8859_1_to_utf8() {
verify_conversion(
GUAC_READ_ISO8859_1, test_string_iso8859_1, sizeof(test_string_iso8859_1),
GUAC_WRITE_UTF8, test_string_utf8, sizeof(test_string_utf8));
void test_iconv__normalize_crlf() {
for (int i = 0; i < NUM_SUPPORTED_ENCODINGS; i++) {
for (int j = 0; j < NUM_SUPPORTED_ENCODINGS; j++) {
encoding_test_parameters* from = &test_params[i];
encoding_test_parameters* to = &test_params[j];
printf("# \"%s\" -> \"%s\" ...\n", from->name, to->name);
verify_conversion(from->reader_normalized, &from->test_mixed,
to->writer_crlf, &to->test_windows);
}
}
}

115
src/guacd-docker/bin/build-all.sh Executable file
View File

@ -0,0 +1,115 @@
#!/bin/sh -e
#
# 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.
#
##
## @fn build-all.sh
##
## Builds the source of guacamole-server and its various core protocol library
## dependencies.
##
# Pre-populate build control variables such that the custom build prefix is
# used for C headers, locating libraries, etc.
export CFLAGS="-I${PREFIX_DIR}/include"
export LDFLAGS="-L${PREFIX_DIR}/lib"
export PKG_CONFIG_PATH="${PREFIX_DIR}/lib/pkgconfig"
# Ensure thread stack size will be 8 MB (glibc's default on Linux) rather than
# 128 KB (musl's default)
export LDFLAGS="$LDFLAGS -Wl,-z,stack-size=8388608"
##
## Builds and installs the source at the given git repository, automatically
## switching to the version of the source at the tag/commit that matches the
## given pattern.
##
## @param URL
## The URL of the git repository that the source should be downloaded from.
##
## @param PATTERN
## The Perl-compatible regular expression that the tag must match. If no
## tag matches the regular expression, the pattern is assumed to be an
## exact reference to a commit, branch, etc. acceptable by git checkout.
##
## @param ...
## Any additional command-line options that should be provided to CMake or
## the configure script.
##
install_from_git() {
URL="$1"
PATTERN="$2"
shift 2
# Calculate top-level directory name of resulting repository from the
# provided URL
REPO_DIR="$(basename "$URL" .git)"
# Allow dependencies to be manually omitted with the tag/commit pattern "NO"
if [ "$PATTERN" = "NO" ]; then
echo "NOT building $REPO_DIR (explicitly skipped)"
return
fi
# Clone repository and change to top-level directory of source
cd /tmp
git clone "$URL"
cd $REPO_DIR/
# Locate tag/commit based on provided pattern
VERSION="$(git tag -l --sort=-v:refname | grep -Px -m1 "$PATTERN" \
|| echo "$PATTERN")"
# Switch to desired version of source
echo "Building $REPO_DIR @ $VERSION ..."
git -c advice.detachedHead=false checkout "$VERSION"
# Configure build using CMake or GNU Autotools, whichever happens to be
# used by the library being built
if [ -e CMakeLists.txt ]; then
cmake -DCMAKE_INSTALL_PREFIX:PATH="$PREFIX_DIR" "$@" .
else
[ -e configure ] || autoreconf -fi
./configure --prefix="$PREFIX_DIR" "$@"
fi
# Build and install
make && make install
}
#
# Build and install core protocol library dependencies
#
install_from_git "https://github.com/FreeRDP/FreeRDP" "$WITH_FREERDP" $FREERDP_OPTS
install_from_git "https://github.com/libssh2/libssh2" "$WITH_LIBSSH2" $LIBSSH2_OPTS
install_from_git "https://github.com/seanmiddleditch/libtelnet" "$WITH_LIBTELNET" $LIBTELNET_OPTS
install_from_git "https://github.com/LibVNC/libvncserver" "$WITH_LIBVNCCLIENT" $LIBVNCCLIENT_OPTS
install_from_git "https://libwebsockets.org/repo/libwebsockets" "$WITH_LIBWEBSOCKETS" $LIBWEBSOCKETS_OPTS
#
# Build guacamole-server
#
cd "$BUILD_DIR"
autoreconf -fi && ./configure --prefix="$PREFIX_DIR" $GUACAMOLE_SERVER_OPTS
make && make install

View File

@ -1,86 +0,0 @@
#!/bin/sh -e
#
# 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.
#
##
## @fn link-freerdp-plugins.sh
##
## Automatically creates any required symbolic links for the proper loading of
## the given FreeRDP plugins. If a given plugin is already in the correct
## directory, no link is created for that plugin.
##
## @param ...
## The FreeRDP plugins to add links for.
##
##
## Given the full path to a FreeRDP plugin, locates the base directory of the
## associated FreeRDP installation (where the FreeRDP library .so files are
## located), printing the result to STDOUT. If the directory cannot be
## determined, an error is printed.
##
## @param PLUGIN_FILE
## The full path to the FreeRDP plugin to check.
##
where_is_freerdp() {
PLUGIN_FILE="$1"
# Determine the location of all libfreerdp* libraries explicitly linked
# to given file
PATHS="$(ldd "$PLUGIN_FILE" \
| awk '/=>/{print $(NF-1)}' \
| grep 'libfreerdp' \
| xargs -r dirname \
| xargs -r realpath \
| sort -u)"
# Verify that exactly one location was found
if [ "$(echo "$PATHS" | wc -l)" != 1 ]; then
echo "$1: Unable to locate FreeRDP install location." >&2
return 1
fi
echo "$PATHS"
}
#
# Create symbolic links as necessary to include all given plugins within the
# search path of FreeRDP
#
while [ -n "$1" ]; do
# Determine correct install location for FreeRDP plugins
FREERDP_DIR="$(where_is_freerdp "$1")"
FREERDP_PLUGIN_DIR="${FREERDP_DIR}/freerdp2"
# Add symbolic link if necessary
if [ ! -e "$FREERDP_PLUGIN_DIR/$(basename "$1")" ]; then
mkdir -p "$FREERDP_PLUGIN_DIR"
ln -s "$1" "$FREERDP_PLUGIN_DIR"
else
echo "$1: Already in correct directory." >&2
fi
shift
done

View File

@ -21,7 +21,7 @@
##
## @fn list-dependencies.sh
##
## Lists the Debian/Ubuntu package names for all library dependencies of the
## Lists the Alpine Linux package names for all library dependencies of the
## given binaries. Each package is only listed once, even if multiple binaries
## provided by the same package are given.
##
@ -35,14 +35,17 @@ while [ -n "$1" ]; do
ldd "$1" | grep -v 'libguac' | awk '/=>/{print $(NF-1)}' \
| while read LIBRARY; do
# Determine the Debian package which is associated with that
# library, if any
dpkg-query -S "$LIBRARY" 2> /dev/null || true
# List the package providing that library, if any
apk info -W "$LIBRARY" 2> /dev/null \
| grep 'is owned by' | grep -o '[^ ]*$' || true
done
# Next binary
shift
done | cut -f1 -d: | sort -u
# Strip the "-VERSION" suffix from each package name, listing each resulting
# package uniquely ("apk add" cannot handle package names that include the
# version number)
done | sed 's/\(.*\)-[0-9]\+\..*$/\1/' | sort -u

View File

@ -176,8 +176,8 @@ guacd_config* guacd_conf_load() {
return NULL;
/* Load defaults */
conf->bind_host = NULL;
conf->bind_port = strdup("4822");
conf->bind_host = strdup(GUACD_DEFAULT_BIND_HOST);
conf->bind_port = strdup(GUACD_DEFAULT_BIND_PORT);
conf->pidfile = NULL;
conf->foreground = 0;
conf->print_version = 0;

View File

@ -24,6 +24,18 @@
#include <guacamole/client.h>
/**
* The default host that guacd should bind to, if no other host is explicitly
* specified.
*/
#define GUACD_DEFAULT_BIND_HOST "localhost"
/**
* The default port that guacd should bind to, if no other port is explicitly
* specified.
*/
#define GUACD_DEFAULT_BIND_PORT "4822"
/**
* The contents of a guacd configuration file.
*/

View File

@ -278,10 +278,13 @@ static int guacd_route_connection(guacd_proc_map* map, guac_socket* socket) {
proc = guacd_proc_map_retrieve(map, identifier);
new_process = 0;
/* Warn if requested connection does not exist */
if (proc == NULL)
guacd_log(GUAC_LOG_INFO, "Connection \"%s\" does not exist.",
identifier);
/* Warn and ward off client if requested connection does not exist */
if (proc == NULL) {
guacd_log(GUAC_LOG_INFO, "Connection \"%s\" does not exist", identifier);
guac_protocol_send_error(socket, "No such connection.",
GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND);
}
else
guacd_log(GUAC_LOG_INFO, "Joining existing connection \"%s\"",
identifier);

View File

@ -304,20 +304,6 @@ int main(int argc, char* argv[]) {
}
/* Get socket */
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd < 0) {
guacd_log(GUAC_LOG_ERROR, "Error opening socket: %s", strerror(errno));
exit(EXIT_FAILURE);
}
/* Allow socket reuse */
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,
(void*) &opt_on, sizeof(opt_on))) {
guacd_log(GUAC_LOG_WARNING, "Unable to set socket options for reuse: %s",
strerror(errno));
}
/* Attempt binding of each address until success */
current_address = addresses;
while (current_address != NULL) {
@ -333,27 +319,47 @@ int main(int argc, char* argv[]) {
guacd_log(GUAC_LOG_ERROR, "Unable to resolve host: %s",
gai_strerror(retval));
/* Get socket */
socket_fd = socket(current_address->ai_family, SOCK_STREAM, 0);
if (socket_fd < 0) {
guacd_log(GUAC_LOG_ERROR, "Error opening socket: %s", strerror(errno));
/* Unable to get a socket for the resolved address family, try next */
current_address = current_address->ai_next;
continue;
}
/* Allow socket reuse */
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,
(void*) &opt_on, sizeof(opt_on))) {
guacd_log(GUAC_LOG_WARNING, "Unable to set socket options for reuse: %s",
strerror(errno));
}
/* Attempt to bind socket to address */
if (bind(socket_fd,
current_address->ai_addr,
current_address->ai_addrlen) == 0) {
guacd_log(GUAC_LOG_DEBUG, "Successfully bound socket to "
"host %s, port %s", bound_address, bound_port);
guacd_log(GUAC_LOG_DEBUG, "Successfully bound "
"%s socket to host %s, port %s",
(current_address->ai_family == AF_INET) ? "AF_INET" : "AF_INET6",
bound_address, bound_port);
/* Done if successful bind */
break;
}
/* Otherwise log information regarding bind failure */
else
guacd_log(GUAC_LOG_DEBUG, "Unable to bind socket to "
"host %s, port %s: %s",
bound_address, bound_port, strerror(errno));
close(socket_fd);
socket_fd = -1;
guacd_log(GUAC_LOG_DEBUG, "Unable to bind %s socket to "
"host %s, port %s: %s",
(current_address->ai_family == AF_INET) ? "AF_INET" : "AF_INET6",
bound_address, bound_port, strerror(errno));
/* Try next address */
current_address = current_address->ai_next;
}
/* If unable to bind to anything, fail */
@ -375,10 +381,15 @@ int main(int argc, char* argv[]) {
CRYPTO_set_locking_callback(guacd_openssl_locking_callback);
#endif
/* Init SSL */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
/* Init OpenSSL for OpenSSL Versions < 1.1.0 */
SSL_library_init();
SSL_load_error_strings();
ssl_context = SSL_CTX_new(SSLv23_server_method());
#else
/* Set up OpenSSL for OpenSSL Versions >= 1.1.0 */
ssl_context = SSL_CTX_new(TLS_server_method());
#endif
/* Load key */
if (config->key_file != NULL) {

View File

@ -133,7 +133,7 @@ void guacd_log_guac_error(guac_client_log_level level, const char* message) {
void guacd_log_handshake_failure() {
if (guac_error == GUAC_STATUS_CLOSED)
guacd_log(GUAC_LOG_INFO,
guacd_log(GUAC_LOG_DEBUG,
"Guacamole connection closed during handshake");
else if (guac_error == GUAC_STATUS_PROTOCOL_ERROR)
guacd_log(GUAC_LOG_ERROR,

View File

@ -330,6 +330,9 @@ static void guacd_exec_proc(guacd_proc* proc, const char* protocol) {
/* The first file descriptor is the owner */
int owner = 1;
/* Enable keep alive on the broadcast socket */
guac_socket_require_keep_alive(client->socket);
/* Add each received file descriptor as a new user */
int received_fd;
while ((received_fd = guacd_recv_fd(proc->fd_socket)) != -1) {
@ -340,7 +343,7 @@ static void guacd_exec_proc(guacd_proc* proc, const char* protocol) {
owner = 0;
}
cleanup_client:
/* Request client to stop/disconnect */

View File

@ -90,6 +90,7 @@ endif
guacenc_CFLAGS = \
-Werror -Wall \
@AVCODEC_CFLAGS@ \
@AVFORMAT_CFLAGS@ \
@AVUTIL_CFLAGS@ \
@LIBGUAC_INCLUDE@ \
@SWSCALE_CFLAGS@
@ -97,12 +98,13 @@ guacenc_CFLAGS = \
guacenc_LDADD = \
@LIBGUAC_LTLIB@
guacenc_LDFLAGS = \
@AVCODEC_LIBS@ \
@AVUTIL_LIBS@ \
@CAIRO_LIBS@ \
@JPEG_LIBS@ \
@SWSCALE_LIBS@ \
guacenc_LDFLAGS = \
@AVCODEC_LIBS@ \
@AVFORMAT_LIBS@ \
@AVUTIL_LIBS@ \
@CAIRO_LIBS@ \
@JPEG_LIBS@ \
@SWSCALE_LIBS@ \
@WEBP_LIBS@
EXTRA_DIST = \

View File

@ -51,8 +51,41 @@
*/
static int guacenc_write_packet(guacenc_video* video, void* data, int size) {
/* Write data, logging any errors */
if (fwrite(data, 1, size, video->output) == 0) {
int ret;
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,1,0)
AVPacket pkt;
/* Have to create a packet around the encoded data we have */
av_init_packet(&pkt);
if (video->context->coded_frame->pts != AV_NOPTS_VALUE) {
pkt.pts = av_rescale_q(video->context->coded_frame->pts,
video->context->time_base,
video->output_stream->time_base);
}
if (video->context->coded_frame->key_frame) {
pkt->flags |= AV_PKT_FLAG_KEY;
}
pkt.data = data;
pkt.size = size;
pkt.stream_index = video->output_stream->index;
ret = av_interleaved_write_frame(video->container_format_context, &pkt);
#else
/* We know data is already a packet if we're using a newer libavcodec */
AVPacket* pkt = (AVPacket*) data;
av_packet_rescale_ts(pkt, video->context->time_base, video->output_stream->time_base);
pkt->stream_index = video->output_stream->index;
ret = av_interleaved_write_frame(video->container_format_context, pkt);
#endif
if (ret != 0) {
guacenc_log(GUAC_LOG_ERROR, "Unable to write frame "
"#%" PRId64 ": %s", video->next_pts, strerror(errno));
return -1;
@ -62,8 +95,7 @@ static int guacenc_write_packet(guacenc_video* video, void* data, int size) {
guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": wrote %i bytes",
video->next_pts, size);
return 0;
return ret;
}
int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
@ -103,6 +135,15 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
#else
/* For libavcodec < 57.37.100: input/output was not decoupled and static
* allocation of AVPacket was supported.
*
* NOTE: Since dynamic allocation of AVPacket was added before this point (in
* 57.12.100) and static allocation was deprecated later (in 58.133.100), it is
* convenient to tie static vs. dynamic allocation to the old vs. new I/O
* mechanism and avoid further complicating the version comparison logic. */
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 37, 100)
/* Init video packet */
AVPacket packet;
av_init_packet(&packet);
@ -111,8 +152,6 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
packet.data = NULL;
packet.size = 0;
/* For libavcodec < 57.37.100: input/output was not decoupled */
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57,37,100)
/* Write frame to video */
int got_data;
if (avcodec_encode_video2(video->context, &packet, frame, &got_data) < 0) {
@ -123,10 +162,12 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
/* Write corresponding data to file */
if (got_data) {
guacenc_write_packet(video, packet.data, packet.size);
guacenc_write_packet(video, (void*) &packet, packet.size);
av_packet_unref(&packet);
}
#else
/* Write frame to video */
int result = avcodec_send_frame(video->context, frame);
@ -141,18 +182,25 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
return -1;
}
AVPacket* packet = av_packet_alloc();
if (packet == NULL)
return -1;
/* Flush all available packets */
int got_data = 0;
while (avcodec_receive_packet(video->context, &packet) == 0) {
while (avcodec_receive_packet(video->context, packet) == 0) {
/* Data was received */
got_data = 1;
/* Attempt to write data to output file */
guacenc_write_packet(video, packet.data, packet.size);
av_packet_unref(&packet);
guacenc_write_packet(video, (void*) packet, packet->size);
av_packet_unref(packet);
}
av_packet_free(&packet);
#endif
/* Frame may have been queued for later writing / reordering */
@ -165,3 +213,54 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
#endif
}
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, const AVCodec* codec,
int bitrate, int width, int height, int gop_size, int qmax, int qmin,
int pix_fmt, AVRational time_base) {
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(57, 33, 100)
stream->codec->bit_rate = bitrate;
stream->codec->width = width;
stream->codec->height = height;
stream->codec->gop_size = gop_size;
stream->codec->qmax = qmax;
stream->codec->qmin = qmin;
stream->codec->pix_fmt = pix_fmt;
stream->codec->time_base = time_base;
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 44, 100)
stream->time_base = time_base;
#endif
return stream->codec;
#else
AVCodecContext* context = avcodec_alloc_context3(codec);
if (context) {
context->bit_rate = bitrate;
context->width = width;
context->height = height;
context->gop_size = gop_size;
context->qmax = qmax;
context->qmin = qmin;
context->pix_fmt = pix_fmt;
context->time_base = time_base;
stream->time_base = time_base;
}
return context;
#endif
}
int guacenc_open_avcodec(AVCodecContext *avcodec_context,
const AVCodec *codec, AVDictionary **options,
AVStream* stream) {
int ret = avcodec_open2(avcodec_context, codec, options);
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100)
/* Copy stream parameters to the muxer */
int codecpar_ret = avcodec_parameters_from_context(stream->codecpar, avcodec_context);
if (codecpar_ret < 0)
return codecpar_ret;
#endif
return ret;
}

View File

@ -52,6 +52,16 @@
#define av_packet_unref av_free_packet
#endif
/* For libavcodec <= 56.41.100: Global header flag didn't have AV_ prefix.
* Guacenc defines its own flag here to avoid conflicts with libavcodec
* macros.
*/
#if LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(56,41,100)
#define GUACENC_FLAG_GLOBAL_HEADER CODEC_FLAG_GLOBAL_HEADER
#else
#define GUACENC_FLAG_GLOBAL_HEADER AV_CODEC_FLAG_GLOBAL_HEADER
#endif
/* For libavutil < 51.42.0: AV_PIX_FMT_* was PIX_FMT_* */
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51,42,0)
#define AV_PIX_FMT_RGB32 PIX_FMT_RGB32
@ -78,5 +88,78 @@
*/
int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame);
/**
* Creates and sets up the AVCodecContext for the appropriate version of
* libavformat installed. The AVCodecContext will be built, but the AVStream
* will also be affected by having its time_base field set to the value passed
* into this function.
*
* @param stream
* The open AVStream.
*
* @param codec
* The codec used on the AVStream.
*
* @param bitrate
* The target bitrate for the encoded video
*
* @param width
* The target width for the encoded video.
*
* @param height
* The target height for the encoded video.
*
* @param gop_size
* The size of the Group of Pictures.
*
* @param qmax
* The max value of the quantizer.
*
* @param qmin
* The min value of the quantizer.
*
* @param pix_fmt
* The target pixel format for the encoded video.
*
* @param time_base
* The target time base for the encoded video.
*
* @return
* The pointer to the configured AVCodecContext.
*
*/
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, const AVCodec* codec,
int bitrate, int width, int height, int gop_size, int qmax, int qmin,
int pix_fmt, AVRational time_base);
/**
* A wrapper for avcodec_open2(). Because libavformat ver 57.33.100 and greater
* use stream->codecpar rather than stream->codec to handle information to the
* codec, there needs to be an additional step in that version. So this
* wrapper handles that. Otherwise, it's the same as avcodec_open2().
*
* @param avcodec_context
* The context to initialize.
*
* @param codec
* The codec to open this context for. If a non-NULL codec has been
* previously passed to avcodec_alloc_context3() or for this context, then
* this parameter MUST be either NULL or equal to the previously passed
* codec.
*
* @param options
* A dictionary filled with AVCodecContext and codec-private options. On
* return this object will be filled with options that were not found.
*
* @param stream
* The stream for the codec context.
*
* @return
* Zero on success, a negative value on error.
*/
int guacenc_open_avcodec(AVCodecContext *avcodec_context,
const AVCodec *codec, AVDictionary **options,
AVStream* stream);
#endif

View File

@ -25,6 +25,7 @@
#include "parse.h"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <getopt.h>
#include <stdbool.h>
@ -80,6 +81,10 @@ int main(int argc, char* argv[]) {
avcodec_register_all();
#endif
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
av_register_all();
#endif
/* Track number of overall failures */
int total_files = argc - optind;
int failures = 0;

View File

@ -38,7 +38,7 @@ is essentially an implementation of a Guacamole client which accepts
its input from files instead of a network connection, and renders directly to
video instead of to the user's screen.
.P
Each \fIFILE\fR specified will be encoded as a raw MPEG-4 video stream to a new
Each \fIFILE\fR specified will be encoded as MPEG-4 video to a new
file named \fIFILE\fR.m4v, encoded according to the other options specified. By
default, the output video will be \fI640\fRx\fI480\fR pixels, and will be saved
with a bitrate of \fI2000000\fR bits per second (2 Mbps). These defaults can be

View File

@ -25,6 +25,9 @@
#include <cairo/cairo.h>
#include <libavcodec/avcodec.h>
#ifndef AVFORMAT_AVFORMAT_H
#include <libavformat/avformat.h>
#endif
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
@ -34,42 +37,68 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
int width, int height, int bitrate) {
const AVOutputFormat *container_format;
AVFormatContext *container_format_context;
AVStream *video_stream;
int ret;
int failed_header = 0;
/* allocate the output media context */
avformat_alloc_output_context2(&container_format_context, NULL, NULL, path);
if (container_format_context == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Failed to determine container from output file name");
goto fail_codec;
}
container_format = container_format_context->oformat;
/* Pull codec based on name */
AVCodec* codec = avcodec_find_encoder_by_name(codec_name);
const AVCodec* codec = avcodec_find_encoder_by_name(codec_name);
if (codec == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Failed to locate codec \"%s\".",
codec_name);
goto fail_codec;
}
/* create stream */
video_stream = NULL;
video_stream = avformat_new_stream(container_format_context, codec);
if (video_stream == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Could not allocate encoder stream. Cannot continue.");
goto fail_format_context;
}
video_stream->id = container_format_context->nb_streams - 1;
/* Retrieve encoding context */
AVCodecContext* context = avcodec_alloc_context3(codec);
if (context == NULL) {
AVCodecContext* avcodec_context =
guacenc_build_avcodeccontext(video_stream, codec, bitrate, width,
height, /*gop size*/ 10, /*qmax*/ 31, /*qmin*/ 2,
/*pix fmt*/ AV_PIX_FMT_YUV420P,
/*time base*/ (AVRational) { 1, GUACENC_VIDEO_FRAMERATE });
if (avcodec_context == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Failed to allocate context for "
"codec \"%s\".", codec_name);
goto fail_context;
}
/* Init context with encoding parameters */
context->bit_rate = bitrate;
context->width = width;
context->height = height;
context->time_base = (AVRational) { 1, GUACENC_VIDEO_FRAMERATE };
context->gop_size = 10;
context->max_b_frames = 1;
context->pix_fmt = AV_PIX_FMT_YUV420P;
/* If format needs global headers, write them */
if (container_format_context->oformat->flags & AVFMT_GLOBALHEADER) {
avcodec_context->flags |= GUACENC_FLAG_GLOBAL_HEADER;
}
/* Open codec for use */
if (avcodec_open2(context, codec, NULL) < 0) {
if (guacenc_open_avcodec(avcodec_context, codec, NULL, video_stream) < 0) {
guacenc_log(GUAC_LOG_ERROR, "Failed to open codec \"%s\".", codec_name);
goto fail_codec_open;
}
@ -81,9 +110,9 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
}
/* Copy necessary data for frame from context */
frame->format = context->pix_fmt;
frame->width = context->width;
frame->height = context->height;
frame->format = avcodec_context->pix_fmt;
frame->width = avcodec_context->width;
frame->height = avcodec_context->height;
/* Allocate actual backing data for frame */
if (av_image_alloc(frame->data, frame->linesize, frame->width,
@ -91,31 +120,32 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
goto fail_frame_data;
}
/* Open output file */
int fd = open(path, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
if (fd == -1) {
guacenc_log(GUAC_LOG_ERROR, "Failed to open output file \"%s\": %s",
path, strerror(errno));
goto fail_output_fd;
/* Open output file, if the container needs it */
if (!(container_format->flags & AVFMT_NOFILE)) {
ret = avio_open(&container_format_context->pb, path, AVIO_FLAG_WRITE);
if (ret < 0) {
guacenc_log(GUAC_LOG_ERROR, "Error occurred while opening output file.");
goto fail_output_avio;
}
}
/* Create stream for output file */
FILE* output = fdopen(fd, "wb");
if (output == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Failed to allocate stream for output "
"file \"%s\": %s", path, strerror(errno));
/* write the stream header, if needed */
ret = avformat_write_header(container_format_context, NULL);
if (ret < 0) {
guacenc_log(GUAC_LOG_ERROR, "Error occurred while writing output file header.");
failed_header = true;
goto fail_output_file;
}
/* Allocate video structure */
guacenc_video* video = malloc(sizeof(guacenc_video));
if (video == NULL) {
goto fail_video;
}
if (video == NULL)
goto fail_alloc_video;
/* Init properties of video */
video->output = output;
video->context = context;
video->output_stream = video_stream;
video->context = avcodec_context;
video->container_format_context = container_format_context;
video->next_frame = frame;
video->width = width;
video->height = height;
@ -128,13 +158,16 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
return video;
/* Free all allocated data in case of failure */
fail_video:
fclose(output);
fail_alloc_video:
fail_output_file:
close(fd);
avio_close(container_format_context->pb);
fail_output_fd:
/* Delete the file that was created if it was actually created */
if (unlink(path) == -1 && errno != ENOENT)
guacenc_log(GUAC_LOG_WARNING, "Failed output file \"%s\" could not "
"be automatically deleted: %s", path, strerror(errno));
fail_output_avio:
av_freep(&frame->data[0]);
fail_frame_data:
@ -142,7 +175,13 @@ fail_frame_data:
fail_frame:
fail_codec_open:
avcodec_free_context(&context);
avcodec_free_context(&avcodec_context);
fail_format_context:
/* failing to write the container implicitly frees the context */
if (!failed_header) {
avformat_free_context(container_format_context);
}
fail_context:
fail_codec:
@ -435,26 +474,34 @@ int guacenc_video_free(guacenc_video* video) {
/* Write final frame */
guacenc_video_flush_frame(video);
/* Init video packet for final flush of encoded data */
AVPacket packet;
av_init_packet(&packet);
/* Flush any unwritten frames */
int retval;
do {
retval = guacenc_video_write_frame(video, NULL);
} while (retval > 0);
/* write trailer, if needed */
if (video->container_format_context != NULL &&
video->output_stream != NULL) {
guacenc_log(GUAC_LOG_DEBUG, "Writing trailer: %s",
av_write_trailer(video->container_format_context) == 0 ?
"success" : "failure");
}
/* File is now completely written */
fclose(video->output);
if (video->container_format_context != NULL) {
avio_close(video->container_format_context->pb);
}
/* Free frame encoding data */
av_freep(&video->next_frame->data[0]);
av_frame_free(&video->next_frame);
/* Clean up encoding context */
avcodec_close(video->context);
avcodec_free_context(&(video->context));
if (video->context != NULL) {
avcodec_close(video->context);
avcodec_free_context(&(video->context));
}
free(video);
return 0;

View File

@ -26,6 +26,14 @@
#include <guacamole/timestamp.h>
#include <libavcodec/avcodec.h>
#ifndef AVCODEC_AVCODEC_H
#include <libavcodec/avcodec.h>
#endif
#ifndef AVFORMAT_AVFORMAT_H
#include <libavformat/avformat.h>
#endif
#include <stdint.h>
#include <stdio.h>
@ -42,9 +50,11 @@
typedef struct guacenc_video {
/**
* Output file stream.
* AVStream for video output.
* Frames sent to this stream are written into
* the output file in the specified container format.
*/
FILE* output;
AVStream* output_stream;
/**
* The open encoding context from libavcodec, created for the codec
@ -52,6 +62,12 @@ typedef struct guacenc_video {
*/
AVCodecContext* context;
/**
* The open format context from libavformat, created for the file
* container specified when this guacenc_video was created.
*/
AVFormatContext* container_format_context;
/**
* The width of the video, in pixels.
*/

View File

@ -32,6 +32,9 @@ SUBDIRS = . tests
libguacincdir = $(includedir)/guacamole
libguacinc_HEADERS = \
guacamole/argv.h \
guacamole/argv-constants.h \
guacamole/argv-fntypes.h \
guacamole/audio.h \
guacamole/audio-fntypes.h \
guacamole/audio-types.h \
@ -41,6 +44,7 @@ libguacinc_HEADERS = \
guacamole/client-types.h \
guacamole/error.h \
guacamole/error-types.h \
guacamole/fips.h \
guacamole/hash.h \
guacamole/layer.h \
guacamole/layer-types.h \
@ -56,6 +60,7 @@ libguacinc_HEADERS = \
guacamole/protocol.h \
guacamole/protocol-constants.h \
guacamole/protocol-types.h \
guacamole/recording.h \
guacamole/socket-constants.h \
guacamole/socket.h \
guacamole/socket-fntypes.h \
@ -69,7 +74,9 @@ libguacinc_HEADERS = \
guacamole/user.h \
guacamole/user-constants.h \
guacamole/user-fntypes.h \
guacamole/user-types.h
guacamole/user-types.h \
guacamole/wol.h \
guacamole/wol-constants.h
noinst_HEADERS = \
id.h \
@ -81,11 +88,13 @@ noinst_HEADERS = \
wait-fd.h
libguac_la_SOURCES = \
argv.c \
audio.c \
client.c \
encode-jpeg.c \
encode-png.c \
error.c \
fips.c \
hash.c \
id.c \
palette.c \
@ -93,6 +102,7 @@ libguac_la_SOURCES = \
pool.c \
protocol.c \
raw_encoder.c \
recording.c \
socket.c \
socket-broadcast.c \
socket-fd.c \
@ -104,7 +114,8 @@ libguac_la_SOURCES = \
user.c \
user-handlers.c \
user-handshake.c \
wait-fd.c
wait-fd.c \
wol.c
# Compile WebP support if available
if ENABLE_WEBP
@ -128,7 +139,7 @@ libguac_la_CFLAGS = \
-Werror -Wall -pedantic
libguac_la_LDFLAGS = \
-version-info 17:0:0 \
-version-info 21:0:0 \
-no-undefined \
@CAIRO_LIBS@ \
@DL_LIBS@ \

350
src/libguac/argv.c Normal file
View File

@ -0,0 +1,350 @@
/*
* 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 "config.h"
#include "guacamole/argv.h"
#include "guacamole/client.h"
#include "guacamole/protocol.h"
#include "guacamole/socket.h"
#include "guacamole/stream.h"
#include "guacamole/string.h"
#include "guacamole/user.h"
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
/**
* The state of an argument that will be automatically processed. Note that
* this is distinct from the state of an argument value that is currently being
* processed. Argument value states are dynamically-allocated and scoped by the
* associated guac_stream.
*/
typedef struct guac_argv_state {
/**
* The name of the argument.
*/
char name[GUAC_ARGV_MAX_NAME_LENGTH];
/**
* Whether at least one value for this argument has been received since it
* was registered.
*/
int received;
/**
* Bitwise OR of all option flags that should affect processing of this
* argument.
*/
int options;
/**
* The callback that should be invoked when a new value for the associated
* argument has been received. If the GUAC_ARGV_OPTION_ONCE flag is set,
* the callback will be invoked at most once.
*/
guac_argv_callback* callback;
/**
* The arbitrary data that should be passed to the callback.
*/
void* data;
} guac_argv_state;
/**
* The current state of automatic processing of "argv" streams.
*/
typedef struct guac_argv_await_state {
/**
* Whether automatic argument processing has been stopped via a call to
* guac_argv_stop().
*/
int stopped;
/**
* The total number of arguments registered.
*/
unsigned int num_registered;
/**
* All registered arguments and their corresponding callbacks.
*/
guac_argv_state registered[GUAC_ARGV_MAX_REGISTERED];
/**
* Lock which protects multi-threaded access to this entire state
* structure, including the condition that signals specific modifications
* to the structure.
*/
pthread_mutex_t lock;
/**
* Condition which is signalled whenever the overall state of "argv"
* processing changes, either through the receipt of a new argument value
* or due to a call to guac_argv_stop().
*/
pthread_cond_t changed;
} guac_argv_await_state;
/**
* The value or current status of a connection parameter received over an
* "argv" stream.
*/
typedef struct guac_argv {
/**
* The state of the specific setting being updated.
*/
guac_argv_state* state;
/**
* The mimetype of the data being received.
*/
char mimetype[GUAC_ARGV_MAX_MIMETYPE_LENGTH];
/**
* Buffer space for containing the received argument value.
*/
char buffer[GUAC_ARGV_MAX_LENGTH];
/**
* The number of bytes received so far.
*/
int length;
} guac_argv;
/**
* Statically-allocated, shared state of the guac_argv_*() family of functions.
*/
static guac_argv_await_state await_state = {
.lock = PTHREAD_MUTEX_INITIALIZER,
.changed = PTHREAD_COND_INITIALIZER
};
/**
* Returns whether at least one value for each of the provided arguments has
* been received.
*
* @param args
* A NULL-terminated array of the names of all arguments to test.
*
* @return
* Non-zero if at least one value has been received for each of the
* provided arguments, zero otherwise.
*/
static int guac_argv_is_received(const char** args) {
for (int i = 0; i < await_state.num_registered; i++) {
/* Ignore all received arguments */
guac_argv_state* state = &await_state.registered[i];
if (state->received)
continue;
/* Fail immediately for any matching non-received arguments */
for (const char** arg = args; *arg != NULL; arg++) {
if (strcmp(state->name, *arg) == 0)
return 0;
}
}
/* All arguments were received */
return 1;
}
int guac_argv_register(const char* name, guac_argv_callback* callback, void* data, int options) {
pthread_mutex_lock(&await_state.lock);
if (await_state.num_registered == GUAC_ARGV_MAX_REGISTERED) {
pthread_mutex_unlock(&await_state.lock);
return 1;
}
guac_argv_state* state = &await_state.registered[await_state.num_registered++];
guac_strlcpy(state->name, name, sizeof(state->name));
state->options = options;
state->callback = callback;
state->data = data;
pthread_mutex_unlock(&await_state.lock);
return 0;
}
int guac_argv_await(const char** args) {
/* Wait for all requested arguments to be received (or for receipt to be
* stopped) */
pthread_mutex_lock(&await_state.lock);
while (!await_state.stopped && !guac_argv_is_received(args))
pthread_cond_wait(&await_state.changed, &await_state.lock);
/* Arguments were successfully received only if receipt was not stopped */
int retval = await_state.stopped;
pthread_mutex_unlock(&await_state.lock);
return retval;
}
/**
* Handler for "blob" instructions which appends the data from received blobs
* to the end of the in-progress argument value buffer.
*
* @see guac_user_blob_handler
*/
static int guac_argv_blob_handler(guac_user* user, guac_stream* stream,
void* data, int length) {
guac_argv* argv = (guac_argv*) stream->data;
/* Calculate buffer size remaining, including space for null terminator,
* adjusting received length accordingly */
int remaining = sizeof(argv->buffer) - argv->length - 1;
if (length > remaining)
length = remaining;
/* Append received data to end of buffer */
memcpy(argv->buffer + argv->length, data, length);
argv->length += length;
return 0;
}
/**
* Handler for "end" instructions which applies the changes specified by the
* argument value buffer associated with the stream.
*
* @see guac_user_end_handler
*/
static int guac_argv_end_handler(guac_user* user, guac_stream* stream) {
int result = 0;
/* Append null terminator to value */
guac_argv* argv = (guac_argv*) stream->data;
argv->buffer[argv->length] = '\0';
pthread_mutex_lock(&await_state.lock);
/* Invoke callback, limiting to a single invocation if
* GUAC_ARGV_OPTION_ONCE applies */
guac_argv_state* state = argv->state;
if (!(state->options & GUAC_ARGV_OPTION_ONCE) || !state->received) {
if (state->callback != NULL)
result = state->callback(user, argv->mimetype, state->name, argv->buffer, state->data);
}
/* Alert connected clients regarding newly-accepted values if echo is
* enabled */
if (!result && (state->options & GUAC_ARGV_OPTION_ECHO)) {
guac_client* client = user->client;
guac_client_stream_argv(client, client->socket, argv->mimetype, state->name, argv->buffer);
}
/* Notify that argument has been received */
state->received = 1;
pthread_cond_broadcast(&await_state.changed);
pthread_mutex_unlock(&await_state.lock);
free(argv);
return 0;
}
int guac_argv_received(guac_stream* stream, const char* mimetype, const char* name) {
pthread_mutex_lock(&await_state.lock);
for (int i = 0; i < await_state.num_registered; i++) {
/* Ignore any arguments that have already been received if they are
* declared as being acceptable only once */
guac_argv_state* state = &await_state.registered[i];
if ((state->options & GUAC_ARGV_OPTION_ONCE) && state->received)
continue;
/* Argument matched */
if (strcmp(state->name, name) == 0) {
guac_argv* argv = malloc(sizeof(guac_argv));
guac_strlcpy(argv->mimetype, mimetype, sizeof(argv->mimetype));
argv->state = state;
argv->length = 0;
stream->data = argv;
stream->blob_handler = guac_argv_blob_handler;
stream->end_handler = guac_argv_end_handler;
pthread_mutex_unlock(&await_state.lock);
return 0;
}
}
/* No such argument awaiting processing */
pthread_mutex_unlock(&await_state.lock);
return 1;
}
void guac_argv_stop() {
pthread_mutex_lock(&await_state.lock);
/* Signal any waiting threads that no further argument values will be
* received */
if (!await_state.stopped) {
await_state.stopped = 1;
pthread_cond_broadcast(&await_state.changed);
}
pthread_mutex_unlock(&await_state.lock);
}
int guac_argv_handler(guac_user* user, guac_stream* stream,
char* mimetype, char* name) {
/* Refuse stream if argument is not registered */
if (guac_argv_received(stream, mimetype, name)) {
guac_protocol_send_ack(user->socket, stream, "Not allowed.",
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
guac_socket_flush(user->socket);
return 0;
}
/* Signal stream is ready */
guac_protocol_send_ack(user->socket, stream, "Ready for updated "
"parameter.", GUAC_PROTOCOL_STATUS_SUCCESS);
guac_socket_flush(user->socket);
return 0;
}

View File

@ -307,6 +307,10 @@ int guac_client_add_user(guac_client* client, guac_user* user, int argc, char**
pthread_rwlock_unlock(&(client->__users_lock));
/* Notify owner of user joining connection. */
if (retval == 0 && !user->owner)
guac_client_owner_notify_join(client, user);
return retval;
}
@ -333,6 +337,10 @@ void guac_client_remove_user(guac_client* client, guac_user* user) {
pthread_rwlock_unlock(&(client->__users_lock));
/* Update owner of user having left the connection. */
if (!user->owner)
guac_client_owner_notify_leave(client, user);
/* Call handler, if defined */
if (user->leave_handler)
user->leave_handler(user);
@ -413,15 +421,19 @@ void* guac_client_for_user(guac_client* client, guac_user* user,
}
int guac_client_end_frame(guac_client* client) {
return guac_client_end_multiple_frames(client, 0);
}
int guac_client_end_multiple_frames(guac_client* client, int frames) {
/* Update and send timestamp */
client->last_sent_timestamp = guac_timestamp_current();
/* Log received timestamp and calculated lag (at TRACE level only) */
guac_client_log(client, GUAC_LOG_TRACE, "Server completed "
"frame %" PRIu64 "ms.", client->last_sent_timestamp);
"frame %" PRIu64 "ms (%i logical frames)", client->last_sent_timestamp, frames);
return guac_protocol_send_sync(client->socket, client->last_sent_timestamp);
return guac_protocol_send_sync(client->socket, client->last_sent_timestamp, frames);
}
@ -478,6 +490,44 @@ int guac_client_load_plugin(guac_client* client, const char* protocol) {
}
/**
* A callback function which is invoked by guac_client_owner_send_required() to
* send the required parameters to the specified user, who is the owner of the
* client session.
*
* @param user
* The guac_user that will receive the required parameters, who is the owner
* of the client.
*
* @param data
* A pointer to a NULL-terminated array of required parameters that will be
* passed on to the owner to continue the connection.
*
* @return
* Zero if the operation succeeds or non-zero on failure, cast as a void*.
*/
static void* guac_client_owner_send_required_callback(guac_user* user, void* data) {
const char** required = (const char **) data;
/* Send required parameters to owner. */
if (user != NULL)
return (void*) ((intptr_t) guac_protocol_send_required(user->socket, required));
return (void*) ((intptr_t) -1);
}
int guac_client_owner_send_required(guac_client* client, const char** required) {
/* Don't send required instruction if client does not support it. */
if (!guac_client_owner_supports_required(client))
return -1;
return (int) ((intptr_t) guac_client_for_owner(client, guac_client_owner_send_required_callback, required));
}
/**
* Updates the provided approximate processing lag, taking into account the
* processing lag of the given user.
@ -633,6 +683,184 @@ static void* __webp_support_callback(guac_user* user, void* data) {
}
#endif
/**
* A callback function which is invoked by guac_client_owner_supports_msg()
* to determine if the owner of a client supports the "msg" instruction,
* returning zero if the user does not support the instruction or non-zero if
* the user supports it.
*
* @param user
* The guac_user that will be checked for "msg" instruction support.
*
* @param data
* Data provided to the callback. This value is never used within this
* callback.
*
* @return
* A non-zero integer if the provided user who owns the connection supports
* the "msg" instruction, or zero if the user does not. The integer is cast
* as a void*.
*/
static void* guac_owner_supports_msg_callback(guac_user* user, void* data) {
return (void*) ((intptr_t) guac_user_supports_msg(user));
}
int guac_client_owner_supports_msg(guac_client* client) {
return (int) ((intptr_t) guac_client_for_owner(client, guac_owner_supports_msg_callback, NULL));
}
/**
* A callback function which is invoked by guac_client_owner_supports_required()
* to determine if the owner of a client supports the "required" instruction,
* returning zero if the user does not support the instruction or non-zero if
* the user supports it.
*
* @param user
* The guac_user that will be checked for "required" instruction support.
*
* @param data
* Data provided to the callback. This value is never used within this
* callback.
*
* @return
* A non-zero integer if the provided user who owns the connection supports
* the "required" instruction, or zero if the user does not. The integer
* is cast as a void*.
*/
static void* guac_owner_supports_required_callback(guac_user* user, void* data) {
return (void*) ((intptr_t) guac_user_supports_required(user));
}
int guac_client_owner_supports_required(guac_client* client) {
return (int) ((intptr_t) guac_client_for_owner(client, guac_owner_supports_required_callback, NULL));
}
/**
* A callback function that is invokved by guac_client_owner_notify_join() to
* notify the owner of a connection that another user has joined the
* connection, returning zero if the message is sent successfully, or non-zero
* if an error occurs.
*
* @param user
* The user to send the notification to, which will be the owner of the
* connection.
*
* @param data
* The data provided to the callback, which is the user that is joining the
* connection.
*
* @return
* Zero if the message is sent successfully to the owner, otherwise
* non-zero, cast as a void*.
*/
static void* guac_client_owner_notify_join_callback(guac_user* user, void* data) {
const guac_user* joiner = (const guac_user *) data;
if (user == NULL)
return (void*) ((intptr_t) -1);
char* log_owner = "owner";
if (user->info.name != NULL)
log_owner = (char *) user->info.name;
char* log_joiner = "anonymous";
char* send_joiner = "";
if (joiner->info.name != NULL) {
log_joiner = (char *) joiner->info.name;
send_joiner = (char *) joiner->info.name;
}
guac_user_log(user, GUAC_LOG_DEBUG, "Notifying owner \"%s\" of \"%s\" joining.",
log_owner, log_joiner);
/* Send user joined notification to owner. */
const char* args[] = { (const char*)joiner->user_id, (const char*)send_joiner, NULL };
return (void*) ((intptr_t) guac_protocol_send_msg(user->socket, GUAC_MESSAGE_USER_JOINED, args));
}
int guac_client_owner_notify_join(guac_client* client, guac_user* joiner) {
/* Don't send msg instruction if client does not support it. */
if (!guac_client_owner_supports_msg(client)) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Client does not support the \"msg\" instruction and "
"will not be notified of the user joining the connection.");
return -1;
}
return (int) ((intptr_t) guac_client_for_owner(client, guac_client_owner_notify_join_callback, joiner));
}
/**
* A callback function that is invokved by guac_client_owner_notify_leave() to
* notify the owner of a connection that another user has left the connection,
* returning zero if the message is sent successfully, or non-zero
* if an error occurs.
*
* @param user
* The user to send the notification to, which will be the owner of the
* connection.
*
* @param data
* The data provided to the callback, which is the user that is leaving the
* connection.
*
* @return
* Zero if the message is sent successfully to the owner, otherwise
* non-zero, cast as a void*.
*/
static void* guac_client_owner_notify_leave_callback(guac_user* user, void* data) {
const guac_user* quitter = (const guac_user *) data;
if (user == NULL)
return (void*) ((intptr_t) -1);
char* log_owner = "owner";
if (user->info.name != NULL)
log_owner = (char *) user->info.name;
char* log_quitter = "anonymous";
char* send_quitter = "";
if (quitter->info.name != NULL) {
log_quitter = (char *) quitter->info.name;
send_quitter = (char *) quitter->info.name;
}
guac_user_log(user, GUAC_LOG_DEBUG, "Notifying owner \"%s\" of \"%s\" leaving.",
log_owner, log_quitter);
/* Send user left notification to owner. */
const char* args[] = { (const char*)quitter->user_id, (const char*)send_quitter, NULL };
return (void*) ((intptr_t) guac_protocol_send_msg(user->socket, GUAC_MESSAGE_USER_LEFT, args));
}
int guac_client_owner_notify_leave(guac_client* client, guac_user* quitter) {
/* Don't send msg instruction if client does not support it. */
if (!guac_client_owner_supports_msg(client)) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Client does not support the \"msg\" instruction and "
"will not be notified of the user leaving the connection.");
return -1;
}
return (int) ((intptr_t) guac_client_for_owner(client, guac_client_owner_notify_leave_callback, quitter));
}
int guac_client_supports_webp(guac_client* client) {
#ifdef ENABLE_WEBP

51
src/libguac/fips.c Normal file
View File

@ -0,0 +1,51 @@
/*
* 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 "config.h"
#include "guacamole/fips.h"
/* If OpenSSL is available, include header for version numbers */
#ifdef ENABLE_SSL
#include <openssl/opensslv.h>
/* OpenSSL versions prior to 0.9.7e did not have FIPS support */
#if !defined(OPENSSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x00090705f)
#define GUAC_FIPS_ENABLED 0
/* OpenSSL 3+ uses EVP_default_properties_is_fips_enabled() */
#elif defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
#include <openssl/evp.h>
#define GUAC_FIPS_ENABLED EVP_default_properties_is_fips_enabled(NULL)
/* For OpenSSL versions between 0.9.7e and 3.0, use FIPS_mode() */
#else
#include <openssl/crypto.h>
#define GUAC_FIPS_ENABLED FIPS_mode()
#endif
/* FIPS support does not exist if OpenSSL is not available. */
#else
#define GUAC_FIPS_ENABLED 0
#endif
int guac_fips_enabled() {
return GUAC_FIPS_ENABLED;
}

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.
*/
#ifndef GUAC_ARGV_CONSTANTS_H
#define GUAC_ARGV_CONSTANTS_H
/**
* Constants related to automatic handling of received "argv" instructions.
*
* @file argv-constants.h
*/
/**
* Option flag which declares to guac_argv_register() that the associated
* argument should be processed exactly once. If multiple "argv" streams are
* received for the argument, only the first such stream is processed.
* Additional streams will be rejected.
*/
#define GUAC_ARGV_OPTION_ONCE 1
/**
* Option flag which declares to guac_argv_register() that the values received
* and accepted for the associated argument should be echoed to all connected
* users via outbound "argv" streams.
*/
#define GUAC_ARGV_OPTION_ECHO 2
/**
* The maximum number of bytes to allow for any argument value received via an
* argv stream and processed using guac_argv_received(), including null
* terminator.
*/
#define GUAC_ARGV_MAX_LENGTH 16384
/**
* The maximum number of bytes to allow within the name of any argument
* registered with guac_argv_register(), including null terminator.
*/
#define GUAC_ARGV_MAX_NAME_LENGTH 256
/**
* The maximum number of bytes to allow within the mimetype of any received
* argument value passed to a callback registered with guac_argv_register(),
* including null terminator.
*/
#define GUAC_ARGV_MAX_MIMETYPE_LENGTH 4096
/**
* The maximum number of arguments that may be registered via guac_argv_await()
* or guac_argv_await_async() before further argument registrations will fail.
*/
#define GUAC_ARGV_MAX_REGISTERED 128
#endif

View File

@ -0,0 +1,62 @@
/*
* 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.
*/
#ifndef GUAC_ARGV_FNTYPES_H
#define GUAC_ARGV_FNTYPES_H
/**
* Function type definitions related to automatic handling of received "argv"
* instructions.
*
* @file argv-fntypes.h
*/
#include "user-types.h"
/**
* Callback which is invoked by the automatic "argv" handling when the full
* value of a received argument has been received.
*
* @param user
* The user that opened the argument value stream.
*
* @param mimetype
* The mimetype of the data that will be sent along the stream.
*
* @param name
* The name of the connection parameter being updated. It is up to the
* implementation of this handler to decide whether and how to update a
* connection parameter.
*
* @param value
* The value of the received argument.
*
* @param data
* Any arbitrary data that was provided when the received argument was
* registered with guac_argv_register().
*
* @return
* Zero if the received argument value has been accepted and has either
* taken effect or is being intentionally ignored, non-zero otherwise.
*/
typedef int guac_argv_callback(guac_user* user, const char* mimetype,
const char* name, const char* value, void* data);
#endif

View File

@ -0,0 +1,128 @@
/*
* 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.
*/
#ifndef GUAC_ARGV_H
#define GUAC_ARGV_H
/**
* Convenience functions for processing parameter values that are submitted
* dynamically using "argv" instructions.
*
* @file argv.h
*/
#include "argv-constants.h"
#include "argv-fntypes.h"
#include "stream-types.h"
#include "user-fntypes.h"
/**
* Registers the given callback such that it is automatically invoked when an
* "argv" stream for an argument having the given name is processed using
* guac_argv_received(). The maximum number of arguments that may be registered
* in this way is limited by GUAC_ARGV_MAX_REGISTERED. The maximum length of
* any provided argument name is limited by GUAC_ARGV_MAX_NAME_LENGTH.
*
* @see GUAC_ARGV_MAX_NAME_LENGTH
* @see GUAC_ARGV_MAX_REGISTERED
*
* @see GUAC_ARGV_OPTION_ONCE
* @see GUAC_ARGV_OPTION_ECHO
*
* @param name
* The name of the argument that should be handled by the given callback.
*
* @param callback
* The callback to invoke when the value of an argument having the given
* name has finished being received.
*
* @param data
* Arbitrary data to be passed to the given callback when a value is
* received for the given argument.
*
* @param options
* Bitwise OR of all option flags that should affect processing of this
* argument.
*
* @return
* Zero if the callback was successfully registered, non-zero if the
* maximum number of registered callbacks has already been reached.
*/
int guac_argv_register(const char* name, guac_argv_callback* callback, void* data, int options);
/**
* Waits for receipt of each of the given arguments via guac_argv_received().
* This function will block until either ALL of the given arguments have been
* received via guac_argv_received() or until automatic processing of received
* arguments is stopped with guac_argv_stop().
*
* @param args
* A NULL-terminated array of the names of all arguments that this function
* should wait for.
*
* @return
* Zero if all of the specified arguments were received, non-zero if
* guac_argv_stop() was called before all arguments were received.
*/
int guac_argv_await(const char** args);
/**
* Hands off management of the given guac_stream, automatically processing data
* received over that stream as the value of the argument having the given
* name. The argument must have already been registered with
* guac_argv_register(). The blob_handler and end_handler of the given stream,
* if already set, will be overridden without regard to their current value.
*
* It is the responsibility of the caller to properly send any required "ack"
* instructions to accept or reject the received stream.
*
* @param stream
* The guac_stream that will receive the value of the argument having the
* given name.
*
* @param mimetype
* The mimetype of the data that will be received over the stream.
*
* @param name
* The name of the argument being received.
*
* @return
* Zero if handling of the guac_stream has been successfully handed off,
* non-zero if the provided argument has not yet been registered with
* guac_argv_register().
*/
int guac_argv_received(guac_stream* stream, const char* mimetype, const char* name);
/**
* Stops further automatic processing of received "argv" streams. Any call to
* guac_argv_await() that is currently blocking will return, and any future
* calls to guac_argv_await() will return immediately without blocking.
*/
void guac_argv_stop();
/**
* Convenience implementation of the "argv" instruction handler which
* automatically sends any required "ack" instructions and invokes
* guac_argv_received(). Only arguments that are registered with
* guac_argv_register() will be processed.
*/
guac_user_argv_handler guac_argv_handler;
#endif

View File

@ -509,18 +509,47 @@ void* guac_client_for_user(guac_client* client, guac_user* user,
guac_user_callback* callback, void* data);
/**
* Marks the end of the current frame by sending a "sync" instruction to
* all connected users. This instruction will contain the current timestamp.
* The last_sent_timestamp member of guac_client will be updated accordingly.
* Marks the end of the current frame by sending a "sync" instruction to all
* connected users, where the number of input frames that were considered in
* creating this frame is either unknown or inapplicable. This instruction will
* contain the current timestamp. The last_sent_timestamp member of guac_client
* will be updated accordingly.
*
* If an error occurs sending the instruction, a non-zero value is
* returned, and guac_error is set appropriately.
*
* @param client The guac_client which has finished a frame.
* @return Zero on success, non-zero on error.
* @param client
* The guac_client which has finished a frame.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_client_end_frame(guac_client* client);
/**
* Marks the end of the current frame by sending a "sync" instruction to all
* connected users, where that frame may combine or otherwise represent the
* changes of an arbitrary number of input frames. This instruction will
* contain the current timestamp, as well as the number of frames that were
* considered in creating that frame. The last_sent_timestamp member of
* guac_client will be updated accordingly.
*
* If an error occurs sending the instruction, a non-zero value is
* returned, and guac_error is set appropriately.
*
* @param client
* The guac_client which has finished a frame.
*
* @param frames
* The number of distinct frames that were considered or combined when
* generating the current frame, or zero if the boundaries of relevant
* frames are unknown.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_client_end_multiple_frames(guac_client* client, int frames);
/**
* Initializes the given guac_client using the initialization routine provided
* by the plugin corresponding to the named protocol. This will automatically
@ -549,6 +578,22 @@ int guac_client_load_plugin(guac_client* client, const char* protocol);
*/
int guac_client_get_processing_lag(guac_client* client);
/**
* Sends a request to the owner of the given guac_client for parameters required
* to continue the connection started by the client. The function returns zero
* on success or non-zero on failure.
*
* @param client
* The client where additional connection parameters are required.
*
* @param required
* The NULL-terminated array of required parameters.
*
* @return
* Zero on success, non-zero on failure.
*/
int guac_client_owner_send_required(guac_client* client, const char** required);
/**
* Streams the given connection parameter value over an argument value stream
* ("argv" instruction), exposing the current value of the named connection
@ -692,6 +737,72 @@ void guac_client_stream_webp(guac_client* client, guac_socket* socket,
guac_composite_mode mode, const guac_layer* layer, int x, int y,
cairo_surface_t* surface, int quality, int lossless);
/**
* Returns whether the owner of the given client supports the "msg"
* instruction, returning non-zero if the client owner does support the
* instruction, or zero if the owner does not.
*
* @param client
* The Guacamole client whose owner should be checked for supporting
* the "msg" instruction.
*
* @return
* Non-zero if the owner of the given client supports the "msg"
* instruction, zero otherwise.
*/
int guac_client_owner_supports_msg(guac_client* client);
/**
* Returns whether the owner of the given client supports the "required"
* instruction, returning non-zero if the client owner does support the
* instruction, or zero if the owner does not.
*
* @param client
* The Guacamole client whose owner should be checked for supporting
* the "required" instruction.
*
* @return
* Non-zero if the owner of the given client supports the "required"
* instruction, zero otherwise.
*/
int guac_client_owner_supports_required(guac_client* client);
/**
* Notifies the owner of the given client that a user has joined the connection,
* and returns zero if the message was sent successfully, or non-zero if the
* notification failed.
*
* @param client
* The Guacamole Client whose owner should be notified of a user joining
* the connection.
*
* @param joiner
* The Guacamole User who joined the connection.
*
* @return
* Zero if the notification to the owner was sent successfully, or non-zero
* if an error occurred.
*/
int guac_client_owner_notify_join(guac_client* client, guac_user* joiner);
/**
* Notifies the owner of the given client that a user has left the connection,
* and returns zero if the message was sent successfully, or non-zero if the
* notification failed.
*
* @param client
* The Guacamole Client whose owner should be notified of a user leaving
* the connection.
*
* @param quitter
* The Guacamole User who left the connection.
*
* @return
* Zero if the notification to the owner was sent successfully, or non-zero
* if an error occurred.
*/
int guac_client_owner_notify_leave(guac_client* client, guac_user* quitter);
/**
* Returns whether all users of the given client support WebP. If any user does
* not support WebP, or the server cannot encode WebP images, zero is returned.

View File

@ -17,24 +17,17 @@
* under the License.
*/
#ifndef GUAC_COMMON_SSH_RSA_COMPAT_H
#define GUAC_COMMON_SSH_RSA_COMPAT_H
#ifndef GUAC_FIPS_H
#define GUAC_FIPS_H
#include "config.h"
#include <openssl/bn.h>
#include <openssl/rsa.h>
#ifndef HAVE_RSA_GET0_KEY
/**
* RSA_get0_key() implementation for versions of OpenSSL which lack this
* function (pre 1.1).
* Returns a non-zero value if FIPS mode is enabled, or zero if FIPS mode
* is not enabled.
*
* See: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html
* @return
* A non-zero value if FIPS mode is enabled, or zero if FIPS mode is
* not enabled.
*/
void RSA_get0_key(const RSA* rsa_key, const BIGNUM** n,
const BIGNUM** e, const BIGNUM**d);
#endif
#endif
int guac_fips_enabled();
#endif

View File

@ -38,7 +38,7 @@
* This version is passed by the __guac_protocol_send_args() function from the
* server to the client during the client/server handshake.
*/
#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_1_0"
#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_5_0"
/**
* The maximum number of bytes that should be sent in any one blob instruction
@ -49,5 +49,20 @@
*/
#define GUAC_PROTOCOL_BLOB_MAX_LENGTH 6048
/**
* The name of the layer parameter defining the number of simultaneous points
* of contact supported by a layer. This parameter should be set to a non-zero
* value if the associated layer should receive touch events ("touch"
* instructions).
*
* This value specified for this parameter is advisory, and the client is not
* required to honor the declared level of touch support. Implementations are
* expected to safely handle or ignore any received touch events, regardless of
* the level of touch support declared.
*
* @see guac_protocol_send_set_int()
*/
#define GUAC_PROTOCOL_LAYER_PARAMETER_MULTI_TOUCH "multi-touch"
#endif

View File

@ -276,5 +276,70 @@ typedef enum guac_line_join_style {
GUAC_LINE_JOIN_ROUND = 0x2
} guac_line_join_style;
/**
* The set of protocol versions known to guacd to handle negotiation or feature
* support between differing versions of Guacamole clients and guacd.
*/
typedef enum guac_protocol_version {
/**
* An unknown version of the Guacamole protocol.
*/
GUAC_PROTOCOL_VERSION_UNKNOWN = 0x000000,
/**
* Original protocol version 1.0.0, which lacks support for negotiating
* parameters and protocol version, and requires that parameters in the
* client/server handshake be delivered in order.
*/
GUAC_PROTOCOL_VERSION_1_0_0 = 0x010000,
/**
* Protocol version 1.1.0, which includes support for parameter and version
* negotiation and for sending timezone information from the client
* to the server.
*/
GUAC_PROTOCOL_VERSION_1_1_0 = 0x010100,
/**
* Protocol version 1.3.0, which supports the "required" instruction,
* allowing connections in guacd to request information from the client and
* await a response.
*/
GUAC_PROTOCOL_VERSION_1_3_0 = 0x010300,
/**
* Protocol version 1.5.0, which supports the "msg" instruction, allowing
* messages to be sent to the client, and adds support for the "name"
* handshake instruction.
*/
GUAC_PROTOCOL_VERSION_1_5_0 = 0x010500
} guac_protocol_version;
/**
* A type that represents codes for human-readable messages sent by the "msg"
* instruction to the Client, that will be displayed in the client's browser.
* The codes will be interpreted by the client into translatable messages, and
* make take arguments, as noted below.
*/
typedef enum guac_message_type {
/**
* A message that notifies the owner of a connection that another user has
* joined their connection. There should be a single argument provided, the
* name of the user who has joined.
*/
GUAC_MESSAGE_USER_JOINED = 0x0001,
/**
* A message that notifies the owner of a connection that another user has
* left their connection. There should be a single argument provided, the
* name of the user who has left.
*/
GUAC_MESSAGE_USER_LEFT = 0x0002
} guac_message_type;
#endif

View File

@ -171,6 +171,27 @@ int guac_protocol_send_log(guac_socket* socket, const char* format, ...);
int vguac_protocol_send_log(guac_socket* socket, const char* format,
va_list args);
/**
* Sends the given string over the socket to be displayed on the client. Returns
* zero if the message was sent successfully or non-zero if an error occurs.
*
* @param socket
* The guac_socket connection to send the message to.
*
* @param msg
* The message code to send to the client.
*
* @param args
* A null-terminated array of strings that will be provided to the client
* as part of the message, that the client may then place in the message,
* or null if the message requires no arguments.
*
* @return
* Zero if the message is sent successfully; otherwise non-zero.
*/
int guac_protocol_send_msg(guac_socket* socket, guac_message_type msg,
const char** args);
/**
* Sends a mouse instruction over the given guac_socket connection.
*
@ -209,6 +230,53 @@ int vguac_protocol_send_log(guac_socket* socket, const char* format,
int guac_protocol_send_mouse(guac_socket* socket, int x, int y,
int button_mask, guac_timestamp timestamp);
/**
* Sends a touch instruction over the given guac_socket connection.
*
* If an error occurs sending the instruction, a non-zero value is
* returned, and guac_error is set appropriately.
*
* @param socket
* The guac_socket connection to use.
*
* @param id
* An arbitrary integer ID which uniquely identifies this contact relative
* to other active contacts.
*
* @param x
* The X coordinate of the center of the touch contact.
*
* @param y
* The Y coordinate of the center of the touch contact.
*
* @param x_radius
* The X radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param y_radius
* The Y radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param angle
* The rough angle of clockwise rotation of the general area of the touch
* contact, in degrees.
*
* @param force
* The relative force exerted by the touch contact, where 0 is no force
* (the touch has been lifted) and 1 is maximum force (the maximum amount
* of force representable by the device).
*
* @param timestamp
* The server timestamp (in milliseconds) at the point in time this touch
* event was acknowledged.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_touch(guac_socket* socket, int id, int x, int y,
int x_radius, int y_radius, double angle, double force,
guac_timestamp timestamp);
/**
* Sends a nest instruction over the given guac_socket connection.
*
@ -271,6 +339,32 @@ int guac_protocol_send_ready(guac_socket* socket, const char* id);
int guac_protocol_send_set(guac_socket* socket, const guac_layer* layer,
const char* name, const char* value);
/**
* Sends a set instruction over the given guac_socket connection. This function
* behavies identically to guac_protocol_send_set() except that the provided
* parameter value is an integer, rather than a string.
*
* If an error occurs sending the instruction, a non-zero value is
* returned, and guac_error is set appropriately.
*
* @param socket
* The guac_socket connection to use.
*
* @param layer
* The layer to set the parameter of.
*
* @param name
* The name of the parameter to set.
*
* @param value
* The value to set the parameter to.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_set_int(guac_socket* socket, const guac_layer* layer,
const char* name, int value);
/**
* Sends a select instruction over the given guac_socket connection.
*
@ -290,11 +384,22 @@ int guac_protocol_send_select(guac_socket* socket, const char* protocol);
* If an error occurs sending the instruction, a non-zero value is
* returned, and guac_error is set appropriately.
*
* @param socket The guac_socket connection to use.
* @param timestamp The current timestamp (in milliseconds).
* @return Zero on success, non-zero on error.
* @param socket
* The guac_socket connection to use.
*
* @param timestamp
* The current timestamp (in milliseconds).
*
* @param frames
* The number of distinct frames that were considered or combined when
* generating the frame terminated by this instruction, or zero if the
* boundaries of relevant frames are unknown.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp);
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp,
int frames);
/* OBJECT INSTRUCTIONS */
@ -794,6 +899,22 @@ int guac_protocol_send_push(guac_socket* socket, const guac_layer* layer);
int guac_protocol_send_rect(guac_socket* socket, const guac_layer* layer,
int x, int y, int width, int height);
/**
* Sends a "required" instruction over the given guac_socket connection. This
* instruction indicates to the client that one or more additional parameters
* are needed to continue the connection.
*
* @param socket
* The guac_socket connection to which to send the instruction.
*
* @param required
* A NULL-terminated array of required parameters.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_required(guac_socket* socket, const char** required);
/**
* Sends a reset instruction over the given guac_socket connection.
*
@ -1007,5 +1128,32 @@ int guac_protocol_send_name(guac_socket* socket, const char* name);
*/
int guac_protocol_decode_base64(char* base64);
/**
* Given a string representation of a protocol version, return the enum value of
* that protocol version, or GUAC_PROTOCOL_VERSION_UNKNOWN if the value is not a
* known version.
*
* @param version_string
* The string representation of the protocol version.
*
* @return
* The enum value of the protocol version, or GUAC_PROTOCOL_VERSION_UNKNOWN
* if the provided version is not known.
*/
guac_protocol_version guac_protocol_string_to_version(const char* version_string);
/**
* Given the enum value of the protocol version, return a pointer to the string
* representation of the version, or NULL if the version is unknown.
*
* @param version
* The enum value of the protocol version.
*
* @return
* A pointer to the string representation of the protocol version, or NULL
* if the version is unknown.
*/
const char* guac_protocol_version_to_string(guac_protocol_version version);
#endif

View File

@ -17,11 +17,17 @@
* under the License.
*/
#ifndef GUAC_COMMON_RECORDING_H
#define GUAC_COMMON_RECORDING_H
#ifndef GUAC_RECORDING_H
#define GUAC_RECORDING_H
#include <guacamole/client.h>
/**
* Provides functions and structures to be use for session recording.
*
* @file recording.h
*/
/**
* The maximum numeric value allowed for the .1, .2, .3, etc. suffix appended
* to the end of the session recording filename if a recording having the
@ -47,7 +53,7 @@
* that output Guacamole instructions may be dynamically intercepted and
* written to a file.
*/
typedef struct guac_common_recording {
typedef struct guac_recording {
/**
* The guac_socket which writes directly to the recording file, rather than
@ -71,6 +77,15 @@ typedef struct guac_common_recording {
*/
int include_mouse;
/**
* Non-zero if multi-touch events should be included in the session
* recording, zero otherwise. Depending on whether the remote desktop will
* automatically provide graphical feedback for touches, including touch
* events may be necessary for multi-touch interactions to be rendered in
* any resulting video.
*/
int include_touch;
/**
* Non-zero if keys pressed and released should be included in the session
* recording, zero otherwise. Including key events within the recording may
@ -80,7 +95,7 @@ typedef struct guac_common_recording {
*/
int include_keys;
} guac_common_recording;
} guac_recording;
/**
* Replaces the socket of the given client such that all further Guacamole
@ -119,6 +134,13 @@ typedef struct guac_common_recording {
* otherwise. Including mouse state is necessary for the mouse cursor to be
* rendered in any resulting video.
*
* @param include_touch
* Non-zero if touch events should be included in the session recording,
* zero otherwise. Depending on whether the remote desktop will
* automatically provide graphical feedback for touches, including touch
* events may be necessary for multi-touch interactions to be rendered in
* any resulting video.
*
* @param include_keys
* Non-zero if keys pressed and released should be included in the session
* recording, zero otherwise. Including key events within the recording may
@ -127,13 +149,14 @@ typedef struct guac_common_recording {
* passwords, credit card numbers, etc.
*
* @return
* A new guac_common_recording structure representing the in-progress
* A new guac_recording structure representing the in-progress
* recording if the recording file has been successfully created and a
* recording will be written, NULL otherwise.
*/
guac_common_recording* guac_common_recording_create(guac_client* client,
guac_recording* guac_recording_create(guac_client* client,
const char* path, const char* name, int create_path,
int include_output, int include_mouse, int include_keys);
int include_output, int include_mouse, int include_touch,
int include_keys);
/**
* Frees the resources associated with the given in-progress recording. Note
@ -142,15 +165,15 @@ guac_common_recording* guac_common_recording_create(guac_client* client,
* freed when the guac_client is freed.
*
* @param recording
* The guac_common_recording to free.
* The guac_recording to free.
*/
void guac_common_recording_free(guac_common_recording* recording);
void guac_recording_free(guac_recording* recording);
/**
* Reports the current mouse position and button state within the recording.
*
* @param recording
* The guac_common_recording associated with the mouse that has moved.
* The guac_recording associated with the mouse that has moved.
*
* @param x
* The new X coordinate of the mouse cursor, in pixels.
@ -171,14 +194,52 @@ void guac_common_recording_free(guac_common_recording* recording);
* @see GUAC_CLIENT_MOUSE_SCROLL_UP
* @see GUAC_CLIENT_MOUSE_SCROLL_DOWN
*/
void guac_common_recording_report_mouse(guac_common_recording* recording,
void guac_recording_report_mouse(guac_recording* recording,
int x, int y, int button_mask);
/**
* Reports the current state of a touch contact within the recording.
*
* @param recording
* The guac_recording associated with the touch contact that
* has changed state.
*
* @param id
* An arbitrary integer ID which uniquely identifies this contact relative
* to other active contacts.
*
* @param x
* The X coordinate of the center of the touch contact.
*
* @param y
* The Y coordinate of the center of the touch contact.
*
* @param x_radius
* The X radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param y_radius
* The Y radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param angle
* The rough angle of clockwise rotation of the general area of the touch
* contact, in degrees.
*
* @param force
* The relative force exerted by the touch contact, where 0 is no force
* (the touch has been lifted) and 1 is maximum force (the maximum amount
* of force representable by the device).
*/
void guac_recording_report_touch(guac_recording* recording,
int id, int x, int y, int x_radius, int y_radius,
double angle, double force);
/**
* Reports a change in the state of an individual key within the recording.
*
* @param recording
* The guac_common_recording associated with the key that was pressed or
* The guac_recording associated with the key that was pressed or
* released.
*
* @param keysym
@ -188,7 +249,7 @@ void guac_common_recording_report_mouse(guac_common_recording* recording,
* Non-zero if the key represented by the given keysym is currently
* pressed, zero if it is released.
*/
void guac_common_recording_report_key(guac_common_recording* recording,
void guac_recording_report_key(guac_recording* recording,
int keysym, int pressed);
#endif

View File

@ -109,6 +109,41 @@ size_t guac_strlcpy(char* restrict dest, const char* restrict src, size_t n);
*/
size_t guac_strlcat(char* restrict dest, const char* restrict src, size_t n);
/**
* Search for the null-terminated string needle in the possibly null-
* terminated haystack, looking at no more than len bytes.
*
* @param haystack
* The string to search. It may or may not be null-terminated. Only the
* first len bytes are searched.
*
* @param needle
* The string to look for. It must be null-terminated.
*
* @param len
* The maximum number of bytes to examine in haystack.
*
* @return
* A pointer to the first instance of needle within haystack, or NULL if
* needle does not exist in haystack. If needle is the empty string,
* haystack is returned.
*
*/
char* guac_strnstr(const char *haystack, const char *needle, size_t len);
/**
* Simple wrapper for strdup() which behaves identically to standard strdup(),
* except that NULL will be returned if the provided string is NULL.
*
* @param str
* The string to duplicate as a newly-allocated string.
*
* @return
* A newly-allocated string containing identically the same content as the
* given string, or NULL if the given string was NULL.
*/
char* guac_strdup(const char* str);
/**
* Concatenates each of the given strings, separated by the given delimiter,
* storing the result within a destination buffer. The number of bytes written

View File

@ -95,6 +95,51 @@ typedef void* guac_user_callback(guac_user* user, void* data);
typedef int guac_user_mouse_handler(guac_user* user, int x, int y,
int button_mask);
/**
* Handler for Guacamole touch events, invoked when a "touch" instruction has
* been received from a user.
*
* @param user
* The user that sent the touch event.
*
* @param id
* An arbitrary integer ID which uniquely identifies this contact relative
* to other active contacts.
*
* @param x
* The X coordinate of the center of the touch contact within the display
* when the event occurred, in pixels. This value is not guaranteed to be
* within the bounds of the display area.
*
* @param y
* The Y coordinate of the center of the touch contact within the display
* when the event occurred, in pixels. This value is not guaranteed to be
* within the bounds of the display area.
*
* @param x_radius
* The X radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param y_radius
* The Y radius of the ellipse covering the general area of the touch
* contact, in pixels.
*
* @param angle
* The rough angle of clockwise rotation of the general area of the touch
* contact, in degrees.
*
* @param force
* The relative force exerted by the touch contact, where 0 is no force
* (the touch has been lifted) and 1 is maximum force (the maximum amount
* of force representable by the device).
*
* @return
* Zero if the touch event was handled successfully, or non-zero if an
* error occurred.
*/
typedef int guac_user_touch_handler(guac_user* user, int id, int x, int y,
int x_radius, int y_radius, double angle, double force);
/**
* Handler for Guacamole key events, invoked when a "key" event has been
* received from a user.

View File

@ -88,13 +88,27 @@ struct guac_user_info {
* stated resolution of the display size request is recommended.
*/
int optimal_resolution;
/**
* The timezone of the remote system. If the client does not provide
* a specific timezone then this will be NULL. The format of the timezone
* is the standard tzdata naming convention.
*/
const char* timezone;
/**
* The Guacamole protocol version that the remote system supports, allowing
* for feature support to be negotiated between client and server.
*/
guac_protocol_version protocol_version;
/**
* The human-readable name of the Guacamole user, supplied by the client
* during the handshake. This is an arbitrary value, with no requirements or
* constraints, including that it need not uniquely identify the user.
* If the client does not provide a name then this will be NULL.
*/
const char* name;
};
@ -503,6 +517,27 @@ struct guac_user {
*/
guac_user_argv_handler* argv_handler;
/**
* Handler for touch events sent by the Guacamole web-client.
*
* The handler takes the integer X and Y coordinates representing the
* center of the touch contact, as well as several parameters describing
* the general shape of the contact area. The force parameter indicates the
* amount of force exerted by the contact, including whether the contact
* has been lifted.
*
* Example:
* @code
* int touch_handler(guac_user* user, int id, int x, int y,
* int x_radius, int y_radius, double angle, double force);
*
* int guac_user_init(guac_user* user, int argc, char** argv) {
* user->touch_handler = touch_handler;
* }
* @endcode
*/
guac_user_touch_handler* touch_handler;
};
/**
@ -823,6 +858,28 @@ void guac_user_stream_webp(guac_user* user, guac_socket* socket,
guac_composite_mode mode, const guac_layer* layer, int x, int y,
cairo_surface_t* surface, int quality, int lossless);
/**
* Returns whether the given user supports the "msg" instruction.
*
* @param user
* The Guacamole user to check for support of the "msg" instruction.
*
* @return
* Non-zero if the user supports the "msg" instruction, otherwise zero.
*/
int guac_user_supports_msg(guac_user* user);
/**
* Returns whether the given user supports the "required" instruction.
*
* @param user
* The Guacamole user to check for support of the "required" instruction.
*
* @return
* Non-zero if the user supports the "required" instruction, otherwise zero.
*/
int guac_user_supports_required(guac_user* user);
/**
* Returns whether the given user supports WebP. If the user does not
* support WebP, or the server cannot encode WebP images, zero is returned.

View File

@ -0,0 +1,51 @@
/*
* 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.
*/
#ifndef GUAC_WOL_CONSTANTS_H
#define GUAC_WOL_CONSTANTS_H
/**
* Header file that provides constants and defaults related to libguac
* Wake-on-LAN support.
*
* @file wol-constants.h
*/
/**
* The value for the local IPv4 broadcast address.
*/
#define GUAC_WOL_LOCAL_IPV4_BROADCAST "255.255.255.255"
/**
* The size of the magic Wake-on-LAN packet to send to wake a remote host. This
* consists of 6 bytes of 0xFF, and then the MAC address repeated 16 times.
* https://en.wikipedia.org/wiki/Wake-on-LAN#Magic_packet
*/
#define GUAC_WOL_PACKET_SIZE 102
/**
* The port number that the magic packet should contain as the destination. In
* reality this doesn't matter all that much, since the packet is not usually
* processed by a full IP stack, but defining one is considered a standard
* practice.
*/
#define GUAC_WOL_PORT 9
#endif /* GUAC_WOL_CONSTANTS_H */

View File

@ -0,0 +1,56 @@
/*
* 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.
*/
#ifndef GUAC_WOL_H
#define GUAC_WOL_H
/**
* Header that provides functions and structures related to Wake-on-LAN
* support in libguac.
*
* @file wol.h
*/
#include "wol-constants.h"
/**
* Send the wake-up packet to the specified destination, returning zero if the
* wake was sent successfully, or non-zero if an error occurs sending the
* wake packet. Note that the return value does not specify whether the
* system actually wakes up successfully, only whether or not the packet
* is transmitted.
*
* @param mac_addr
* The MAC address to place in the magic Wake-on-LAN packet.
*
* @param broadcast_addr
* The broadcast address to which to send the magic Wake-on-LAN packet.
*
* @param udp_port
* The UDP port to use when sending the WoL packet.
*
* @return
* Zero if the packet is successfully sent to the destination; non-zero
* if the packet cannot be sent.
*/
int guac_wol_wake(const char* mac_addr, const char* broadcast_addr,
const unsigned short udp_port);
#endif /* GUAC_WOL_H */

View File

@ -22,7 +22,9 @@
#include "guacamole/error.h"
#include "id.h"
#ifdef HAVE_OSSP_UUID_H
#if defined(HAVE_LIBUUID)
#include <uuid/uuid.h>
#elif defined(HAVE_OSSP_UUID_H)
#include <ossp/uuid.h>
#else
#include <uuid.h>
@ -30,54 +32,73 @@
#include <stdlib.h>
/**
* The length of a UUID in bytes. All UUIDs are guaranteed to be 36 1-byte
* characters long.
*/
#define GUAC_UUID_LEN 36
char* guac_generate_id(char prefix) {
char* buffer;
char* identifier;
size_t identifier_length;
/* Prepare object to receive generated UUID */
#ifdef HAVE_LIBUUID
uuid_t uuid;
#else
uuid_t* uuid;
/* Attempt to create UUID object */
if (uuid_create(&uuid) != UUID_RC_OK) {
guac_error = GUAC_STATUS_NO_MEMORY;
guac_error_message = "Could not allocate memory for UUID";
return NULL;
}
#endif
/* Generate random UUID */
/* Generate unique identifier */
#ifdef HAVE_LIBUUID
uuid_generate(uuid);
#else
if (uuid_make(uuid, UUID_MAKE_V4) != UUID_RC_OK) {
uuid_destroy(uuid);
guac_error = GUAC_STATUS_NO_MEMORY;
guac_error_message = "UUID generation failed";
return NULL;
}
#endif
/* Allocate buffer for future formatted ID */
buffer = malloc(UUID_LEN_STR + 2);
buffer = malloc(GUAC_UUID_LEN + 2);
if (buffer == NULL) {
#ifndef HAVE_LIBUUID
uuid_destroy(uuid);
#endif
guac_error = GUAC_STATUS_NO_MEMORY;
guac_error_message = "Could not allocate memory for connection ID";
guac_error_message = "Could not allocate memory for unique ID";
return NULL;
}
identifier = &(buffer[1]);
identifier_length = UUID_LEN_STR + 1;
/* Build connection ID from UUID */
/* Convert UUID to string to produce unique identifier */
#ifdef HAVE_LIBUUID
uuid_unparse_lower(uuid, identifier);
#else
size_t identifier_length = GUAC_UUID_LEN + 1;
if (uuid_export(uuid, UUID_FMT_STR, &identifier, &identifier_length) != UUID_RC_OK) {
free(buffer);
uuid_destroy(uuid);
guac_error = GUAC_STATUS_INTERNAL_ERROR;
guac_error_message = "Conversion of UUID to connection ID failed";
guac_error_message = "Conversion of UUID to unique ID failed";
return NULL;
}
/* Clean up generated UUID */
uuid_destroy(uuid);
#endif
buffer[0] = prefix;
buffer[UUID_LEN_STR + 1] = '\0';
buffer[GUAC_UUID_LEN + 1] = '\0';
return buffer;
}

View File

@ -23,6 +23,7 @@
#include "guacamole/layer.h"
#include "guacamole/object.h"
#include "guacamole/protocol.h"
#include "guacamole/protocol-types.h"
#include "guacamole/socket.h"
#include "guacamole/stream.h"
#include "guacamole/unicode.h"
@ -39,6 +40,35 @@
#include <string.h>
#include <sys/types.h>
/**
* A structure mapping the enum value of a Guacamole protocol version to the
* string representation of the version.
*/
typedef struct guac_protocol_version_mapping {
/**
* The enum value of the protocol version.
*/
guac_protocol_version version;
/**
* The string value representing the protocol version.
*/
char* version_string;
} guac_protocol_version_mapping;
/**
* The map of known protocol versions to the corresponding string value.
*/
guac_protocol_version_mapping guac_protocol_version_table[] = {
{ GUAC_PROTOCOL_VERSION_1_0_0, "VERSION_1_0_0" },
{ GUAC_PROTOCOL_VERSION_1_1_0, "VERSION_1_1_0" },
{ GUAC_PROTOCOL_VERSION_1_3_0, "VERSION_1_3_0" },
{ GUAC_PROTOCOL_VERSION_1_5_0, "VERSION_1_5_0" },
{ GUAC_PROTOCOL_VERSION_UNKNOWN, NULL }
};
/* Output formatting functions */
ssize_t __guac_socket_write_length_string(guac_socket* socket, const char* str) {
@ -66,6 +96,38 @@ ssize_t __guac_socket_write_length_double(guac_socket* socket, double d) {
}
/**
* Loop through the provided NULL-terminated array, writing the values in the
* array to the given socket. Values are written as a series of Guacamole
* protocol elements, including the leading comma and the value length in
* addition to the value itself. Returns zero on success, non-zero on error.
*
* @param socket
* The socket to which the data should be written.
*
* @param array
* The NULL-terminated array of values to write.
*
* @return
* Zero on success, non-zero on error.
*/
static int guac_socket_write_array(guac_socket* socket, const char** array) {
/* Loop through array, writing provided values to the socket. */
for (int i=0; array[i] != NULL; i++) {
if (guac_socket_write_string(socket, ","))
return -1;
if (__guac_socket_write_length_string(socket, array[i]))
return -1;
}
return 0;
}
/* Protocol functions */
int guac_protocol_send_ack(guac_socket* socket, guac_stream* stream,
@ -90,8 +152,6 @@ int guac_protocol_send_ack(guac_socket* socket, guac_stream* stream,
static int __guac_protocol_send_args(guac_socket* socket, const char** args) {
int i;
if (guac_socket_write_string(socket, "4.args")) return -1;
/* Send protocol version ahead of other args. */
@ -99,15 +159,8 @@ static int __guac_protocol_send_args(guac_socket* socket, const char** args) {
|| __guac_socket_write_length_string(socket, GUACAMOLE_PROTOCOL_VERSION))
return -1;
for (i=0; args[i] != NULL; i++) {
if (guac_socket_write_string(socket, ","))
return -1;
if (__guac_socket_write_length_string(socket, args[i]))
return -1;
}
if (guac_socket_write_array(socket, args))
return -1;
return guac_socket_write_string(socket, ";");
@ -308,19 +361,11 @@ int guac_protocol_send_close(guac_socket* socket, const guac_layer* layer) {
static int __guac_protocol_send_connect(guac_socket* socket, const char** args) {
int i;
if (guac_socket_write_string(socket, "7.connect"))
return -1;
if (guac_socket_write_string(socket, "7.connect")) return -1;
for (i=0; args[i] != NULL; i++) {
if (guac_socket_write_string(socket, ","))
return -1;
if (__guac_socket_write_length_string(socket, args[i]))
return -1;
}
if (guac_socket_write_array(socket, args))
return -1;
return guac_socket_write_string(socket, ";");
@ -614,6 +659,23 @@ int guac_protocol_send_log(guac_socket* socket, const char* format, ...) {
}
int guac_protocol_send_msg(guac_socket* socket, guac_message_type msg,
const char** args) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val =
guac_socket_write_string(socket, "3.msg,")
|| __guac_socket_write_length_int(socket, msg)
|| guac_socket_write_array(socket, args)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket);
return ret_val;
}
int guac_protocol_send_file(guac_socket* socket, const guac_stream* stream,
const char* mimetype, const char* name) {
@ -776,6 +838,37 @@ int guac_protocol_send_mouse(guac_socket* socket, int x, int y,
}
int guac_protocol_send_touch(guac_socket* socket, int id, int x, int y,
int x_radius, int y_radius, double angle, double force,
guac_timestamp timestamp) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val =
guac_socket_write_string(socket, "5.touch,")
|| __guac_socket_write_length_int(socket, id)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, x)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, y)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, x_radius)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, y_radius)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_double(socket, angle)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_double(socket, force)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, timestamp)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket);
return ret_val;
}
int guac_protocol_send_move(guac_socket* socket, const guac_layer* layer,
const guac_layer* parent, int x, int y, int z) {
@ -961,6 +1054,23 @@ int guac_protocol_send_rect(guac_socket* socket,
}
int guac_protocol_send_required(guac_socket* socket, const char** required) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val = guac_socket_write_string(socket, "8.required")
|| guac_socket_write_array(socket, required)
|| guac_socket_write_string(socket, ";")
|| guac_socket_flush(socket);
guac_socket_instruction_end(socket);
return ret_val;
}
int guac_protocol_send_reset(guac_socket* socket, const guac_layer* layer) {
int ret_val;
@ -996,6 +1106,26 @@ int guac_protocol_send_set(guac_socket* socket, const guac_layer* layer,
}
int guac_protocol_send_set_int(guac_socket* socket, const guac_layer* layer,
const char* name, int value) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val =
guac_socket_write_string(socket, "3.set,")
|| __guac_socket_write_length_int(socket, layer->index)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_string(socket, name)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, value)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket);
return ret_val;
}
int guac_protocol_send_select(guac_socket* socket, const char* protocol) {
int ret_val;
@ -1069,7 +1199,8 @@ int guac_protocol_send_start(guac_socket* socket, const guac_layer* layer,
}
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp) {
int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp,
int frames) {
int ret_val;
@ -1077,6 +1208,8 @@ int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp) {
ret_val =
guac_socket_write_string(socket, "4.sync,")
|| __guac_socket_write_length_int(socket, timestamp)
|| guac_socket_write_string(socket, ",")
|| __guac_socket_write_length_int(socket, frames)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket);
@ -1241,3 +1374,35 @@ int guac_protocol_decode_base64(char* base64) {
}
guac_protocol_version guac_protocol_string_to_version(const char* version_string) {
guac_protocol_version_mapping* current = guac_protocol_version_table;
while (current->version != GUAC_PROTOCOL_VERSION_UNKNOWN) {
if (strcmp(current->version_string, version_string) == 0)
return current->version;
current++;
}
return GUAC_PROTOCOL_VERSION_UNKNOWN;
}
const char* guac_protocol_version_to_string(guac_protocol_version version) {
guac_protocol_version_mapping* current = guac_protocol_version_table;
while (current->version != GUAC_PROTOCOL_VERSION_UNKNOWN) {
if (current->version == version)
return (const char*) current->version_string;
current++;
}
return NULL;
}

View File

@ -17,12 +17,11 @@
* under the License.
*/
#include "common/recording.h"
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/timestamp.h>
#include "guacamole/client.h"
#include "guacamole/protocol.h"
#include "guacamole/recording.h"
#include "guacamole/socket.h"
#include "guacamole/timestamp.h"
#ifdef __MINGW32__
#include <direct.h>
@ -64,7 +63,7 @@
* The file descriptor of the open data file if open succeeded, or -1 on
* failure.
*/
static int guac_common_recording_open(const char* path,
static int guac_recording_open(const char* path,
const char* name, char* basename, int basename_size) {
int i;
@ -84,7 +83,7 @@ static int guac_common_recording_open(const char* path,
/* Attempt to open recording */
int fd = open(basename,
O_CREAT | O_EXCL | O_WRONLY,
S_IRUSR | S_IWUSR);
S_IRUSR | S_IWUSR | S_IRGRP);
/* Continuously retry with alternate names on failure */
if (fd == -1) {
@ -103,7 +102,7 @@ static int guac_common_recording_open(const char* path,
/* Retry with newly-suffixed filename */
fd = open(basename,
O_CREAT | O_EXCL | O_WRONLY,
S_IRUSR | S_IWUSR);
S_IRUSR | S_IWUSR | S_IRGRP);
}
@ -135,15 +134,17 @@ static int guac_common_recording_open(const char* path,
}
guac_common_recording* guac_common_recording_create(guac_client* client,
guac_recording* guac_recording_create(guac_client* client,
const char* path, const char* name, int create_path,
int include_output, int include_mouse, int include_keys) {
int include_output, int include_mouse, int include_touch,
int include_keys) {
char filename[GUAC_COMMON_RECORDING_MAX_NAME_LENGTH];
/* Create path if it does not exist, fail if impossible */
#ifndef __MINGW32__
if (create_path && mkdir(path, S_IRWXU) && errno != EEXIST) {
if (create_path && mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP)
&& errno != EEXIST) {
#else
if (create_path && _mkdir(path) && errno != EEXIST) {
#endif
@ -153,7 +154,7 @@ guac_common_recording* guac_common_recording_create(guac_client* client,
}
/* Attempt to open recording file */
int fd = guac_common_recording_open(path, name, filename, sizeof(filename));
int fd = guac_recording_open(path, name, filename, sizeof(filename));
if (fd == -1) {
guac_client_log(client, GUAC_LOG_ERROR,
"Creation of recording failed: %s", strerror(errno));
@ -161,10 +162,11 @@ guac_common_recording* guac_common_recording_create(guac_client* client,
}
/* Create recording structure with reference to underlying socket */
guac_common_recording* recording = malloc(sizeof(guac_common_recording));
guac_recording* recording = malloc(sizeof(guac_recording));
recording->socket = guac_socket_open(fd);
recording->include_output = include_output;
recording->include_mouse = include_mouse;
recording->include_touch = include_touch;
recording->include_keys = include_keys;
/* Replace client socket with wrapped recording socket only if including
@ -181,7 +183,7 @@ guac_common_recording* guac_common_recording_create(guac_client* client,
}
void guac_common_recording_free(guac_common_recording* recording) {
void guac_recording_free(guac_recording* recording) {
/* If not including broadcast output, the output socket is not associated
* with the client, and must be freed manually */
@ -193,7 +195,7 @@ void guac_common_recording_free(guac_common_recording* recording) {
}
void guac_common_recording_report_mouse(guac_common_recording* recording,
void guac_recording_report_mouse(guac_recording* recording,
int x, int y, int button_mask) {
/* Report mouse location only if recording should contain mouse events */
@ -203,7 +205,18 @@ void guac_common_recording_report_mouse(guac_common_recording* recording,
}
void guac_common_recording_report_key(guac_common_recording* recording,
void guac_recording_report_touch(guac_recording* recording,
int id, int x, int y, int x_radius, int y_radius,
double angle, double force) {
/* Report touches only if recording should contain touch events */
if (recording->include_touch)
guac_protocol_send_touch(recording->socket, id, x, y,
x_radius, y_radius, angle, force, guac_timestamp_current());
}
void guac_recording_report_key(guac_recording* recording,
int keysym, int pressed) {
/* Report key state only if recording should contain key events */

View File

@ -44,6 +44,8 @@ char __guac_socket_BASE64_CHARACTERS[64] = {
static void* __guac_socket_keep_alive_thread(void* data) {
int old_cancelstate;
/* Calculate sleep interval */
struct timespec interval;
interval.tv_sec = GUAC_SOCKET_KEEP_ALIVE_INTERVAL / 1000;
@ -65,8 +67,11 @@ static void* __guac_socket_keep_alive_thread(void* data) {
}
/* Sleep until next keep-alive check */
/* Sleep until next keep-alive check, but allow thread cancellation
* during that sleep */
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate);
nanosleep(&interval, NULL);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
}
@ -202,9 +207,11 @@ void guac_socket_free(guac_socket* socket) {
/* Mark as closed */
socket->state = GUAC_SOCKET_CLOSED;
/* Wait for keep-alive, if enabled */
if (socket->__keep_alive_enabled)
/* Stop keep-alive thread, if enabled */
if (socket->__keep_alive_enabled) {
pthread_cancel(socket->__keep_alive_thread);
pthread_join(socket->__keep_alive_thread, NULL);
}
free(socket);
}

View File

@ -81,6 +81,49 @@ size_t guac_strlcat(char* restrict dest, const char* restrict src, size_t n) {
}
char* guac_strnstr(const char *haystack, const char *needle, size_t len) {
#ifdef HAVE_STRNSTR
return strnstr(haystack, needle, len);
#else
char* chr;
size_t nlen = strlen(needle), off = 0;
/* Follow documented API: return haystack if needle is the empty string. */
if (nlen == 0)
return (char *)haystack;
/* Use memchr to find candidates. It might be optimized in asm. */
while (off < len && NULL != (chr = memchr(haystack + off, needle[0], len - off))) {
/* chr is guaranteed to be in bounds of and >= haystack. */
off = chr - haystack;
/* If needle would go beyond provided len, it doesn't exist in haystack. */
if (off + nlen > len)
return NULL;
/* Now that we know we have at least nlen bytes, compare them. */
if (!memcmp(chr, needle, nlen))
return chr;
/* Make sure we make progress. */
off += 1;
}
/* memchr ran out of candidates, needle wasn't found. */
return NULL;
#endif
}
char* guac_strdup(const char* str) {
/* Return NULL if no string provided */
if (str == NULL)
return NULL;
/* Otherwise just invoke strdup() */
return strdup(str);
}
size_t guac_strljoin(char* restrict dest, const char* restrict const* elements,
int nmemb, const char* restrict delim, size_t n) {

View File

@ -36,15 +36,19 @@ TESTS = $(check_PROGRAMS)
test_libguac_SOURCES = \
client/buffer_pool.c \
client/layer_pool.c \
id/generate.c \
parser/append.c \
parser/read.c \
pool/next_free.c \
protocol/base64_decode.c \
protocol/guac_protocol_version.c \
socket/fd_send_instruction.c \
socket/nested_send_instruction.c \
string/strdup.c \
string/strlcat.c \
string/strlcpy.c \
string/strljoin.c \
string/strnstr.c \
unicode/charsize.c \
unicode/read.c \
unicode/strlen.c \

View File

@ -0,0 +1,88 @@
/*
* 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 "id.h"
#include <CUnit/CUnit.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/**
* Test which verifies that each call to guac_generate_id() produces a
* different string.
*/
void test_id__unique() {
char* id1 = guac_generate_id('x');
char* id2 = guac_generate_id('x');
/* Neither string may be NULL */
CU_ASSERT_PTR_NOT_NULL_FATAL(id1);
CU_ASSERT_PTR_NOT_NULL_FATAL(id2);
/* Both strings should be different */
CU_ASSERT_STRING_NOT_EQUAL(id1, id2);
free(id1);
free(id2);
}
/**
* Test which verifies that guac_generate_id() produces strings are in the
* correc UUID-based format.
*/
void test_id__format() {
unsigned int ignore;
char* id = guac_generate_id('x');
CU_ASSERT_PTR_NOT_NULL_FATAL(id);
int items_read = sscanf(id, "x%08x-%04x-%04x-%04x-%08x%04x",
&ignore, &ignore, &ignore, &ignore, &ignore, &ignore);
CU_ASSERT_EQUAL(items_read, 6);
CU_ASSERT_EQUAL(strlen(id), 37);
free(id);
}
/**
* Test which verifies that guac_generate_id() takes the specified prefix
* character into account when generating the ID string.
*/
void test_id__prefix() {
char* id;
id = guac_generate_id('a');
CU_ASSERT_PTR_NOT_NULL_FATAL(id);
CU_ASSERT_EQUAL(id[0], 'a');
free(id);
id = guac_generate_id('b');
CU_ASSERT_PTR_NOT_NULL_FATAL(id);
CU_ASSERT_EQUAL(id[0], 'b');
free(id);
}

View File

@ -72,7 +72,7 @@ static void write_instructions(int fd) {
/**
* Reads and parses instructions from the given file descriptor using a
* guac_socket and guac_parser, verfying that those instructions match the
* guac_socket and guac_parser, verifying that those instructions match the
* series of Guacamole instructions expected to be written by
* write_instructions(). The given file descriptor is automatically closed as a
* result of calling this function.

View File

@ -0,0 +1,69 @@
/*
* 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 <CUnit/CUnit.h>
#include <guacamole/protocol.h>
#include <guacamole/protocol-types.h>
/**
* Test which verifies that conversion of the guac_protocol_version enum to
* string values succeeds and produces the expected results.
*/
void test_guac_protocol__version_to_string() {
guac_protocol_version version_a = GUAC_PROTOCOL_VERSION_1_5_0;
guac_protocol_version version_b = GUAC_PROTOCOL_VERSION_1_0_0;
guac_protocol_version version_c = GUAC_PROTOCOL_VERSION_UNKNOWN;
CU_ASSERT_STRING_EQUAL(guac_protocol_version_to_string(version_a), "VERSION_1_5_0");
CU_ASSERT_STRING_EQUAL(guac_protocol_version_to_string(version_b), "VERSION_1_0_0");
CU_ASSERT_PTR_NULL(guac_protocol_version_to_string(version_c));
}
/**
* Test which verifies that the version of String representations of Guacamole
* protocol versions are successfully converted into their matching
* guac_protocol_version enum values, and that versions that do not match
* any version get the correct unknown value.
*/
void test_guac_protocol__string_to_version() {
char* str_version_a = "VERSION_1_3_0";
char* str_version_b = "VERSION_1_1_0";
char* str_version_c = "AVACADO";
char* str_version_d = "VERSION_31_4_1";
CU_ASSERT_EQUAL(guac_protocol_string_to_version(str_version_a), GUAC_PROTOCOL_VERSION_1_3_0);
CU_ASSERT_EQUAL(guac_protocol_string_to_version(str_version_b), GUAC_PROTOCOL_VERSION_1_1_0);
CU_ASSERT_EQUAL(guac_protocol_string_to_version(str_version_c), GUAC_PROTOCOL_VERSION_UNKNOWN);
CU_ASSERT_EQUAL(guac_protocol_string_to_version(str_version_d), GUAC_PROTOCOL_VERSION_UNKNOWN);
}
/**
* Test which verifies that the comparisons between guac_protocol_version enum
* values produces the expected results.
*/
void test_gauc_protocol__version_comparison() {
CU_ASSERT_TRUE(GUAC_PROTOCOL_VERSION_1_3_0 > GUAC_PROTOCOL_VERSION_1_0_0);
CU_ASSERT_TRUE(GUAC_PROTOCOL_VERSION_UNKNOWN < GUAC_PROTOCOL_VERSION_1_1_0);
}

View File

@ -54,7 +54,7 @@ static void write_instructions(int fd) {
/* Write instructions */
guac_protocol_send_name(socket, "a" UTF8_4 "b" UTF8_4 "c");
guac_protocol_send_sync(socket, 12345);
guac_protocol_send_sync(socket, 12345, 1);
guac_socket_flush(socket);
/* Close and free socket */
@ -64,7 +64,7 @@ static void write_instructions(int fd) {
/**
* Reads raw bytes from the given file descriptor until no further bytes
* remain, verfying that those bytes represent the series of Guacamole
* remain, verifying that those bytes represent the series of Guacamole
* instructions expected to be written by write_instructions(). The given
* file descriptor is automatically closed as a result of calling this
* function.
@ -76,7 +76,7 @@ static void read_expected_instructions(int fd) {
char expected[] =
"4.name,11.a" UTF8_4 "b" UTF8_4 "c;"
"4.sync,5.12345;";
"4.sync,5.12345,1.1;";
int numread;
char buffer[1024];

View File

@ -65,7 +65,7 @@ static void write_instructions(int fd) {
/* Write instructions */
guac_protocol_send_name(nested_socket, "a" UTF8_4 "b" UTF8_4 "c");
guac_protocol_send_sync(nested_socket, 12345);
guac_protocol_send_sync(nested_socket, 12345, 1);
/* Close and free sockets */
guac_socket_free(nested_socket);
@ -75,7 +75,7 @@ static void write_instructions(int fd) {
/**
* Reads raw bytes from the given file descriptor until no further bytes
* remain, verfying that those bytes represent the series of Guacamole
* remain, verifying that those bytes represent the series of Guacamole
* instructions expected to be written by write_instructions(). The given
* file descriptor is automatically closed as a result of calling this
* function.
@ -86,9 +86,9 @@ static void write_instructions(int fd) {
static void read_expected_instructions(int fd) {
char expected[] =
"4.nest,3.123,37."
"4.nest,3.123,41."
"4.name,11.a" UTF8_4 "b" UTF8_4 "c;"
"4.sync,5.12345;"
"4.sync,5.12345,1.1;"
";";
int numread;

View File

@ -0,0 +1,49 @@
/*
* 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 <CUnit/CUnit.h>
#include <guacamole/string.h>
#include <stdlib.h>
#include <string.h>
/**
* Source test string for copying.
*/
const char* source_string = "Mashing avocados.";
/**
* A NULL string variable for copying to insure that NULL is copied properly.
*/
const char* null_string = NULL;
/**
* Verify guac_strdup() behavior when the string is both NULL and not NULL.
*/
void test_string__strdup() {
/* Copy the strings. */
char* dest_string = guac_strdup(source_string);
char* null_copy = guac_strdup(null_string);
/* Run the tests. */
CU_ASSERT_STRING_EQUAL(dest_string, "Mashing avocados.");
CU_ASSERT_PTR_NULL(null_copy);
}

View File

@ -0,0 +1,73 @@
/*
* 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 <CUnit/CUnit.h>
#include <guacamole/string.h>
#include <stdlib.h>
#include <string.h>
/**
* Verify guac_strnstr() behaviors:
*/
void test_string__strnstr() {
char haystack[8] = {'a', 'h', 'i', ' ', 't', 'u', 'n', 'a'};
char* result;
/* needle exists at start of haystack */
result = guac_strnstr(haystack, "ah", sizeof(haystack));
CU_ASSERT_EQUAL(result, haystack);
/* needle exists in the middle of haystack */
result = guac_strnstr(haystack, "hi", sizeof(haystack));
CU_ASSERT_EQUAL(result, haystack + 1);
/* needle exists at end of haystack */
result = guac_strnstr(haystack, "tuna", sizeof(haystack));
CU_ASSERT_EQUAL(result, haystack + 4);
/* needle doesn't exist in haystack, needle[0] isn't in haystack */
result = guac_strnstr(haystack, "mahi", sizeof(haystack));
CU_ASSERT_EQUAL(result, NULL);
/*
* needle doesn't exist in haystack, needle[0] is in haystack,
* length wouldn't allow needle to exist
*/
result = guac_strnstr(haystack, "narwhal", sizeof(haystack));
CU_ASSERT_EQUAL(result, NULL);
/*
* needle doesn't exist in haystack, needle[0] is in haystack,
* length would allow needle to exist
*/
result = guac_strnstr(haystack, "taco", sizeof(haystack));
CU_ASSERT_EQUAL(result, NULL);
/*
* needle doesn't exist in haystack, needle[0] is in haystack
* multiple times
*/
result = guac_strnstr(haystack, "ahha", sizeof(haystack));
CU_ASSERT_EQUAL(result, NULL);
/* empty needle should return haystack according to API docs */
result = guac_strnstr(haystack, "", sizeof(haystack));
CU_ASSERT_EQUAL(result, haystack);
}

View File

@ -37,6 +37,7 @@
__guac_instruction_handler_mapping __guac_instruction_handler_map[] = {
{"sync", __guac_handle_sync},
{"touch", __guac_handle_touch},
{"mouse", __guac_handle_mouse},
{"key", __guac_handle_key},
{"clipboard", __guac_handle_clipboard},
@ -63,6 +64,7 @@ __guac_instruction_handler_mapping __guac_handshake_handler_map[] = {
{"video", __guac_handshake_video_handler},
{"image", __guac_handshake_image_handler},
{"timezone", __guac_handshake_timezone_handler},
{"name", __guac_handshake_name_handler},
{NULL, NULL}
};
@ -119,37 +121,60 @@ int __guac_handle_sync(guac_user* user, int argc, char** argv) {
/* Calculate length of frame, including network and processing lag */
frame_duration = current - timestamp;
/* Update lag statistics if at least one frame has been rendered */
/* Calculate processing lag portion of length of frame */
int frame_processing_lag = 0;
if (user->last_frame_duration != 0) {
/* Calculate lag using the previous frame as a baseline */
int processing_lag = frame_duration - user->last_frame_duration;
frame_processing_lag = frame_duration - user->last_frame_duration;
/* Adjust back to zero if cumulative error leads to a negative
* value */
if (processing_lag < 0)
processing_lag = 0;
user->processing_lag = processing_lag;
if (frame_processing_lag < 0)
frame_processing_lag = 0;
}
/* Record baseline duration of frame by excluding lag */
user->last_frame_duration = frame_duration - user->processing_lag;
/* Record baseline duration of frame by excluding lag (this is the
* network round-trip time) */
int estimated_rtt = frame_duration - frame_processing_lag;
user->last_frame_duration = estimated_rtt;
/* Calculate cumulative accumulated processing lag relative to server timeline */
int processing_lag = current - user->last_received_timestamp - estimated_rtt;
if (processing_lag < 0)
processing_lag = 0;
user->processing_lag = processing_lag;
}
/* Log received timestamp and calculated lag (at TRACE level only) */
guac_user_log(user, GUAC_LOG_TRACE,
"User confirmation of frame %" PRIu64 "ms received "
"at %" PRIu64 "ms (processing_lag=%ims)",
timestamp, current, user->processing_lag);
"at %" PRIu64 "ms (processing_lag=%ims, estimated_rtt=%ims)",
timestamp, current, user->processing_lag, user->last_frame_duration);
if (user->sync_handler)
return user->sync_handler(user, timestamp);
return 0;
}
int __guac_handle_touch(guac_user* user, int argc, char** argv) {
if (user->touch_handler)
return user->touch_handler(
user,
atoi(argv[0]), /* id */
atoi(argv[1]), /* x */
atoi(argv[2]), /* y */
atoi(argv[3]), /* x_radius */
atoi(argv[4]), /* y_radius */
atof(argv[5]), /* angle */
atof(argv[6]) /* force */
);
return 0;
}
int __guac_handle_mouse(guac_user* user, int argc, char** argv) {
if (user->mouse_handler)
return user->mouse_handler(
@ -660,6 +685,23 @@ int __guac_handshake_image_handler(guac_user* user, int argc, char** argv) {
}
int __guac_handshake_name_handler(guac_user* user, int argc, char** argv) {
/* Free any past value for the user's name */
free((char *) user->info.name);
/* If a value is provided for the name, copy it into guac_user. */
if (argc > 0 && strcmp(argv[0], ""))
user->info.name = (const char*) strdup(argv[0]);
/* No or empty value was provided, so make sure this is NULLed out. */
else
user->info.name = NULL;
return 0;
}
int __guac_handshake_timezone_handler(guac_user* user, int argc, char** argv) {
/* Free any past value */

View File

@ -85,6 +85,13 @@ __guac_instruction_handler __guac_handle_sync;
*/
__guac_instruction_handler __guac_handle_mouse;
/**
* Internal initial handler for the touch instruction. When a touch instruction
* is received, this handler will be called. The client's touch handler will
* be invoked if defined.
*/
__guac_instruction_handler __guac_handle_touch;
/**
* Internal initial handler for the key instruction. When a key instruction
* is received, this handler will be called. The client's key handler will
@ -211,6 +218,13 @@ __guac_instruction_handler __guac_handshake_video_handler;
*/
__guac_instruction_handler __guac_handshake_image_handler;
/**
* Internal handler function that is called when the name instruction is
* received during the handshake process, specifying the name of the Guacamole
* user establishing the connection.
*/
__guac_instruction_handler __guac_handshake_name_handler;
/**
* Internal handler function that is called when the timezone instruction is
* received during the handshake process, specifying the timezone of the

View File

@ -100,7 +100,7 @@ static void guac_user_log_guac_error(guac_user* user,
static void guac_user_log_handshake_failure(guac_user* user) {
if (guac_error == GUAC_STATUS_CLOSED)
guac_user_log(user, GUAC_LOG_INFO,
guac_user_log(user, GUAC_LOG_DEBUG,
"Guacamole connection closed during handshake");
else if (guac_error == GUAC_STATUS_PROTOCOL_ERROR)
guac_user_log(user, GUAC_LOG_ERROR,
@ -296,6 +296,7 @@ int guac_user_handle_connection(guac_user* user, int usec_timeout) {
user->info.audio_mimetypes = NULL;
user->info.image_mimetypes = NULL;
user->info.video_mimetypes = NULL;
user->info.name = NULL;
user->info.timezone = NULL;
/* Count number of arguments. */
@ -344,12 +345,16 @@ int guac_user_handle_connection(guac_user* user, int usec_timeout) {
guac_client_log(client, GUAC_LOG_INFO, "User \"%s\" joined connection "
"\"%s\" (%i users now present)", user->user_id,
client->connection_id, client->connected_users);
if (strcmp(parser->argv[0],"") != 0)
if (strcmp(parser->argv[0],"") != 0) {
guac_client_log(client, GUAC_LOG_DEBUG, "Client is using protocol "
"version \"%s\"", parser->argv[0]);
else
user->info.protocol_version = guac_protocol_string_to_version(parser->argv[0]);
}
else {
guac_client_log(client, GUAC_LOG_DEBUG, "Client has not defined "
"its protocol version.");
user->info.protocol_version = GUAC_PROTOCOL_VERSION_1_0_0;
}
/* Handle user I/O, wait for connection to terminate */
guac_user_start(parser, user, usec_timeout);
@ -366,7 +371,8 @@ int guac_user_handle_connection(guac_user* user, int usec_timeout) {
guac_free_mimetypes((char **) user->info.image_mimetypes);
guac_free_mimetypes((char **) user->info.video_mimetypes);
/* Free timezone info. */
/* Free name and timezone info. */
free((char *) user->info.name);
free((char *) user->info.timezone);
guac_parser_free(parser);

View File

@ -316,6 +316,24 @@ void guac_user_stream_webp(guac_user* user, guac_socket* socket,
}
int guac_user_supports_msg(guac_user* user) {
if (user == NULL)
return 0;
return (user->info.protocol_version >= GUAC_PROTOCOL_VERSION_1_5_0);
}
int guac_user_supports_required(guac_user* user) {
if (user == NULL)
return 0;
return (user->info.protocol_version >= GUAC_PROTOCOL_VERSION_1_3_0);
}
int guac_user_supports_webp(guac_user* user) {
#ifdef ENABLE_WEBP

198
src/libguac/wol.c Normal file
View File

@ -0,0 +1,198 @@
/*
* 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 "config.h"
#include "guacamole/error.h"
#include "guacamole/wol.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
/**
* Generate the magic Wake-on-LAN (WoL) packet for the specified MAC address
* and place it in the character array.
*
* @param packet
* The character array that will contain the generated packet.
*
* @param mac_address
* The unsigned int representation of the MAC address to place in the packet.
*/
static void __guac_wol_create_magic_packet(unsigned char packet[],
unsigned int mac_address[]) {
int i;
unsigned char mac[6];
/* Concurrently fill the first part of the packet with 0xFF, and copy the
MAC address from the int array to the char array. */
for (i = 0; i < 6; i++) {
packet[i] = 0xFF;
mac[i] = mac_address[i];
}
/* Copy the MAC address contents into the char array that is storing
the rest of the packet. */
for (i = 1; i <= 16; i++) {
memcpy(&packet[i * 6], &mac, 6 * sizeof(unsigned char));
}
}
/**
* Send the magic Wake-on-LAN (WoL) packet to the specified broadcast address,
* returning the number of bytes sent, or zero if any error occurred and nothing
* was sent.
*
* @param broadcast_addr
* The broadcast address to which to send the magic WoL packet.
*
* @param udp_port
* The UDP port to use when sending the WoL packet.
*
* @param packet
* The magic WoL packet to send.
*
* @return
* The number of bytes sent, or zero if nothing could be sent.
*/
static ssize_t __guac_wol_send_packet(const char* broadcast_addr,
const unsigned short udp_port, unsigned char packet[]) {
struct sockaddr_in wol_dest;
int wol_socket;
/* Determine the IP version, starting with IPv4. */
wol_dest.sin_port = htons(udp_port);
wol_dest.sin_family = AF_INET;
int retval = inet_pton(wol_dest.sin_family, broadcast_addr, &(wol_dest.sin_addr));
/* If return value is less than zero, this system doesn't know about IPv4. */
if (retval < 0) {
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "IPv4 address family is not supported";
return 0;
}
/* If return value is zero, address doesn't match the IPv4, so try IPv6. */
else if (retval == 0) {
wol_dest.sin_family = AF_INET6;
retval = inet_pton(wol_dest.sin_family, broadcast_addr, &(wol_dest.sin_addr));
/* System does not support IPv6. */
if (retval < 0) {
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "IPv6 address family is not supported";
return 0;
}
/* Address didn't match IPv6. */
else if (retval == 0) {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Invalid broadcast or multicast address specified for Wake-on-LAN";
return 0;
}
}
/* Set up the socket */
wol_socket = socket(wol_dest.sin_family, SOCK_DGRAM, 0);
/* If socket open fails, bail out. */
if (wol_socket < 0) {
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "Failed to open socket to send Wake-on-LAN packet";
return 0;
}
/* Set up socket for IPv4 broadcast. */
if (wol_dest.sin_family == AF_INET) {
/* For configuring socket broadcast */
int wol_bcast = 1;
/* Attempt to set IPv4 broadcast; exit with error if this fails. */
if (setsockopt(wol_socket, SOL_SOCKET, SO_BROADCAST, &wol_bcast,
sizeof(wol_bcast)) < 0) {
close(wol_socket);
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "Failed to set IPv4 broadcast for Wake-on-LAN socket";
return 0;
}
}
/* Set up socket for IPv6 multicast. */
else {
/* Stick to a single hop for now. */
int hops = 1;
/* Attempt to set IPv6 multicast; exit with error if this fails. */
if (setsockopt(wol_socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops,
sizeof(hops)) < 0) {
close(wol_socket);
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "Failed to set IPv6 multicast for Wake-on-LAN socket";
return 0;
}
}
/* Send the packet and return number of bytes sent. */
int bytes = sendto(wol_socket, packet, GUAC_WOL_PACKET_SIZE, 0,
(struct sockaddr*) &wol_dest, sizeof(wol_dest));
close(wol_socket);
return bytes;
}
int guac_wol_wake(const char* mac_addr, const char* broadcast_addr,
const unsigned short udp_port) {
unsigned char wol_packet[GUAC_WOL_PACKET_SIZE];
unsigned int dest_mac[6];
/* Parse mac address and return with error if parsing fails. */
if (sscanf(mac_addr, "%x:%x:%x:%x:%x:%x",
&(dest_mac[0]), &(dest_mac[1]), &(dest_mac[2]),
&(dest_mac[3]), &(dest_mac[4]), &(dest_mac[5])) != 6) {
guac_error = GUAC_STATUS_INVALID_ARGUMENT;
guac_error_message = "Invalid argument for Wake-on-LAN MAC address";
return -1;
}
/* Generate the magic packet. */
__guac_wol_create_magic_packet(wol_packet, dest_mac);
/* Send the packet and record bytes sent. */
int bytes_sent = __guac_wol_send_packet(broadcast_addr, udp_port,
wol_packet);
/* Return 0 if bytes were sent, otherwise return an error. */
if (bytes_sent)
return 0;
return -1;
}

5
src/protocols/kubernetes/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# Auto-generated test runner and binary
_generated_runner.c
test_kubernetes

View File

@ -27,6 +27,7 @@ AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
lib_LTLIBRARIES = libguac-client-kubernetes.la
SUBDIRS = . tests
libguac_client_kubernetes_la_SOURCES = \
argv.c \

View File

@ -29,172 +29,34 @@
#include <stdlib.h>
#include <string.h>
/**
* All Kubernetes connection settings which may be updated by unprivileged
* users through "argv" streams.
*/
typedef enum guac_kubernetes_argv_setting {
/**
* The color scheme of the terminal.
*/
GUAC_KUBERNETES_ARGV_SETTING_COLOR_SCHEME,
/**
* The name of the font family used by the terminal.
*/
GUAC_KUBERNETES_ARGV_SETTING_FONT_NAME,
/**
* The size of the font used by the terminal, in points.
*/
GUAC_KUBERNETES_ARGV_SETTING_FONT_SIZE
} guac_kubernetes_argv_setting;
/**
* The value or current status of a connection parameter received over an
* "argv" stream.
*/
typedef struct guac_kubernetes_argv {
/**
* The specific setting being updated.
*/
guac_kubernetes_argv_setting setting;
/**
* Buffer space for containing the received argument value.
*/
char buffer[GUAC_KUBERNETES_ARGV_MAX_LENGTH];
/**
* The number of bytes received so far.
*/
int length;
} guac_kubernetes_argv;
/**
* Handler for "blob" instructions which appends the data from received blobs
* to the end of the in-progress argument value buffer.
*
* @see guac_user_blob_handler
*/
static int guac_kubernetes_argv_blob_handler(guac_user* user,
guac_stream* stream, void* data, int length) {
guac_kubernetes_argv* argv = (guac_kubernetes_argv*) stream->data;
/* Calculate buffer size remaining, including space for null terminator,
* adjusting received length accordingly */
int remaining = sizeof(argv->buffer) - argv->length - 1;
if (length > remaining)
length = remaining;
/* Append received data to end of buffer */
memcpy(argv->buffer + argv->length, data, length);
argv->length += length;
return 0;
}
/**
* Handler for "end" instructions which applies the changes specified by the
* argument value buffer associated with the stream.
*
* @see guac_user_end_handler
*/
static int guac_kubernetes_argv_end_handler(guac_user* user,
guac_stream* stream) {
int size;
int guac_kubernetes_argv_callback(guac_user* user, const char* mimetype,
const char* name, const char* value, void* data) {
guac_client* client = user->client;
guac_kubernetes_client* kubernetes_client = (guac_kubernetes_client*) client->data;
guac_terminal* terminal = kubernetes_client->term;
/* Append null terminator to value */
guac_kubernetes_argv* argv = (guac_kubernetes_argv*) stream->data;
argv->buffer[argv->length] = '\0';
/* Update color scheme */
if (strcmp(name, GUAC_KUBERNETES_ARGV_COLOR_SCHEME) == 0)
guac_terminal_apply_color_scheme(terminal, value);
/* Apply changes to chosen setting */
switch (argv->setting) {
/* Update color scheme */
case GUAC_KUBERNETES_ARGV_SETTING_COLOR_SCHEME:
guac_terminal_apply_color_scheme(terminal, argv->buffer);
guac_client_stream_argv(client, client->socket, "text/plain",
"color-scheme", argv->buffer);
break;
/* Update font name */
case GUAC_KUBERNETES_ARGV_SETTING_FONT_NAME:
guac_terminal_apply_font(terminal, argv->buffer, -1, 0);
guac_client_stream_argv(client, client->socket, "text/plain",
"font-name", argv->buffer);
break;
/* Update font size */
case GUAC_KUBERNETES_ARGV_SETTING_FONT_SIZE:
/* Update only if font size is sane */
size = atoi(argv->buffer);
if (size > 0) {
guac_terminal_apply_font(terminal, NULL, size,
kubernetes_client->settings->resolution);
guac_client_stream_argv(client, client->socket, "text/plain",
"font-size", argv->buffer);
}
break;
/* Update font name */
else if (strcmp(name, GUAC_KUBERNETES_ARGV_FONT_NAME) == 0)
guac_terminal_apply_font(terminal, value, -1, 0);
/* Update only if font size is sane */
else if (strcmp(name, GUAC_KUBERNETES_ARGV_FONT_SIZE) == 0) {
int size = atoi(value);
if (size > 0)
guac_terminal_apply_font(terminal, NULL, size,
kubernetes_client->settings->resolution);
}
/* Update Kubernetes terminal size */
guac_kubernetes_resize(client, terminal->term_height,
terminal->term_width);
guac_kubernetes_resize(client,
guac_terminal_get_rows(terminal),
guac_terminal_get_columns(terminal));
free(argv);
return 0;
}
int guac_kubernetes_argv_handler(guac_user* user, guac_stream* stream,
char* mimetype, char* name) {
guac_kubernetes_argv_setting setting;
/* Allow users to update the color scheme and font details */
if (strcmp(name, "color-scheme") == 0)
setting = GUAC_KUBERNETES_ARGV_SETTING_COLOR_SCHEME;
else if (strcmp(name, "font-name") == 0)
setting = GUAC_KUBERNETES_ARGV_SETTING_FONT_NAME;
else if (strcmp(name, "font-size") == 0)
setting = GUAC_KUBERNETES_ARGV_SETTING_FONT_SIZE;
/* No other connection parameters may be updated */
else {
guac_protocol_send_ack(user->socket, stream, "Not allowed.",
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
guac_socket_flush(user->socket);
return 0;
}
guac_kubernetes_argv* argv = malloc(sizeof(guac_kubernetes_argv));
argv->setting = setting;
argv->length = 0;
/* Prepare stream to receive argument value */
stream->blob_handler = guac_kubernetes_argv_blob_handler;
stream->end_handler = guac_kubernetes_argv_end_handler;
stream->data = argv;
/* Signal stream is ready */
guac_protocol_send_ack(user->socket, stream, "Ready for updated "
"parameter.", GUAC_PROTOCOL_STATUS_SUCCESS);
guac_socket_flush(user->socket);
return 0;
}
@ -205,20 +67,21 @@ void* guac_kubernetes_send_current_argv(guac_user* user, void* data) {
guac_terminal* terminal = kubernetes_client->term;
/* Send current color scheme */
guac_user_stream_argv(user, user->socket, "text/plain", "color-scheme",
terminal->color_scheme);
guac_user_stream_argv(user, user->socket, "text/plain",
GUAC_KUBERNETES_ARGV_COLOR_SCHEME,
guac_terminal_get_color_scheme(terminal));
/* Send current font name */
guac_user_stream_argv(user, user->socket, "text/plain", "font-name",
terminal->font_name);
guac_user_stream_argv(user, user->socket, "text/plain",
GUAC_KUBERNETES_ARGV_FONT_NAME,
guac_terminal_get_font_name(terminal));
/* Send current font size */
char font_size[64];
sprintf(font_size, "%i", terminal->font_size);
guac_user_stream_argv(user, user->socket, "text/plain", "font-size",
font_size);
sprintf(font_size, "%i", guac_terminal_get_font_size(terminal));
guac_user_stream_argv(user, user->socket, "text/plain",
GUAC_KUBERNETES_ARGV_FONT_SIZE, font_size);
return NULL;
}

View File

@ -23,19 +23,32 @@
#include "config.h"
#include <guacamole/argv.h>
#include <guacamole/user.h>
/**
* The maximum number of bytes to allow for any argument value received via an
* argv stream, including null terminator.
* The name of the parameter that specifies/updates the color scheme used by
* the terminal emulator.
*/
#define GUAC_KUBERNETES_ARGV_MAX_LENGTH 16384
#define GUAC_KUBERNETES_ARGV_COLOR_SCHEME "color-scheme"
/**
* Handles an incoming stream from a Guacamole "argv" instruction, updating the
* given connection parameter if that parameter is allowed to be updated.
* The name of the parameter that specifies/updates the name of the font used
* by the terminal emulator.
*/
guac_user_argv_handler guac_kubernetes_argv_handler;
#define GUAC_KUBERNETES_ARGV_FONT_NAME "font-name"
/**
* The name of the parameter that specifies/updates the font size used by the
* terminal emulator.
*/
#define GUAC_KUBERNETES_ARGV_FONT_SIZE "font-size"
/**
* Handles a received argument value from a Guacamole "argv" instruction,
* updating the given connection parameter.
*/
guac_argv_callback guac_kubernetes_argv_callback;
/**
* Sends the current values of all non-sensitive parameters which may be set

View File

@ -17,12 +17,13 @@
* under the License.
*/
#include "argv.h"
#include "client.h"
#include "common/clipboard.h"
#include "kubernetes.h"
#include "settings.h"
#include "user.h"
#include <guacamole/argv.h>
#include <guacamole/client.h>
#include <libwebsockets.h>
@ -93,12 +94,15 @@ int guac_client_init(guac_client* client) {
guac_kubernetes_client* kubernetes_client = calloc(1, sizeof(guac_kubernetes_client));
client->data = kubernetes_client;
/* Init clipboard */
kubernetes_client->clipboard = guac_common_clipboard_alloc(GUAC_KUBERNETES_CLIPBOARD_MAX_LENGTH);
/* Set handlers */
client->join_handler = guac_kubernetes_user_join_handler;
client->free_handler = guac_kubernetes_client_free_handler;
client->leave_handler = guac_kubernetes_user_leave_handler;
/* Register handlers for argument values that may be sent after the handshake */
guac_argv_register(GUAC_KUBERNETES_ARGV_COLOR_SCHEME, guac_kubernetes_argv_callback, NULL, GUAC_ARGV_OPTION_ECHO);
guac_argv_register(GUAC_KUBERNETES_ARGV_FONT_NAME, guac_kubernetes_argv_callback, NULL, GUAC_ARGV_OPTION_ECHO);
guac_argv_register(GUAC_KUBERNETES_ARGV_FONT_SIZE, guac_kubernetes_argv_callback, NULL, GUAC_ARGV_OPTION_ECHO);
/* Set locale and warn if not UTF-8 */
setlocale(LC_CTYPE, "");
@ -125,7 +129,6 @@ int guac_kubernetes_client_free_handler(guac_client* client) {
if (kubernetes_client->settings != NULL)
guac_kubernetes_settings_free(kubernetes_client->settings);
guac_common_clipboard_free(kubernetes_client->clipboard);
free(kubernetes_client);
return 0;

View File

@ -22,11 +22,6 @@
#include <guacamole/client.h>
/**
* The maximum number of bytes to allow within the clipboard.
*/
#define GUAC_KUBERNETES_CLIPBOARD_MAX_LENGTH 262144
/**
* Static reference to the guac_client associated with the active Kubernetes
* connection. While libwebsockets provides some means of storing and

View File

@ -18,8 +18,8 @@
*/
#include "clipboard.h"
#include "common/clipboard.h"
#include "kubernetes.h"
#include "terminal/terminal.h"
#include <guacamole/client.h>
#include <guacamole/stream.h>
@ -33,7 +33,7 @@ int guac_kubernetes_clipboard_handler(guac_user* user, guac_stream* stream,
(guac_kubernetes_client*) client->data;
/* Clear clipboard and prepare for new data */
guac_common_clipboard_reset(kubernetes_client->clipboard, mimetype);
guac_terminal_clipboard_reset(kubernetes_client->term, mimetype);
/* Set handlers for clipboard stream */
stream->blob_handler = guac_kubernetes_clipboard_blob_handler;
@ -50,7 +50,7 @@ int guac_kubernetes_clipboard_blob_handler(guac_user* user,
(guac_kubernetes_client*) client->data;
/* Append new data */
guac_common_clipboard_append(kubernetes_client->clipboard, data, length);
guac_terminal_clipboard_append(kubernetes_client->term, data, length);
return 0;
}

View File

@ -17,12 +17,12 @@
* under the License.
*/
#include "common/recording.h"
#include "input.h"
#include "kubernetes.h"
#include "terminal/terminal.h"
#include <guacamole/client.h>
#include <guacamole/recording.h>
#include <guacamole/user.h>
#include <stdlib.h>
@ -41,7 +41,7 @@ int guac_kubernetes_user_mouse_handler(guac_user* user,
/* Report mouse position within recording */
if (kubernetes_client->recording != NULL)
guac_common_recording_report_mouse(kubernetes_client->recording, x, y,
guac_recording_report_mouse(kubernetes_client->recording, x, y,
mask);
guac_terminal_send_mouse(term, user, x, y, mask);
@ -57,7 +57,7 @@ int guac_kubernetes_user_key_handler(guac_user* user, int keysym, int pressed) {
/* Report key state within recording */
if (kubernetes_client->recording != NULL)
guac_common_recording_report_key(kubernetes_client->recording,
guac_recording_report_key(kubernetes_client->recording,
keysym, pressed);
/* Skip if terminal not yet ready */
@ -86,8 +86,9 @@ int guac_kubernetes_user_size_handler(guac_user* user, int width, int height) {
guac_terminal_resize(terminal, width, height);
/* Update Kubernetes terminal window size if connected */
guac_kubernetes_resize(client, terminal->term_height,
terminal->term_width);
guac_kubernetes_resize(client,
guac_terminal_get_rows(terminal),
guac_terminal_get_columns(terminal));
return 0;
}

View File

@ -21,7 +21,6 @@
#include "argv.h"
#include "client.h"
#include "common/recording.h"
#include "io.h"
#include "kubernetes.h"
#include "ssl.h"
@ -30,6 +29,7 @@
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/recording.h>
#include <libwebsockets.h>
#include <pthread.h>
@ -213,10 +213,11 @@ void* guac_kubernetes_client_thread(void* data) {
}
/* Generate endpoint for attachment URL */
if (guac_kubernetes_endpoint_attach(endpoint_path, sizeof(endpoint_path),
if (guac_kubernetes_endpoint_uri(endpoint_path, sizeof(endpoint_path),
settings->kubernetes_namespace,
settings->kubernetes_pod,
settings->kubernetes_container)) {
settings->kubernetes_container,
settings->exec_command)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Unable to generate path for Kubernetes API endpoint: "
"Resulting path too long");
@ -228,21 +229,33 @@ void* guac_kubernetes_client_thread(void* data) {
/* Set up screen recording, if requested */
if (settings->recording_path != NULL) {
kubernetes_client->recording = guac_common_recording_create(client,
kubernetes_client->recording = guac_recording_create(client,
settings->recording_path,
settings->recording_name,
settings->create_recording_path,
!settings->recording_exclude_output,
!settings->recording_exclude_mouse,
0, /* Touch events not supported */
settings->recording_include_keys);
}
/* Create terminal options with required parameters */
guac_terminal_options* options = guac_terminal_options_create(
settings->width, settings->height, settings->resolution);
/* Set optional parameters */
options->disable_copy = settings->disable_copy;
options->max_scrollback = settings->max_scrollback;
options->font_name = settings->font_name;
options->font_size = settings->font_size;
options->color_scheme = settings->color_scheme;
options->backspace = settings->backspace;
/* Create terminal */
kubernetes_client->term = guac_terminal_create(client,
kubernetes_client->clipboard, settings->disable_copy,
settings->max_scrollback, settings->font_name, settings->font_size,
settings->resolution, settings->width, settings->height,
settings->color_scheme, settings->backspace);
kubernetes_client->term = guac_terminal_create(client, options);
/* Free options struct now that it's been used */
free(options);
/* Fail if terminal init failed */
if (kubernetes_client->term == NULL) {
@ -356,7 +369,7 @@ fail:
/* Clean up recording, if in progress */
if (kubernetes_client->recording != NULL)
guac_common_recording_free(kubernetes_client->recording);
guac_recording_free(kubernetes_client->recording);
/* Free WebSocket context if successfully allocated */
if (kubernetes_client->context != NULL)
@ -400,8 +413,8 @@ void guac_kubernetes_force_redraw(guac_client* client) {
/* Get current terminal dimensions */
guac_terminal* term = kubernetes_client->term;
int rows = term->term_height;
int columns = term->term_width;
int rows = guac_terminal_get_rows(term);
int columns = guac_terminal_get_columns(term);
/* Force a redraw by increasing the terminal size by one character in
* each dimension and then resizing it back to normal (the same technique

View File

@ -21,12 +21,12 @@
#define GUAC_KUBERNETES_H
#include "common/clipboard.h"
#include "common/recording.h"
#include "io.h"
#include "settings.h"
#include "terminal/terminal.h"
#include <guacamole/client.h>
#include <guacamole/recording.h>
#include <libwebsockets.h>
#include <pthread.h>
@ -102,11 +102,6 @@ typedef struct guac_kubernetes_client {
*/
pthread_t client_thread;
/**
* The current clipboard contents.
*/
guac_common_clipboard* clipboard;
/**
* The terminal which will render all output from the Kubernetes pod.
*/
@ -128,7 +123,7 @@ typedef struct guac_kubernetes_client {
* The in-progress session recording, or NULL if no recording is in
* progress.
*/
guac_common_recording* recording;
guac_recording* recording;
} guac_kubernetes_client;

View File

@ -17,7 +17,9 @@
* under the License.
*/
#include "argv.h"
#include "settings.h"
#include "terminal/terminal.h"
#include <guacamole/user.h>
@ -30,14 +32,15 @@ const char* GUAC_KUBERNETES_CLIENT_ARGS[] = {
"namespace",
"pod",
"container",
"exec-command",
"use-ssl",
"client-cert",
"client-key",
"ca-cert",
"ignore-cert",
"font-name",
"font-size",
"color-scheme",
GUAC_KUBERNETES_ARGV_FONT_NAME,
GUAC_KUBERNETES_ARGV_FONT_SIZE,
GUAC_KUBERNETES_ARGV_COLOR_SCHEME,
"typescript-path",
"typescript-name",
"create-typescript-path",
@ -85,6 +88,11 @@ enum KUBERNETES_ARGS_IDX {
*/
IDX_CONTAINER,
/**
* The command used by exec call. If omitted, attach call will be used.
*/
IDX_EXEC_COMMAND,
/**
* Whether SSL/TLS should be used. If omitted, SSL/TLS will not be used.
*/
@ -208,8 +216,8 @@ enum KUBERNETES_ARGS_IDX {
IDX_READ_ONLY,
/**
* ASCII code, as an integer to use for the backspace key, or 127
* if not specified.
* ASCII code, as an integer to use for the backspace key, or
* GUAC_TERMINAL_DEFAULT_BACKSPACE if not specified.
*/
IDX_BACKSPACE,
@ -274,6 +282,11 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_CONTAINER, NULL);
/* Read exec command (optional) */
settings->exec_command =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_EXEC_COMMAND, NULL);
/* Parse whether SSL should be used */
settings->use_ssl =
guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
@ -308,22 +321,22 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
/* Read maximum scrollback size */
settings->max_scrollback =
guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_SCROLLBACK, GUAC_KUBERNETES_DEFAULT_MAX_SCROLLBACK);
IDX_SCROLLBACK, GUAC_TERMINAL_DEFAULT_MAX_SCROLLBACK);
/* Read font name */
settings->font_name =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_FONT_NAME, GUAC_KUBERNETES_DEFAULT_FONT_NAME);
IDX_FONT_NAME, GUAC_TERMINAL_DEFAULT_FONT_NAME);
/* Read font size */
settings->font_size =
guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_FONT_SIZE, GUAC_KUBERNETES_DEFAULT_FONT_SIZE);
IDX_FONT_SIZE, GUAC_TERMINAL_DEFAULT_FONT_SIZE);
/* Copy requested color scheme */
settings->color_scheme =
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_COLOR_SCHEME, "");
IDX_COLOR_SCHEME, GUAC_TERMINAL_DEFAULT_COLOR_SCHEME);
/* Pull width/height/resolution directly from user */
settings->width = user->info.optimal_width;
@ -378,7 +391,7 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
/* Parse backspace key code */
settings->backspace =
guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_BACKSPACE, 127);
IDX_BACKSPACE, GUAC_TERMINAL_DEFAULT_BACKSPACE);
/* Parse clipboard copy disable flag */
settings->disable_copy =
@ -405,6 +418,9 @@ void guac_kubernetes_settings_free(guac_kubernetes_settings* settings) {
free(settings->kubernetes_pod);
free(settings->kubernetes_container);
/* Free Kubernetes exec command */
free(settings->exec_command);
/* Free SSL/TLS details */
free(settings->client_cert);
free(settings->client_key);

View File

@ -24,17 +24,6 @@
#include <stdbool.h>
/**
* The name of the font to use for the terminal if no name is specified.
*/
#define GUAC_KUBERNETES_DEFAULT_FONT_NAME "monospace"
/**
* The size of the font to use for the terminal if no font size is specified,
* in points.
*/
#define GUAC_KUBERNETES_DEFAULT_FONT_SIZE 12
/**
* The port to connect to when initiating any Kubernetes connection, if no
* other port is specified.
@ -57,11 +46,6 @@
*/
#define GUAC_KUBERNETES_DEFAULT_RECORDING_NAME "recording"
/**
* The default maximum scrollback size in rows.
*/
#define GUAC_KUBERNETES_DEFAULT_MAX_SCROLLBACK 1000
/**
* Settings for the Kubernetes connection. The values for this structure are
* parsed from the arguments given during the Guacamole protocol handshake
@ -97,6 +81,12 @@ typedef struct guac_kubernetes_settings {
*/
char* kubernetes_container;
/**
* The command to generate api endpoint for call exec.
* If omitted call attach will be used.
*/
char* exec_command;
/**
* Whether SSL/TLS should be used.
*/

View File

@ -0,0 +1,66 @@
#
# 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.
#
AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
#
# Unit tests for Kubernetes support
#
check_PROGRAMS = test_kubernetes
TESTS = $(check_PROGRAMS)
test_kubernetes_SOURCES = \
url/append.c \
url/escape.c
test_kubernetes_CFLAGS = \
-Werror -Wall -pedantic \
@LIBGUAC_CLIENT_KUBERNETES_INCLUDE@ \
@LIBGUAC_INCLUDE@
test_kubernetes_LDADD = \
@CUNIT_LIBS@ \
@LIBGUAC_CLIENT_KUBERNETES_LTLIB@
#
# Autogenerate test runner
#
GEN_RUNNER = $(top_srcdir)/util/generate-test-runner.pl
CLEANFILES = _generated_runner.c
_generated_runner.c: $(test_kubernetes_SOURCES)
$(AM_V_GEN) $(GEN_RUNNER) $(test_kubernetes_SOURCES) > $@
nodist_test_kubernetes_SOURCES = \
_generated_runner.c
# Use automake's TAP test driver for running any tests
LOG_DRIVER = \
env AM_TAP_AWK='$(AWK)' \
$(SHELL) $(top_srcdir)/build-aux/tap-driver.sh

View File

@ -0,0 +1,74 @@
/*
* 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 "url.h"
#include <CUnit/CUnit.h>
#include <stdio.h>
#include <stdlib.h>
/**
* Verifies that guac_kubernetes_append_endpoint_param() correctly appends
* parameters to URLs that do not already have a query string.
*/
void test_url__append_no_query() {
char url[256] = "http://example.net";
CU_ASSERT(!guac_kubernetes_append_endpoint_param(url, sizeof(url), "foo", "100% test value"));
CU_ASSERT_STRING_EQUAL(url, "http://example.net?foo=100%25%20test%20value");
}
/**
* Verifies that guac_kubernetes_append_endpoint_param() correctly appends
* parameters to URLs that already have a query string.
*/
void test_url__append_existing_query() {
char url[256] = "http://example.net?foo=test%20value";
CU_ASSERT(!guac_kubernetes_append_endpoint_param(url, sizeof(url), "foo2", "yet&another/test\\value"));
CU_ASSERT_STRING_EQUAL(url, "http://example.net?foo=test%20value&foo2=yet%26another%2Ftest%5Cvalue");
}
/**
* Verifies that guac_kubernetes_append_endpoint_param() refuses to overflow
* the bounds of the provided buffer.
*/
void test_url__append_bounds() {
char url[256];
/* Appending "?a=1" to the 18-character string "http://example.net" should
* fail for all buffer sizes with 22 bytes or less, with a 22-byte buffer
* lacking space for the null terminator */
for (int length = 18; length <= 22; length++) {
strcpy(url, "http://example.net");
printf("Testing buffer with length %i ...\n", length);
CU_ASSERT(guac_kubernetes_append_endpoint_param(url, length, "a", "1"));
}
/* A 23-byte buffer should be sufficient */
strcpy(url, "http://example.net");
CU_ASSERT(!guac_kubernetes_append_endpoint_param(url, 23, "a", "1"));
}

View File

@ -0,0 +1,72 @@
/*
* 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 "url.h"
#include <CUnit/CUnit.h>
#include <stdlib.h>
/**
* Verifies that guac_kubernetes_escape_url_component() correctly escapes
* characters that would otherwise have special meaning within URLs.
*/
void test_url__escape_special() {
char value[256];
CU_ASSERT(!guac_kubernetes_escape_url_component(value, sizeof(value), "?foo%20bar\\1/2&3=4"));
CU_ASSERT_STRING_EQUAL(value, "%3Ffoo%2520bar%5C1%2F2%263%3D4");
}
/**
* Verifies that guac_kubernetes_escape_url_component() leaves strings
* untouched if they contain no characters requiring escaping.
*/
void test_url__escape_nospecial() {
char value[256];
CU_ASSERT(!guac_kubernetes_escape_url_component(value, sizeof(value), "potato"));
CU_ASSERT_STRING_EQUAL(value, "potato");
}
/**
* Verifies that guac_kubernetes_escape_url_component() refuses to overflow the
* bounds of the provided buffer.
*/
void test_url__escape_bounds() {
char value[256];
/* Escaping "?potato" (or "potato?") should fail for all buffer sizes with
* 9 bytes or less, with a 9-byte buffer lacking space for the null
* terminator */
for (int length = 0; length <= 9; length++) {
printf("Testing buffer with length %i ...\n", length);
CU_ASSERT(guac_kubernetes_escape_url_component(value, length, "?potato"));
CU_ASSERT(guac_kubernetes_escape_url_component(value, length, "potato?"));
}
/* A 10-byte buffer should be sufficient */
CU_ASSERT(!guac_kubernetes_escape_url_component(value, 10, "?potato"));
}

View File

@ -89,15 +89,60 @@ int guac_kubernetes_escape_url_component(char* output, int length,
}
int guac_kubernetes_endpoint_attach(char* buffer, int length,
const char* kubernetes_namespace, const char* kubernetes_pod,
const char* kubernetes_container) {
int guac_kubernetes_append_endpoint_param(char* buffer, int length,
const char* param_name, const char* param_value) {
int written;
char escaped_param_value[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
/* Escape value */
if (guac_kubernetes_escape_url_component(escaped_param_value,
sizeof(escaped_param_value), param_value))
return 1;
char* str = buffer;
int str_len = 0;
int qmark = 0;
while (*str != '\0') {
/* Look for a question mark */
if (*str=='?') qmark = 1;
/* Compute the buffer string length */
str_len++;
/* Verify the buffer null terminated */
if (str_len >= length) return 1;
/* Next character */
str++;
}
/* Determine the parameter delimiter */
char delimiter = '?';
if (qmark) delimiter = '&';
/* Advance to end of buffer, where the new parameter and delimiter need to
* be appended */
buffer += str_len;
length -= str_len;
/* Write the parameter and delimiter to the buffer */
int written = snprintf(buffer, length, "%c%s=%s", delimiter,
param_name, escaped_param_value);
/* The parameter was successfully added if it was written to the given
* buffer without truncation */
return (written < 0 || written >= length);
}
int guac_kubernetes_endpoint_uri(char* buffer, int length,
const char* kubernetes_namespace, const char* kubernetes_pod,
const char* kubernetes_container, const char* exec_command) {
char escaped_namespace[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
char escaped_pod[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
char escaped_container[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
/* Escape Kubernetes namespace */
if (guac_kubernetes_escape_url_component(escaped_namespace,
@ -109,29 +154,38 @@ int guac_kubernetes_endpoint_attach(char* buffer, int length,
sizeof(escaped_pod), kubernetes_pod))
return 1;
/* Generate attachment endpoint URL */
if (kubernetes_container != NULL) {
/* Determine the call type */
char* call = "attach";
if (exec_command != NULL)
call = "exec";
/* Escape container name */
if (guac_kubernetes_escape_url_component(escaped_container,
sizeof(escaped_container), kubernetes_container))
return 1;
int written;
written = snprintf(buffer, length,
"/api/v1/namespaces/%s/pods/%s/attach"
"?container=%s&stdin=true&stdout=true&tty=true",
escaped_namespace, escaped_pod, escaped_container);
}
else {
written = snprintf(buffer, length,
"/api/v1/namespaces/%s/pods/%s/attach"
"?stdin=true&stdout=true&tty=true",
escaped_namespace, escaped_pod);
}
/* Generate the endpoint path and write to the buffer */
written = snprintf(buffer, length,
"/api/v1/namespaces/%s/pods/%s/%s", escaped_namespace, escaped_pod, call);
/* Endpoint URL was successfully generated if it was written to the given
/* Operation successful if the endpoint path was written to the given
* buffer without truncation */
return !(written < length - 1);
if (written < 0 || written >= length)
return 1;
/* Append exec command parameter */
if (exec_command != NULL) {
if (guac_kubernetes_append_endpoint_param(buffer,
length, "command", exec_command))
return 1;
}
/* Append kubernetes container parameter */
if (kubernetes_container != NULL) {
if (guac_kubernetes_append_endpoint_param(buffer,
length, "container", kubernetes_container))
return 1;
}
/* Append stdin, stdout and tty parameters */
return (guac_kubernetes_append_endpoint_param(buffer, length, "stdin", "true"))
|| (guac_kubernetes_append_endpoint_param(buffer, length, "stdout", "true"))
|| (guac_kubernetes_append_endpoint_param(buffer, length, "tty", "true"));
}

View File

@ -49,6 +49,35 @@
int guac_kubernetes_escape_url_component(char* output, int length,
const char* str);
/**
* Appends the given query parameter and value to the given buffer. If the
* buffer does not already contain the '?' character denoting the start of the
* query string, it will be added. If the buffer already contains a query
* string, a '&' character will be added before the new parameter. The
* parameter value will automatically be URL-escaped as necessary.
*
* @param buffer
* The buffer which should receive the parameter. It could contain the endpoint path.
* The parameter will be written to the end of the buffer.
*
* @param length
* The number of bytes available in the given buffer.
*
* @param param_name
* The name of the parameter. If the parameter name contains characters
* with special meaning to URLs, it must already be URL-escaped.
*
* @param param_value
* The value of the parameter.
*
* @return
* Zero if the parameter was successfully attached to the buffer,
* non-zero if insufficient space exists within the buffer or
* buffer not null terminated.
*/
int guac_kubernetes_append_endpoint_param(char* buffer, int length,
const char* param_name, const char* param_value);
/**
* Generates the full path to the Kubernetes API endpoint which handles
* attaching to running containers within specific pods. Values within the path
@ -72,14 +101,18 @@ int guac_kubernetes_escape_url_component(char* output, int length,
* @param kubernetes_container
* The name of the container to attach to, or NULL to arbitrarily attach
* to the first container in the pod.
*
* @param exec_command
* The command used to run a new process and attach to it,
* instead of the main container process.
*
* @return
* Zero if the endpoint path was successfully written to the provided
* buffer, non-zero if insufficient space exists within the buffer.
*/
int guac_kubernetes_endpoint_attach(char* buffer, int length,
int guac_kubernetes_endpoint_uri(char* buffer, int length,
const char* kubernetes_namespace, const char* kubernetes_pod,
const char* kubernetes_container);
const char* kubernetes_container, const char* exec_command);
#endif

Some files were not shown because too many files have changed in this diff Show More