Compare commits
941 Commits
timezone-p
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
393560dedf | ||
|
7db06cc359 | ||
|
d0db9603fc | ||
|
bc5b9e3ccb | ||
|
fe998468b5 | ||
|
950aa7e3af | ||
|
d24cedbcdc | ||
|
1921c320d2 | ||
|
ad242072d9 | ||
|
a4a406305f | ||
|
094d043c6a | ||
|
dabd3af552 | ||
|
a7529b5e87 | ||
|
498b16b4ee | ||
|
e1df042a3c | ||
|
0bb38d0acf | ||
|
c84bb6339b | ||
|
4cb3df46e7 | ||
|
6f3e9d3dfc | ||
|
cf993d56a2 | ||
|
ebf0298e9a | ||
|
9b3f5cd86e | ||
|
af7849b579 | ||
|
8883ccc503 | ||
|
541fc3a8c6 | ||
|
3c8cd7fb7e | ||
|
16789a181a | ||
|
780f6dddce | ||
|
0c57fdc300 | ||
|
0b8de6f4b5 | ||
|
16657adf60 | ||
|
43e471938b | ||
|
51b2b76cbd | ||
|
387147bb4e | ||
|
9e15af8850 | ||
|
db07add07a | ||
|
7797d8d669 | ||
|
08839a11de | ||
|
7f27e168b3 | ||
|
48a387938d | ||
|
08d21e9f09 | ||
|
148549165d | ||
|
24899fa414 | ||
|
0c35fd1a48 | ||
|
cdeb29dbd1 | ||
|
a0d6a8d801 | ||
|
75ff6db4cd | ||
|
78acedb40e | ||
|
f88eda3f11 | ||
|
c0ed8ed6e0 | ||
|
0a7c845eec | ||
|
cb73d77d6e | ||
|
d2ddbd81f6 | ||
|
bc70bbd30e | ||
|
c5711f0a2e | ||
|
c43242fdde | ||
|
ebcf4c44a9 | ||
|
1520d94d33 | ||
|
c7486fc47b | ||
|
4726d5a01e | ||
|
d070857c03 | ||
|
3a1251fa0e | ||
|
7e7ea3e688 | ||
|
3f824daca4 | ||
|
315ab17984 | ||
|
93c4843399 | ||
|
d5149d7b37 | ||
|
cbc5d6d48b | ||
|
fbca659cb5 | ||
|
c9421d3714 | ||
|
aa371dcdd6 | ||
|
c4412a92e2 | ||
|
ec9b5c33bc | ||
|
9089150e28 | ||
|
c2cb03efd8 | ||
|
617b4d966a | ||
|
d9f551b7ca | ||
|
b57a098bf9 | ||
|
7d8711cd72 | ||
|
ce604a5a81 | ||
|
7d5193d3d5 | ||
|
fb3a127d00 | ||
|
ecc84158b7 | ||
|
edf562126e | ||
|
f1877d15d4 | ||
|
070115e77b | ||
|
1b177eeda9 | ||
|
d06a6d78cb | ||
|
a58a390726 | ||
|
7f44ef8114 | ||
|
4ec28e1f82 | ||
|
b6e051f16c | ||
|
dcf5a8e28c | ||
|
63121583eb | ||
|
6b6ae5fa9d | ||
|
2bdb059644 | ||
|
a5b64effab | ||
|
8bb5d598fc | ||
|
d232037e18 | ||
|
dc76879e0b | ||
|
d11be9025d | ||
|
8e33854c62 | ||
|
555d46f7e2 | ||
|
2a41730f70 | ||
|
3ae0a10142 | ||
|
2b0b52be9e | ||
|
1eac8fa15e | ||
|
9db7deb30b | ||
|
a7638bb0e4 | ||
|
24ab4b297f | ||
|
566d4cf637 | ||
|
10e7acdd49 | ||
|
8aa49c3e93 | ||
|
2b95757a7a | ||
|
8ee84ff054 | ||
|
3c9a45e9d0 | ||
|
6548f1f9dd | ||
|
aa954d9be9 | ||
|
4078b0d5b9 | ||
|
e9b942f612 | ||
|
f0d0105d50 | ||
|
fa71cb18f0 | ||
|
882060031f | ||
|
98335b7dde | ||
|
c9a946f272 | ||
|
56786ea606 | ||
|
c513886914 | ||
|
259b4f40c8 | ||
|
a60ac2321d | ||
|
42178b6191 | ||
|
fd1c850c62 | ||
|
7adb9517fc | ||
|
a0cb7ce166 | ||
|
1ea08b776f | ||
|
940d067f52 | ||
|
d2ec568d39 | ||
|
91a5f06ba3 | ||
|
e560733f8e | ||
|
d19b7fa5d0 | ||
|
11570395bc | ||
|
0a6679b793 | ||
|
75c2e9f6a5 | ||
|
e7414ef635 | ||
|
9a21b81422 | ||
|
c377b0ce86 | ||
|
e4e6e254a2 | ||
|
0a02afc225 | ||
|
88d9b1f110 | ||
|
5f86284663 | ||
|
738cbd54b2 | ||
|
04a5bbed4a | ||
|
941c36f4f6 | ||
|
8c9f83a12f | ||
|
edde0f542e | ||
|
a27d9508e2 | ||
|
a3aa01214c | ||
|
5538681fc3 | ||
|
392a085728 | ||
|
7ab30a1b18 | ||
|
14c4cd8dc3 | ||
|
401f81683c | ||
|
1675d926d2 | ||
|
de055a6765 | ||
|
a5208f948d | ||
|
527db6783b | ||
|
d511316e86 | ||
|
180a0e6c9e | ||
|
aa9b03df25 | ||
|
98f4a2b022 | ||
|
e48fb1eabd | ||
|
0d7edfd884 | ||
|
f488d15b7a | ||
|
d3f8cd3073 | ||
|
0cb84a0d8f | ||
|
0545a8a5ef | ||
|
9b32cc59b8 | ||
|
74b596758c | ||
|
a84b303c23 | ||
|
ae5f655f12 | ||
|
9caf38bbb6 | ||
|
6c7f88aecc | ||
|
2329cb23ae | ||
|
4b850f765f | ||
|
575b926626 | ||
|
563c05c499 | ||
|
210249e5c3 | ||
|
e139ff8c86 | ||
|
f4270aeab8 | ||
|
b5f8faa7ae | ||
|
d8dc2956e5 | ||
|
5ad826a503 | ||
|
6ac2a961b6 | ||
|
28037f100c | ||
|
9e18bd94f9 | ||
|
4f4d400f7e | ||
|
efffcee119 | ||
|
b6e0a2381c | ||
|
b9aa91bfcb | ||
|
6929ade1c4 | ||
|
e41f8eb61f | ||
|
c0a3abfc62 | ||
|
6e3cc23ad2 | ||
|
4cf5b32733 | ||
|
0ace936943 | ||
|
fd8947ddc1 | ||
|
b4dab0f419 | ||
|
f1725b2d99 | ||
|
79b0926b60 | ||
|
4ae5b6c254 | ||
|
bd0a3055b3 | ||
|
9368030b3c | ||
|
b253e6ba0a | ||
|
ab31eb7a50 | ||
|
1c061423bc | ||
|
57969939e3 | ||
|
02104bc2c3 | ||
|
8b3f807a0a | ||
|
782f349bd7 | ||
|
24954bf6ed | ||
|
fb1ce1eb53 | ||
|
00fdc97861 | ||
|
f346b94835 | ||
|
5f3be98c61 | ||
|
7bca994dd2 | ||
|
d870035509 | ||
|
122aa76ef1 | ||
|
96730a0be5 | ||
|
5353bff104 | ||
|
d19c0bde21 | ||
|
deb987e491 | ||
|
21668dc941 | ||
|
32169be68e | ||
|
0b65bf5ba8 | ||
|
e3430eeff5 | ||
|
0f8a6392d1 | ||
|
acf80baeb7 | ||
|
52d32d812c | ||
|
005d70c9e4 | ||
|
2fd3ace997 | ||
|
387f0efd24 | ||
|
b5d4339d86 | ||
|
6e8c867ec5 | ||
|
12b2cb9d2f | ||
|
2e73ead996 | ||
|
60609251fb | ||
|
7d5876bdb2 | ||
|
e6abf49a80 | ||
|
026b5218ff | ||
|
f0c1cbe20f | ||
|
dceb15d14e | ||
|
8f069b2be4 | ||
|
e6cf35c518 | ||
|
15d408d6fc | ||
|
62e556f4d0 | ||
|
bd28c2d4bf | ||
|
1cdc864c7d | ||
|
343900d227 | ||
|
dcc288c3d1 | ||
|
1432c65592 | ||
|
7ea8774728 | ||
|
d53e8c2574 | ||
|
d56fac7582 | ||
|
4988ba1188 | ||
|
a01b97e991 | ||
|
91da1e7473 | ||
|
969e212570 | ||
|
c7ce6ebe67 | ||
|
bbc27d53db | ||
|
c73dc72b47 | ||
|
2cbb5685b4 | ||
|
f0922350ee | ||
|
88ce44ed2e | ||
|
6ebef063b5 | ||
|
a12a2dc6af | ||
|
dc8d8553e8 | ||
|
b4dbecd506 | ||
|
7e28e0890c | ||
|
3ddc0e7c73 | ||
|
f5171b6b47 | ||
|
62e8270db1 | ||
|
0fa9e73de6 | ||
|
3d018a827f | ||
|
c3a5387187 | ||
|
8fcc59ed27 | ||
|
b9adc1dd24 | ||
|
f72db3f9f7 | ||
|
815d6a9fdd | ||
|
9531b7b270 | ||
|
b73befd677 | ||
|
8abf9cbe7c | ||
|
2d10effd3c | ||
|
4b52062658 | ||
|
008de893fe | ||
|
fdd02c945a | ||
|
25c281818a | ||
|
4e65fdb9eb | ||
|
3eca097b34 | ||
|
4c034ac36f | ||
|
de5e837c76 | ||
|
1fe0a6f40e | ||
|
1952cd0e66 | ||
|
99b5d2298b | ||
|
0c0a5f4234 | ||
|
d05c781cd9 | ||
|
4cd680c9fc | ||
|
7e8c374fe2 | ||
|
326fb337ef | ||
|
35df9e95ad | ||
|
4797609b80 | ||
|
48eb68b53b | ||
|
8ea903091b | ||
|
cf46f341f6 | ||
|
f4cac29f3c | ||
|
140fe9c50f | ||
|
f00d9c45e0 | ||
|
503b9f0bb7 | ||
|
4fc2e1bb3e | ||
|
4b5274c34f | ||
|
04bd097455 | ||
|
544c6ddce6 | ||
|
53497a572d | ||
|
57ed259be2 | ||
|
b19dcf8d68 | ||
|
eceab9aedb | ||
|
704597937d | ||
|
efeac25fa9 | ||
|
cdb25ea11b | ||
|
08afc250a3 | ||
|
40ba7aa03a | ||
|
429474cd0f | ||
|
5e6d0a4183 | ||
|
eea56dcf60 | ||
|
69b9f81c29 | ||
|
afc4f9b92a | ||
|
142d112eee | ||
|
83921b0f67 | ||
|
42d66eaebe | ||
|
f9bfaba0d2 | ||
|
e878927cd7 | ||
|
2e23ddfc1c | ||
|
fe8e1950aa | ||
|
6c8c67b385 | ||
|
e14b7a1f29 | ||
|
830f208ec4 | ||
|
ac1a922774 | ||
|
31570e31e1 | ||
|
46c1ae145c | ||
|
a54a602dcc | ||
|
315f793078 | ||
|
2c7ca7f4b2 | ||
|
911ed13efc | ||
|
15141385f6 | ||
|
7037a8a846 | ||
|
6c25607c76 | ||
|
3a2a65bbed | ||
|
997ff57301 | ||
|
f55a1e2bc3 | ||
|
10bb42e4d8 | ||
|
b5dbe5a167 | ||
|
e13273d94d | ||
|
4783546582 | ||
|
8a042b33d6 | ||
|
4a0f996d42 | ||
|
57f69ad77d | ||
|
22d1810e12 | ||
|
a0dbb901df | ||
|
cb2cbdabfe | ||
|
a5b80925e2 | ||
|
952076afb7 | ||
|
67817886ab | ||
|
966ddd13df | ||
|
4c5fba4526 | ||
|
cfc37caeb8 | ||
|
c86a78bef7 | ||
|
0146deb4c0 | ||
|
dcdb0bb3b2 | ||
|
1f7ec7324d | ||
|
934cf668a2 | ||
|
276e3d67e0 | ||
|
ac0acd0463 | ||
|
d06d2e0e29 | ||
|
0df01bcf8d | ||
|
fef9b1a012 | ||
|
032b34702d | ||
|
5e1aa072f1 | ||
|
a57263c209 | ||
|
04a8bcd8a5 | ||
|
582ff2c319 | ||
|
f6d71a592e | ||
|
fe8555a4fd | ||
|
f6eb6ad4d7 | ||
|
e95707f3c7 | ||
|
ee81bb017b | ||
|
72350e2587 | ||
|
9c33717522 | ||
|
5f9ef68b07 | ||
|
8d64d75aa2 | ||
|
574742bae8 | ||
|
ae61278d18 | ||
|
a1a8846ad2 | ||
|
8a0194c105 | ||
|
7fdb3a68bd | ||
|
9eeeef6976 | ||
|
fa21806734 | ||
|
1c12a15de1 | ||
|
485ebe03bf | ||
|
95bafbf8d4 | ||
|
e2eb8f6a28 | ||
|
7cd4884de1 | ||
|
1145eb0fc9 | ||
|
cf7fd2d340 | ||
|
9a82045bf2 | ||
|
1cd0d0ab6e | ||
|
a23d45a419 | ||
|
953aa6c436 | ||
|
94c3253cee | ||
|
b44527387d | ||
|
3fe99c87a1 | ||
|
490ca0453b | ||
|
11b7633dc0 | ||
|
55177e0e46 | ||
|
8ddb678639 | ||
|
a85068d42e | ||
|
47c5070805 | ||
|
508238711c | ||
|
fd099d38c7 | ||
|
ba47e1936f | ||
|
a820912a24 | ||
|
f95f873c58 | ||
|
aa47e58f11 | ||
|
ce30f80d2c | ||
|
53942081a1 | ||
|
98e11c69c1 | ||
|
c319541f0d | ||
|
d71c9c566a | ||
|
fffabcaab1 | ||
|
fe590801a1 | ||
|
b891228279 | ||
|
f3055f9644 | ||
|
2973ff7004 | ||
|
dd98b20f4a | ||
|
55a5fe26fb | ||
|
9d478c2ce7 | ||
|
1947f28a70 | ||
|
3825fe8135 | ||
|
f529345d84 | ||
|
51b179b9c7 | ||
|
6e6f50c088 | ||
|
c470b23795 | ||
|
9ec3d9dc0b | ||
|
1bc188158b | ||
|
cf809e6388 | ||
|
3e7a7fdcad | ||
|
9f454bea85 | ||
|
f400b9a602 | ||
|
ab88e79a36 | ||
|
2fe0435e79 | ||
|
5e13d5bf16 | ||
|
8cf0dd4359 | ||
|
3e09d4cfc1 | ||
|
776b0e68b5 | ||
|
64bf1c9a97 | ||
|
66b6a90cc7 | ||
|
9fc7e5bfd9 | ||
|
cfc56e936a | ||
|
609d04deab | ||
|
5e998bb078 | ||
|
ff31004b98 | ||
|
2e1ea335f3 | ||
|
f414bef5cb | ||
|
0d8764263f | ||
|
b1cc0a3191 | ||
|
01d565398e | ||
|
469f75a314 | ||
|
89e6f357b5 | ||
|
1f3e6decf2 | ||
|
3d210d815d | ||
|
0fe89241f8 | ||
|
26003e59cc | ||
|
fcdbc05979 | ||
|
82a43ef4dc | ||
|
193e985063 | ||
|
7ccc794176 | ||
|
c766c13103 | ||
|
57c703cf67 | ||
|
5380a743b5 | ||
|
4b60f0651c | ||
|
ae3330d63d | ||
|
ce2605fad2 | ||
|
b350b0039e | ||
|
69163f91ee | ||
|
7fc4f528c0 | ||
|
239440e28d | ||
|
ed942f8c20 | ||
|
5bcd737548 | ||
|
d3a7629734 | ||
|
516cf54c75 | ||
|
6660119422 | ||
|
69527e117a | ||
|
d0c1e6055f | ||
|
0a24f12281 | ||
|
fd3c7633d6 | ||
|
c8e6b1b71f | ||
|
2b1abcd550 | ||
|
f67e641cce | ||
|
e79aaf4d82 | ||
|
42e3c6fff8 | ||
|
f86f70a022 | ||
|
7a607dbc53 | ||
|
9e416ed002 | ||
|
699fc70941 | ||
|
80623095a0 | ||
|
107a8d9f48 | ||
|
02b98e9287 | ||
|
c509aabc08 | ||
|
96052ca2fb | ||
|
ffd40983ac | ||
|
e2d1938886 | ||
|
35ad7e7d0d | ||
|
33fcc04263 | ||
|
56535c9663 | ||
|
8cd12a87fd | ||
|
183be60bac | ||
|
632e2180f7 | ||
|
46457a5033 | ||
|
c5e8326d07 | ||
|
84719a4d83 | ||
|
564656166e | ||
|
8d46de743e | ||
|
7ae1f07aa8 | ||
|
afde8e779b | ||
|
37271d01fa | ||
|
98f2f69f60 | ||
|
33a7bee4f9 | ||
|
c7c84682e1 | ||
|
b4755a58dc | ||
|
c16e5ffcc2 | ||
|
fbbda2259d | ||
|
fdd8a5cedd | ||
|
3905ef0b01 | ||
|
8ef4b2aefa | ||
|
b7601ec755 | ||
|
ed2c5e9a5b | ||
|
769b225278 | ||
|
bed6e7968e | ||
|
65f6541f67 | ||
|
7b8ddb89c7 | ||
|
d55dfa316f | ||
|
58ea63fd84 | ||
|
deec4df8c0 | ||
|
e6b4bb0e76 | ||
|
e2fbab750b | ||
|
5f37b54847 | ||
|
b738cf01b9 | ||
|
b62249402a | ||
|
347ac5a941 | ||
|
4d8a3a22e2 | ||
|
586f40631f | ||
|
6fb3120c0b | ||
|
accb4eae75 | ||
|
9278a0adb8 | ||
|
0e97a51181 | ||
|
618e02a65e | ||
|
7f52be7d75 | ||
|
b388739a0c | ||
|
328b7433a4 | ||
|
84c7fbe922 | ||
|
b66bc796f4 | ||
|
d3f284505d | ||
|
5281b7a320 | ||
|
688f450fc2 | ||
|
6c05b54e4f | ||
|
cd70c73e39 | ||
|
a3b658f677 | ||
|
c7efbf5b8e | ||
|
ca260e13bb | ||
|
6c88d89566 | ||
|
8cc9e5f4e9 | ||
|
8e1a2e3645 | ||
|
f2e90eca34 | ||
|
073f1ee642 | ||
|
befa4233ad | ||
|
a19305569d | ||
|
bb242b9a89 | ||
|
965d231e49 | ||
|
cd18dea174 | ||
|
f655843260 | ||
|
c0d06c782b | ||
|
07ea37d1d7 | ||
|
4c8f6383ae | ||
|
4d2197eab0 | ||
|
94020c183c | ||
|
7c8c14294f | ||
|
7d67b8e204 | ||
|
80e0c68dca | ||
|
0d5bc92a2b | ||
|
869bfd5e86 | ||
|
039e24bf48 | ||
|
0de75b305c | ||
|
1bd6d70057 | ||
|
a4a7019f97 | ||
|
f7726ed7b6 | ||
|
ba41d5e532 | ||
|
687f946999 | ||
|
f1be630637 | ||
|
8a3f2be769 | ||
|
ca1791cfe8 | ||
|
8b6091a007 | ||
|
a1cef8dd85 | ||
|
65de25205b | ||
|
9cfd748b63 | ||
|
3460f64f57 | ||
|
2bcf2c50eb | ||
|
6327b77461 | ||
|
7295f8a3b8 | ||
|
0b9d35235b | ||
|
dbe92795e3 | ||
|
d5b41bb8a0 | ||
|
01c6447963 | ||
|
5a711f5ae4 | ||
|
d7f6cf90b4 | ||
|
5f2ed9b8ce | ||
|
f0a73e3e9c | ||
|
1c161b0270 | ||
|
70b597ce1e | ||
|
71080e61b0 | ||
|
3895cf9fd3 | ||
|
16cec716e0 | ||
|
f1cddd78f9 | ||
|
d2641ea55f | ||
|
7ef9345743 | ||
|
166441c4ee | ||
|
163aec7e2b | ||
|
afff514ca7 | ||
|
fb5934007a | ||
|
98f22d49b9 | ||
|
aa72746cda | ||
|
c84655cd62 | ||
|
a1a1fc8658 | ||
|
7ebefc505f | ||
|
b29c141c39 | ||
|
f74fd1ab0f | ||
|
f62325d443 | ||
|
7aa80cb751 | ||
|
2638d9d894 | ||
|
adc4f2abf8 | ||
|
29697ca68d | ||
|
773eed8878 | ||
|
6481993305 | ||
|
f9141fb94b | ||
|
e00661d338 | ||
|
c9b0c9ecd5 | ||
|
898cb5782c | ||
|
fa9fff6ed0 | ||
|
b2d58de1da | ||
|
6f756fd1be | ||
|
cc6850bf21 | ||
|
8a990644c0 | ||
|
2ae0c456b4 | ||
|
0f00e37884 | ||
|
66153045be | ||
|
384cd284d8 | ||
|
0de5430578 | ||
|
2175428df5 | ||
|
3682e11e55 | ||
|
993756e575 | ||
|
1f917ca46f | ||
|
265246cdfc | ||
|
06097575a4 | ||
|
ae95b66922 | ||
|
31ef07ead7 | ||
|
8ba7b65725 | ||
|
11671bcd5b | ||
|
a1be46e5dd | ||
|
eb70b1756b | ||
|
eec69bde0f | ||
|
62e52b30c8 | ||
|
861f699d92 | ||
|
953b2664c0 | ||
|
bf4953d288 | ||
|
403ee7ad4c | ||
|
e864fed61f | ||
|
9fed64eb0d | ||
|
c427d0cfb2 | ||
|
a4bd5ba886 | ||
|
9c52238d24 | ||
|
17c209c0f9 | ||
|
b66486ed23 | ||
|
398efacb10 | ||
|
9d10923524 | ||
|
8793d5a843 | ||
|
21d0088b95 | ||
|
b7553d5f41 | ||
|
edf6ab89f7 | ||
|
674e84cbf3 | ||
|
c1918ac189 | ||
|
529ce7534b | ||
|
189abc73f3 | ||
|
3ab6d2f918 | ||
|
a085b0d228 | ||
|
ba7465abf5 | ||
|
64438a4028 | ||
|
fcdd03a020 | ||
|
2a641aa834 | ||
|
215f798665 | ||
|
d93d9765b3 | ||
|
f891bd3646 | ||
|
801c825e2b | ||
|
86bfade4dc | ||
|
4253426e48 | ||
|
cec6fef1de | ||
|
3fa3772038 | ||
|
306af4070c | ||
|
a92a13d74c | ||
|
52ee2e4668 | ||
|
0bbf0b6e80 | ||
|
929e4bdf8d | ||
|
e23115c54f | ||
|
9738fc40f3 | ||
|
d1490e3c12 | ||
|
073426d4c1 | ||
|
02d9d56536 | ||
|
6a64fe121b | ||
|
b6f094da4e | ||
|
db8e340604 | ||
|
fbd9aae501 | ||
|
97a9f3238f | ||
|
082c46ba64 | ||
|
2170cc65e3 | ||
|
1e1e9a445b | ||
|
b26ab2f069 | ||
|
590fbcf005 | ||
|
749023bacb | ||
|
ae05778644 | ||
|
3a9a820092 | ||
|
d0cfd3b502 | ||
|
ec26c8c168 | ||
|
7d48aee829 | ||
|
8fd724b7b6 | ||
|
c670eda6eb | ||
|
b9202bd0b1 | ||
|
e3f94e6622 | ||
|
64a0d87c98 | ||
|
798421e883 | ||
|
9ae37b094f | ||
|
8b92e17e60 | ||
|
b980bf2e14 | ||
|
6a1c3b4545 | ||
|
889ae65a1f | ||
|
245afb706c | ||
|
1a9766e190 | ||
|
9776cc109e | ||
|
a82821b756 | ||
|
d959121b2f | ||
|
9c6126c470 | ||
|
e37dffaca4 | ||
|
0eb0cff367 | ||
|
5aff241096 | ||
|
9df86daf28 | ||
|
5e123735fa | ||
|
3eb905cfb1 | ||
|
b6e72c7674 | ||
|
2fc791fe9b | ||
|
3e933203e2 | ||
|
ff62730419 | ||
|
c851dfcb7f | ||
|
ebbfe598dc | ||
|
c5f346488f | ||
|
16d6c3b795 | ||
|
9a027833b9 | ||
|
fe6d861a5c | ||
|
491c884c7a | ||
|
b77d6e8550 | ||
|
f3d84d4557 | ||
|
baa70d1ab6 | ||
|
cbeded839d | ||
|
a89aefa8d4 | ||
|
734d4ce43e | ||
|
ca5a8e0625 | ||
|
42b078a59d | ||
|
5a4a1a40ad | ||
|
f7fd817d1c | ||
|
72ae7fb54f | ||
|
852e0c1450 | ||
|
cbae4155fb | ||
|
96bdd58226 | ||
|
fe4cb6de65 | ||
|
7c537d681f | ||
|
654e107ade | ||
|
4b62174c4f | ||
|
11a9c010f9 | ||
|
5791b2a5aa | ||
|
6d8d4ddb1a | ||
|
6fb7bfd043 | ||
|
e756c90569 | ||
|
1dea19ac5f | ||
|
5887f8a558 | ||
|
36fbed25c9 | ||
|
3674c5c2f7 | ||
|
7c1fd71b7b | ||
|
48aaa53a84 | ||
|
7d3b2aebfd | ||
|
190929c018 | ||
|
d02a30e377 | ||
|
f60e386f3e | ||
|
e1d0efa817 | ||
|
ab9ae66a8d | ||
|
f22018302f | ||
|
605d390dba | ||
|
ec73265268 | ||
|
b53146150c | ||
|
ba9897f8b7 | ||
|
bd637c6cd1 | ||
|
f3596b950a | ||
|
d277e494c6 | ||
|
a18287694e | ||
|
500495dc00 | ||
|
cc626276d0 | ||
|
ceae258e37 | ||
|
47e40dd7a5 | ||
|
36c3184a0f | ||
|
92e416610a | ||
|
457d6f7578 | ||
|
24cd7828c9 | ||
|
6a3564407e | ||
|
c44f89bc68 | ||
|
b91c768867 | ||
|
9c9d74e920 | ||
|
91cb77a85e | ||
|
1b352bf09a | ||
|
af813a0929 | ||
|
6a5d6d0514 | ||
|
15456e1ee1 | ||
|
4439240fd3 | ||
|
5443a934a9 | ||
|
1d33095500 | ||
|
622fcc7025 | ||
|
1e24fcd9a4 | ||
|
7503efed27 | ||
|
3b8dc5c183 | ||
|
efde29f362 | ||
|
d0addb52bd | ||
|
eeba99d73e | ||
|
380c5808a0 | ||
|
4ccfbc96ac | ||
|
de5b7c8e42 | ||
|
743c923ca7 | ||
|
113810e45b | ||
|
03449da9e9 | ||
|
3c6f0491bf | ||
|
3b1c1e0af1 | ||
|
8e4f9ad5ac | ||
|
396b3b51b4 | ||
|
4a9018ab14 | ||
|
d71af44bb1 | ||
|
6d994feef6 | ||
|
b0afe43d34 | ||
|
3f0000df0f | ||
|
d6b7e90d69 | ||
|
7aae7896ae | ||
|
f928f9465b | ||
|
f1b299c3e2 | ||
|
598746a26b | ||
|
7f13d1993d | ||
|
00eb86970d | ||
|
7054b26916 | ||
|
387dabc4dd | ||
|
1ec18ce827 | ||
|
050eef537a | ||
|
d08e44d883 | ||
|
f855fdc66a | ||
|
eb7d8fdeb0 | ||
|
0b21fddef6 | ||
|
df04e4c888 | ||
|
d81dd9d9ff | ||
|
221dd7ce1d | ||
|
1ca069c771 | ||
|
348ddf61c0 | ||
|
9f911bea62 | ||
|
2a7572e6b3 | ||
|
652c5310f2 | ||
|
56d660f258 | ||
|
36686ba348 | ||
|
679de71056 | ||
|
86e2789d9b | ||
|
2812775c96 | ||
|
95b88baa8f | ||
|
49dc431045 | ||
|
fc6ba9f8f9 | ||
|
ba7fd06ec4 | ||
|
a3d2c33f96 | ||
|
3f5f53d3dc | ||
|
fb57a96eba | ||
|
c7b519472b | ||
|
3fb7bd92cc | ||
|
73b7401bbb | ||
|
8f55f33951 | ||
|
502640caca | ||
|
20986e927f | ||
|
17d44e847f | ||
|
fe76bafd57 | ||
|
9c413abee1 | ||
|
0fe5831187 | ||
|
79abff4329 | ||
|
37008506c2 | ||
|
7fe1164d26 | ||
|
1265498b1f | ||
|
4286a4c164 | ||
|
37f9d8a930 | ||
|
d5d3d8501e | ||
|
7ea8e63a39 | ||
|
39e8c077d4 | ||
|
e95a813560 | ||
|
c2d30a07e9 | ||
|
dcbeedb4d4 | ||
|
ddde8f26e8 | ||
|
63d87dcf18 | ||
|
a863bc0bf4 | ||
|
03794e4679 | ||
|
82835026d3 | ||
|
4b7727bd4e | ||
|
7f84bd5c81 | ||
|
7dfdadbe53 | ||
|
bc4022eb3b | ||
|
b80e33ff70 | ||
|
9f18768483 | ||
|
1f95865907 | ||
|
8f5a20923c | ||
|
1e19ccd76d | ||
|
92b547e91e | ||
|
dac0ae4976 | ||
|
1f806f35db | ||
|
6e2cc358ba | ||
|
4523c4ec78 | ||
|
22fb6aad31 | ||
|
d4ecd16477 | ||
|
1dd9f32e76 | ||
|
14c85bafd3 | ||
|
83b253dc84 | ||
|
4e6f7fb989 | ||
|
b2e15b5f75 |
40
.clang-tidy
40
.clang-tidy
@ -21,31 +21,67 @@ Checks: >
|
||||
-altera-id-dependent-backward-branch,
|
||||
-altera-struct-pack-align,
|
||||
-altera-unroll-loops,
|
||||
-cppcoreguidelines-interfaces-global-init,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-assignment-in-if-condition,
|
||||
-bugprone-branch-clone,
|
||||
-bugprone-macro-parentheses,
|
||||
-cert-dcl16-c,
|
||||
-cert-env33-c,
|
||||
-cert-dcl50-cpp,
|
||||
-clang-analyzer-optin.performance.Padding,
|
||||
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
|
||||
-clang-analyzer-valist.Uninitialized,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-owning-memory,
|
||||
-cppcoreguidelines-avoid-c-arrays,
|
||||
-cppcoreguidelines-avoid-do-while,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-avoid-non-const-global-variables,
|
||||
-cppcoreguidelines-macro-to-enum,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-pro-type-vararg,
|
||||
-cppcoreguidelines-pro-type-reinterpret-cast,
|
||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
-cppcoreguidelines-no-malloc,
|
||||
-google-readability-braces-around-statements,
|
||||
-google-readability-todo,
|
||||
-hicpp-avoid-c-arrays,
|
||||
-hicpp-braces-around-statements,
|
||||
-hicpp-no-array-decay,
|
||||
-hicpp-no-assembler,
|
||||
-hicpp-multiway-paths-covered,
|
||||
-hicpp-signed-bitwise,
|
||||
-hicpp-uppercase-literal-suffix,
|
||||
-hicpp-vararg,
|
||||
-hicpp-no-malloc,
|
||||
-llvm-header-guard,
|
||||
-llvm-include-order,
|
||||
-llvm-qualified-auto,
|
||||
-llvm-else-after-return,
|
||||
-readability-else-after-return,
|
||||
-readability-avoid-nested-conditional-operator,
|
||||
-modernize-use-trailing-return-type,
|
||||
-modernize-return-braced-init-list,
|
||||
-modernize-macro-to-enum,
|
||||
-modernize-pass-by-value,
|
||||
-modernize-avoid-c-arrays,
|
||||
-readability-braces-around-statements,
|
||||
-readability-convert-member-functions-to-static,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-identifier-length,
|
||||
-readability-implicit-bool-conversion,
|
||||
-readability-magic-numbers,
|
||||
-readability-math-missing-parentheses,
|
||||
-readability-misleading-indentation,
|
||||
-readability-qualified-auto,
|
||||
-readability-suspicious-call-argument,
|
||||
-readability-string-compare,
|
||||
-readability-uppercase-literal-suffix,
|
||||
-performance-no-int-to-ptr
|
||||
-performance-no-int-to-ptr,
|
||||
-performance-avoid-endl
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: ''
|
||||
AnalyzeTemporaryDtors: false
|
||||
FormatStyle: file
|
||||
User: nin
|
||||
CheckOptions:
|
||||
|
15
.github/workflows/abi-checker.yml
vendored
15
.github/workflows/abi-checker.yml
vendored
@ -38,13 +38,13 @@ jobs:
|
||||
xserver-xorg-dev \
|
||||
libswscale-dev \
|
||||
libswresample-dev \
|
||||
libavformat-dev \
|
||||
libavutil-dev \
|
||||
libavcodec-dev \
|
||||
libcups2-dev \
|
||||
libpulse-dev \
|
||||
libasound2-dev \
|
||||
libpcsclite-dev \
|
||||
xsltproc \
|
||||
libxcb-cursor-dev \
|
||||
libxcursor-dev \
|
||||
libcairo2-dev \
|
||||
@ -65,7 +65,6 @@ jobs:
|
||||
libcairo2-dev \
|
||||
libsoxr-dev \
|
||||
libsdl2-dev \
|
||||
docbook-xsl \
|
||||
libkrb5-dev \
|
||||
libcjson-dev \
|
||||
libsdl2-ttf-dev \
|
||||
@ -84,11 +83,11 @@ jobs:
|
||||
|
||||
- name: "Prepare configuration"
|
||||
run: |
|
||||
mkdir -p checker
|
||||
cp ci/cmake-preloads/config-abi.txt checker/
|
||||
cp scripts/abi-suppr.txt checker/
|
||||
curl https://gist.githubusercontent.com/akallabeth/aa35caed0d39241fa17c3dc8a0539ea3/raw/ef12f8c720ac6be51aa1878710e2502b1b39cf4c/check-abi -o checker/check-abi
|
||||
chmod +x checker/check-abi
|
||||
mkdir -p abi-checker
|
||||
cp ci/cmake-preloads/config-abi.txt abi-checker/
|
||||
cp scripts/abi-suppr.txt abi-checker/
|
||||
curl https://gist.githubusercontent.com/akallabeth/aa35caed0d39241fa17c3dc8a0539ea3/raw/ef12f8c720ac6be51aa1878710e2502b1b39cf4c/check-abi -o abi-checker/check-abi
|
||||
chmod +x abi-checker/check-abi
|
||||
echo "GITHUB_BASE_REF=$GITHUB_BASE_REF"
|
||||
echo "GITHUB_HEAD_REF=$GITHUB_HEAD_REF"
|
||||
echo "API_BASE_REF=${{ inputs.API_BASE_REF || '3.0.0' }}"
|
||||
@ -100,4 +99,4 @@ jobs:
|
||||
BASE_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event_name == 'workflow_dispatch' && inputs.API_BASE_REF || '3.0.0' }}
|
||||
run: |
|
||||
echo "BASE_REF=$BASE_REF"
|
||||
./checker/check-abi -s checker/abi-suppr.txt --parameters="-Cchecker/config-abi.txt" $BASE_REF $(git rev-parse HEAD)
|
||||
./abi-checker/check-abi -s abi-checker/abi-suppr.txt --parameters="-Cabi-checker/config-abi.txt" $BASE_REF $(git rev-parse HEAD)
|
||||
|
5
.github/workflows/alt-architectures.yml
vendored
5
.github/workflows/alt-architectures.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
||||
distro: ubuntu22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: uraimo/run-on-arch-action@master
|
||||
- uses: uraimo/run-on-arch-action@v2.8.1
|
||||
name: "Run tests"
|
||||
id: build
|
||||
with:
|
||||
@ -52,8 +52,6 @@ jobs:
|
||||
libpulse-dev \
|
||||
libasound2-dev \
|
||||
libpcsclite-dev \
|
||||
xsltproc \
|
||||
libxml2-dev \
|
||||
libxcb-cursor-dev \
|
||||
libxcursor-dev \
|
||||
libcairo2-dev \
|
||||
@ -74,7 +72,6 @@ jobs:
|
||||
libsdl2-dev \
|
||||
libsdl2-ttf-dev \
|
||||
libsdl2-image-dev \
|
||||
docbook-xsl \
|
||||
libkrb5-dev \
|
||||
libcjson-dev \
|
||||
libpkcs11-helper1-dev \
|
||||
|
2
.github/workflows/clang-tidy.yml
vendored
2
.github/workflows/clang-tidy.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
split_workflow: true
|
||||
clang_tidy_checks: ''
|
||||
# List of packages to install
|
||||
apt_packages: libkrb5-dev,libxkbcommon-dev,libxkbfile-dev,libx11-dev,libwayland-dev,libxrandr-dev,libxi-dev,libxrender-dev,libxext-dev,libxinerama-dev,libxfixes-dev,libxcursor-dev,libxv-dev,libxdamage-dev,libxtst-dev,libcups2-dev,libcairo2-dev,libpcsclite-dev,libasound2-dev,libswscale-dev,libpulse-dev,libavcodec-dev,libavutil-dev,libfuse3-dev,libswresample-dev,libusb-1.0-0-dev,libudev-dev,libdbus-glib-1-dev,libpam0g-dev,uuid-dev,libxml2-dev,libcjson-dev,libsdl2-2.0-0,libsdl2-dev,libsdl2-ttf-dev,libsdl2-image-dev,libsystemd-dev,liburiparser-dev,libopus-dev,libwebp-dev,libjpeg-dev,libpng-dev,xsltproc,docbook-xsl,libgsm1-dev,libfaac-dev,libfaad-dev,libsoxr-dev,opencl-c-headers,opencl-headers,ocl-icd-opencl-dev,libssl-dev,libv4l-dev
|
||||
apt_packages: libkrb5-dev,libxkbcommon-dev,libxkbfile-dev,libx11-dev,libwayland-dev,libxrandr-dev,libxi-dev,libxrender-dev,libxext-dev,libxinerama-dev,libxfixes-dev,libxcursor-dev,libxv-dev,libxdamage-dev,libxtst-dev,libcups2-dev,libcairo2-dev,libpcsclite-dev,libasound2-dev,libswscale-dev,libpulse-dev,libavformat-dev,libavcodec-dev,libavutil-dev,libfuse3-dev,libswresample-dev,libusb-1.0-0-dev,libudev-dev,libdbus-glib-1-dev,libpam0g-dev,uuid-dev,libcjson-dev,libsdl2-2.0-0,libsdl2-dev,libsdl2-ttf-dev,libsdl2-image-dev,libsystemd-dev,liburiparser-dev,libopus-dev,libwebp-dev,libjpeg-dev,libpng-dev,libgsm1-dev,libfaac-dev,libfaad-dev,libsoxr-dev,opencl-c-headers,opencl-headers,ocl-icd-opencl-dev,libssl-dev,libv4l-dev
|
||||
|
||||
# CMake command to run in order to generate compile_commands.json
|
||||
build_dir: tidy
|
||||
|
3
.github/workflows/codeql-analysis.yml
vendored
3
.github/workflows/codeql-analysis.yml
vendored
@ -86,6 +86,7 @@ jobs:
|
||||
xserver-xorg-dev \
|
||||
libswscale-dev \
|
||||
libswresample-dev \
|
||||
libavformat-dev \
|
||||
libavutil-dev \
|
||||
libavcodec-dev \
|
||||
libcups2-dev \
|
||||
@ -93,7 +94,6 @@ jobs:
|
||||
libpulse-dev \
|
||||
libasound2-dev \
|
||||
libpcsclite-dev \
|
||||
xsltproc \
|
||||
libxcb-cursor-dev \
|
||||
libxcursor-dev \
|
||||
libcairo2-dev \
|
||||
@ -115,7 +115,6 @@ jobs:
|
||||
libcairo2-dev \
|
||||
libsoxr-dev \
|
||||
libsdl2-dev \
|
||||
docbook-xsl \
|
||||
libkrb5-dev \
|
||||
libcjson-dev \
|
||||
libsdl2-ttf-dev \
|
||||
|
59
.github/workflows/coverity.yml
vendored
59
.github/workflows/coverity.yml
vendored
@ -5,6 +5,7 @@ on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
branches: [ master, stable* ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@ -19,71 +20,33 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
libxrandr-dev \
|
||||
libxinerama-dev \
|
||||
libusb-1.0-0-dev \
|
||||
xserver-xorg-dev \
|
||||
libswscale-dev \
|
||||
libswresample-dev \
|
||||
libavutil-dev \
|
||||
libavcodec-dev \
|
||||
libcups2-dev \
|
||||
libpulse-dev \
|
||||
libasound2-dev \
|
||||
libpcsclite-dev \
|
||||
libv4l-dev \
|
||||
xsltproc \
|
||||
libxcb-cursor-dev \
|
||||
libxcursor-dev \
|
||||
libcairo2-dev \
|
||||
libfaac-dev \
|
||||
libfaad-dev \
|
||||
libjpeg-dev \
|
||||
libgsm1-dev \
|
||||
devscripts \
|
||||
ninja-build \
|
||||
libxfixes-dev \
|
||||
libxkbcommon-dev \
|
||||
libwayland-dev \
|
||||
libpam0g-dev \
|
||||
libxdamage-dev \
|
||||
libxcb-damage0-dev \
|
||||
equivs \
|
||||
ccache \
|
||||
libxtst-dev \
|
||||
libfuse3-dev \
|
||||
libsystemd-dev \
|
||||
libcairo2-dev \
|
||||
libsoxr-dev \
|
||||
libsdl2-dev \
|
||||
docbook-xsl \
|
||||
libkrb5-dev \
|
||||
libcjson-dev \
|
||||
libsdl2-ttf-dev \
|
||||
libsdl2-image-dev \
|
||||
libwebkit2gtk-4.0-dev \
|
||||
clang \
|
||||
libopus-dev \
|
||||
libwebp-dev \
|
||||
libpng-dev \
|
||||
libjpeg-dev \
|
||||
liburiparser-dev
|
||||
clang
|
||||
sudo mk-build-deps --install packaging/deb/freerdp-nightly/control
|
||||
|
||||
- name: Download Coverity build tool
|
||||
run: |
|
||||
wget -c -N https://scan.coverity.com/download/linux64 --post-data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=FreeRDP" -O coverity_tool.tar.gz
|
||||
mkdir coverity_tool
|
||||
tar xzf coverity_tool.tar.gz --strip 1 -C coverity_tool
|
||||
|
||||
- name: Build with Coverity build tool
|
||||
run: |
|
||||
export PATH=`pwd`/coverity_tool/bin:$PATH
|
||||
export CC=/usr/bin/clang
|
||||
export CXX=/usr/bin/clang++
|
||||
cov-configure --template --compiler clang --comptype clangcc
|
||||
# in source build is used to help coverity to determine relative file path
|
||||
cmake \
|
||||
-GNinja \
|
||||
-C ci/cmake-preloads/config-coverity.txt \
|
||||
-DALLOW_IN_SOURCE_BUILD=true \
|
||||
-B. \
|
||||
-DCOVERITY_BUILD=ON \
|
||||
-Bcov-build \
|
||||
-S.
|
||||
cov-build --dir cov-int cmake --build .
|
||||
cov-build --dir cov-int cmake --build cov-build
|
||||
|
||||
- name: Submit build result to Coverity Scan
|
||||
run: |
|
||||
|
2
.github/workflows/fuzzing.yml
vendored
2
.github/workflows/fuzzing.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
dry-run: false
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
- name: Upload crash
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4.3.6
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: ${{ matrix.sanitizer }}-artifacts
|
||||
|
29
.github/workflows/issue-autoclose.yml
vendored
Normal file
29
.github/workflows/issue-autoclose.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Close inactive issues
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
schedule:
|
||||
- cron: "33 3 * * *"
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
with:
|
||||
days-before-stale: 30
|
||||
days-before-close: 30
|
||||
operations-per-run: 90
|
||||
exempt-all-milestones: true
|
||||
exempt-assignees: true
|
||||
exempt-issue-labels: "wip,pinned,help-wanted,blocker,feature"
|
||||
exempt-pr-labels: "wip,pinned,help-wanted,blocker,feature"
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
|
||||
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
2
.github/workflows/timezone-update.yml
vendored
2
.github/workflows/timezone-update.yml
vendored
@ -33,7 +33,9 @@ jobs:
|
||||
run: build\Release\tzextract.exe winpr\libwinpr\timezone
|
||||
- name: Format code
|
||||
run: |
|
||||
clang-format -i --style=file:.clang-format winpr/libwinpr/timezone/WindowsZones.c
|
||||
clang-format -i --style=file:.clang-format winpr/libwinpr/timezone/TimeZoneNameMap.c
|
||||
clang-format -i --style=file:.clang-format winpr/libwinpr/timezone/TimeZoneNameMap_static.h
|
||||
clang-format -i --style=file:.clang-format winpr/libwinpr/timezone/TimeZoneNameMap.json
|
||||
- name: Create Pull Request
|
||||
id: cpr
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
**/CMakeCache.txt
|
||||
**/CMakeFiles
|
||||
build
|
||||
checker
|
||||
abi-checker
|
||||
|
56
.travis.yml
56
.travis.yml
@ -1,56 +0,0 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
os: linux
|
||||
|
||||
language: c
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
- os: linux
|
||||
compiler: clang
|
||||
exclude:
|
||||
- compiler: gcc
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gdb
|
||||
- libx11-dev
|
||||
- libxrandr-dev
|
||||
- libxi-dev
|
||||
- libxv-dev
|
||||
- libcups2-dev
|
||||
- libxdamage-dev
|
||||
- libxcursor-dev
|
||||
- libxext-dev
|
||||
- libxinerama-dev
|
||||
- libxkbcommon-dev
|
||||
- libxkbfile-dev
|
||||
- libxml2-dev
|
||||
- libasound2-dev
|
||||
- libgstreamer1.0-dev
|
||||
- libgstreamer-plugins-base1.0-dev
|
||||
- libpulse-dev
|
||||
- libpcsclite-dev
|
||||
- libgsm1-dev
|
||||
- libavcodec-dev
|
||||
- libavutil-dev
|
||||
- libxext-dev
|
||||
- ninja-build
|
||||
- libsystemd-dev
|
||||
- libwayland-dev
|
||||
|
||||
before_script:
|
||||
- ulimit -c unlimited -S
|
||||
|
||||
script:
|
||||
- sudo hostname travis-ci.local
|
||||
- cmake -G Ninja -C ci/cmake-preloads/config-linux-all.txt -D CMAKE_BUILD_TYPE=Debug .
|
||||
- make
|
||||
- make test
|
109
CMakeLists.txt
109
CMakeLists.txt
@ -56,6 +56,11 @@ if (NOT WIN32 AND NOT ANDROID)
|
||||
option(WITH_X11 "build X11 client/server" ${OPT_DEFAULT_VAL})
|
||||
endif()
|
||||
|
||||
# Enable coverity related pragma definitions
|
||||
if (COVERITY_BUILD)
|
||||
add_compile_definitions(COVERITY_BUILD)
|
||||
endif()
|
||||
|
||||
# Include our extra modules
|
||||
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/)
|
||||
|
||||
@ -84,6 +89,7 @@ include(CMakePackageConfigHelpers)
|
||||
include(InstallFreeRDPMan)
|
||||
include(GetGitRevisionDescription)
|
||||
include(SetFreeRDPCMakeInstallDir)
|
||||
include(Doxygen)
|
||||
|
||||
# Soname versioning
|
||||
set(BUILD_NUMBER 0)
|
||||
@ -92,7 +98,7 @@ if ($ENV{BUILD_NUMBER})
|
||||
endif()
|
||||
|
||||
set(VERSION_REGEX "^(.*)([0-9]+)\\.([0-9]+)\\.([0-9]+)-?(.*)")
|
||||
set(RAW_VERSION_STRING "3.7.1-dev0")
|
||||
set(RAW_VERSION_STRING "3.9.1-dev0")
|
||||
if(EXISTS "${PROJECT_SOURCE_DIR}/.source_tag")
|
||||
file(READ ${PROJECT_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
|
||||
elseif(USE_VERSION_FROM_GIT_TAG)
|
||||
@ -188,14 +194,14 @@ endif(CMAKE_CROSSCOMPILING)
|
||||
# Turn on solution folders (2.8.4+)
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
option(EXPORT_ALL_SYMBOLS "Export all symbols form library" OFF)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
set(EXPORT_ALL_SYMBOLS TRUE)
|
||||
set(CTEST_OUTPUT_ON_FAILURE TRUE)
|
||||
add_definitions(-DBUILD_TESTING)
|
||||
elseif(NOT DEFINED EXPORT_ALL_SYMBOLS)
|
||||
set(EXPORT_ALL_SYMBOLS FALSE)
|
||||
option(CTEST_OUTPUT_ON_FAILURE ON "show verbose output on CTest failures")
|
||||
if(BUILD_TESTING_INTERNAL)
|
||||
set(EXPORT_ALL_SYMBOLS ON CACHE BOOL "testing default" FORCE)
|
||||
add_definitions(-DBUILD_TESTING_INTERNAL)
|
||||
elseif(BUILD_TESTING)
|
||||
set(EXPORT_ALL_SYMBOLS OFF CACHE BOOL "testing default" FORCE)
|
||||
else()
|
||||
option(EXPORT_ALL_SYMBOLS "Export all symbols form library" OFF)
|
||||
endif()
|
||||
|
||||
if (EXPORT_ALL_SYMBOLS)
|
||||
@ -238,22 +244,6 @@ if(CMAKE_COMPILER_IS_GNUCC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# When building with Unix Makefiles and doing any release builds
|
||||
# try to set __FILE__ to relative paths via a make specific macro
|
||||
if (CMAKE_GENERATOR MATCHES "Unix Makefile*")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
string(TOUPPER ${CMAKE_BUILD_TYPE} UPPER_BUILD_TYPE)
|
||||
CHECK_C_COMPILER_FLAG (-Wno-builtin-macro-redefined Wno-builtin-macro-redefined)
|
||||
if(Wno-builtin-macro-redefined)
|
||||
set(CMAKE_C_FLAGS_${UPPER_BUILD_TYPE} "${CMAKE_C_FLAGS_${UPPER_BUILD_TYPE}} -Wno-builtin-macro-redefined -D__FILE__='\"$(subst ${PROJECT_BINARY_DIR}/,,$(subst ${PROJECT_SOURCE_DIR}/,,$(abspath $<)))\"'")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_CLANG)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-c11-extensions -Wno-gnu")
|
||||
endif()
|
||||
|
||||
set(THREAD_PREFER_PTHREAD_FLAG TRUE)
|
||||
|
||||
if(NOT IOS)
|
||||
@ -262,12 +252,6 @@ endif()
|
||||
|
||||
# Enable address sanitizer, where supported and when required
|
||||
if(CMAKE_COMPILER_IS_CLANG OR CMAKE_COMPILER_IS_GNUCC)
|
||||
CHECK_C_COMPILER_FLAG ("-fno-omit-frame-pointer" fno-omit-frame-pointer)
|
||||
|
||||
if (fno-omit-frame-pointer)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
|
||||
set(CMAKE_REQUIRED_LINK_OPTIONS_SAVED ${CMAKE_REQUIRED_LINK_OPTIONS})
|
||||
file(WRITE ${PROJECT_BINARY_DIR}/foo.txt "")
|
||||
if(WITH_SANITIZE_ADDRESS)
|
||||
@ -422,8 +406,13 @@ if(ANDROID)
|
||||
endif()
|
||||
|
||||
if(ANDROID_ABI STREQUAL arm64-v8a)
|
||||
# https://github.com/android/ndk/issues/910
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfloat-abi=softfp")
|
||||
include (CheckCCompilerFlag)
|
||||
check_c_compiler_flag("-mfloat-abi=softfp" ABI_SOFTFP_SUPPORTED)
|
||||
|
||||
if (ABI_SOFTFP_SUPPORTED)
|
||||
# https://github.com/android/ndk/issues/910
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfloat-abi=softfp")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
@ -491,10 +480,6 @@ set(PCSC_FEATURE_TYPE "RECOMMENDED")
|
||||
set(PCSC_FEATURE_PURPOSE "smart card")
|
||||
set(PCSC_FEATURE_DESCRIPTION "smart card device redirection")
|
||||
|
||||
set(FFMPEG_FEATURE_TYPE "RECOMMENDED")
|
||||
set(FFMPEG_FEATURE_PURPOSE "multimedia")
|
||||
set(FFMPEG_FEATURE_DESCRIPTION "multimedia redirection, audio and video playback")
|
||||
|
||||
set(OPENH264_FEATURE_TYPE "OPTIONAL")
|
||||
set(OPENH264_FEATURE_PURPOSE "codec")
|
||||
set(OPENH264_FEATURE_DESCRIPTION "use OpenH264 library")
|
||||
@ -526,11 +511,9 @@ set(SOXR_FEATURE_DESCRIPTION "SOX audio resample library")
|
||||
if(WIN32)
|
||||
set(WAYLAND_FEATURE_TYPE "DISABLED")
|
||||
set(PCSC_FEATURE_TYPE "DISABLED")
|
||||
set(FFMPEG_FEATURE_TYPE "OPTIONAL")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(FFMPEG_FEATURE_TYPE "OPTIONAL")
|
||||
set(WAYLAND_FEATURE_TYPE "DISABLED")
|
||||
if(IOS)
|
||||
set(PCSC_FEATURE_TYPE "DISABLED")
|
||||
@ -547,7 +530,7 @@ find_feature(Wayland ${WAYLAND_FEATURE_TYPE} ${WAYLAND_FEATURE_PURPOSE} ${WAYLAN
|
||||
option(WITH_LIBRESSL "build with LibreSSL" OFF)
|
||||
if (WITH_LIBRESSL)
|
||||
find_package(LibreSSL REQUIRED)
|
||||
include_directories(${LibreSSL_INCLUDE_DIRS})
|
||||
include_directories(SYSTEM ${LibreSSL_INCLUDE_DIRS})
|
||||
set(OPENSSL_INCLUDE_DIR ${LIBRESSL_INCLUDE_DIR})
|
||||
set(OPENSSL_LIBRARIES ${LIBRESSL_LIBRARIES})
|
||||
set(OPENSSL_CRYPTO_LIBRARIES ${LIBRESSL_LIBRARIES})
|
||||
@ -563,11 +546,9 @@ endif()
|
||||
find_feature(PCSC ${PCSC_FEATURE_TYPE} ${PCSC_FEATURE_PURPOSE} ${PCSC_FEATURE_DESCRIPTION})
|
||||
|
||||
if (WITH_DSP_FFMPEG OR WITH_VIDEO_FFMPEG OR WITH_FFMPEG)
|
||||
set(FFMPEG_FEATURE_TYPE "REQUIRED" )
|
||||
find_package(FFmpeg REQUIRED COMPONENTS AVUTIL AVCODEC)
|
||||
endif()
|
||||
|
||||
find_feature(FFmpeg ${FFMPEG_FEATURE_TYPE} ${FFMPEG_FEATURE_PURPOSE} ${FFMPEG_FEATURE_DESCRIPTION})
|
||||
|
||||
find_feature(OpenH264 ${OPENH264_FEATURE_TYPE} ${OPENH264_FEATURE_PURPOSE} ${OPENH264_FEATURE_DESCRIPTION})
|
||||
find_feature(OpenCL ${OPENCL_FEATURE_TYPE} ${OPENCL_FEATURE_PURPOSE} ${OPENCL_FEATURE_DESCRIPTION})
|
||||
find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION})
|
||||
@ -583,29 +564,6 @@ endif (WITH_OPENH264 AND NOT WITH_OPENH264_LOADING)
|
||||
# Version check, if we have detected FFMPEG but the version is too old
|
||||
# deactivate it as sound backend.
|
||||
if (WITH_DSP_FFMPEG)
|
||||
# Deactivate FFmpeg backend for sound, if the version is too old.
|
||||
# See libfreerdp/codec/dsp_ffmpeg.h
|
||||
file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version.h" AV_VERSION_FILE REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+")
|
||||
if (EXISTS "${AVCODEC_INCLUDE_DIR}/libavcodec/version_major.h")
|
||||
file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version_major.h" AV_VERSION_FILE2 REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+")
|
||||
list(APPEND AV_VERSION_FILE ${AV_VERSION_FILE2})
|
||||
endif()
|
||||
|
||||
FOREACH(item ${AV_VERSION_FILE})
|
||||
STRING(REGEX MATCH "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+" litem ${item})
|
||||
IF(litem)
|
||||
string(REGEX REPLACE "[ \t]+" ";" VSPLIT_LINE ${litem})
|
||||
list(LENGTH VSPLIT_LINE VSPLIT_LINE_LEN)
|
||||
if (NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
||||
message(ERROR "invalid entry in libavcodec version header ${item}")
|
||||
endif(NOT "${VSPLIT_LINE_LEN}" EQUAL "2")
|
||||
list(GET VSPLIT_LINE 0 VNAME)
|
||||
list(GET VSPLIT_LINE 1 VVALUE)
|
||||
set(${VNAME} ${VVALUE})
|
||||
ENDIF(litem)
|
||||
ENDFOREACH(item ${AV_VERSION_FILE})
|
||||
|
||||
set(AVCODEC_VERSION "${LIBAVCODEC_VERSION_MAJOR}.${LIBAVCODEC_VERSION_MINOR}.${LIBAVCODEC_VERSION_MICRO}")
|
||||
if (AVCODEC_VERSION VERSION_LESS "57.48.101")
|
||||
message(WARNING "FFmpeg version detected (${AVCODEC_VERSION}) is too old. (Require at least 57.48.101 for sound). Deactivating")
|
||||
set(WITH_DSP_FFMPEG OFF)
|
||||
@ -620,7 +578,7 @@ set(WITH_OPENH264 ${OPENH264_FOUND})
|
||||
if(OPENSSL_FOUND)
|
||||
add_definitions("-DWITH_OPENSSL")
|
||||
message(STATUS "Using OpenSSL Version: ${OPENSSL_VERSION}")
|
||||
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||
include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
if(MBEDTLS_FOUND)
|
||||
@ -682,7 +640,7 @@ if(ANDROID)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg")
|
||||
set(PROFILER_LIBRARIES
|
||||
"${FREERDP_EXTERNAL_PROFILER_PATH}/obj/local/${ANDROID_ABI}/libandroid-ndk-profiler.a")
|
||||
include_directories("${FREERDP_EXTERNAL_PROFILER_PATH}")
|
||||
include_directories(SYSTEM "${FREERDP_EXTERNAL_PROFILER_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -690,7 +648,7 @@ endif()
|
||||
|
||||
include(CTest)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
|
||||
enable_testing()
|
||||
|
||||
if(MSVC)
|
||||
@ -702,13 +660,6 @@ endif()
|
||||
|
||||
include(CommonConfigOptions)
|
||||
|
||||
# WinPR
|
||||
# We want to control the winpr assert for the whole project
|
||||
option(WITH_VERBOSE_WINPR_ASSERT "Compile with verbose WINPR_ASSERT." ON)
|
||||
if (WITH_VERBOSE_WINPR_ASSERT)
|
||||
add_definitions(-DWITH_VERBOSE_WINPR_ASSERT)
|
||||
endif()
|
||||
|
||||
if (FREERDP_UNIFIED_BUILD)
|
||||
add_subdirectory(winpr)
|
||||
if (WITH_WAYLAND)
|
||||
@ -725,7 +676,7 @@ if (FREERDP_UNIFIED_BUILD)
|
||||
include_directories(${PROJECT_BINARY_DIR}/winpr/include)
|
||||
else()
|
||||
find_package(WinPR 3 REQUIRED)
|
||||
include_directories(${WinPR_INCLUDE_DIR})
|
||||
include_directories(SYSTEM ${WinPR_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
option(WITH_AAD "Compile with support for Azure AD authentication" ${WITH_WINPR_JSON})
|
||||
@ -740,7 +691,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
if(WITH_THIRD_PARTY)
|
||||
add_subdirectory(third-party)
|
||||
if (NOT "${THIRD_PARTY_INCLUDES}" STREQUAL "")
|
||||
include_directories(${THIRD_PARTY_INCLUDES})
|
||||
include_directories(SYSTEM ${THIRD_PARTY_INCLUDES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -776,7 +727,7 @@ endif()
|
||||
set(FREERDP_BUILD_CONFIG_LIST "")
|
||||
GET_CMAKE_PROPERTY(res VARIABLES)
|
||||
FOREACH(var ${res})
|
||||
IF (var MATCHES "^WITH_*|^BUILD_TESTING|^WINPR_HAVE_*")
|
||||
IF (var MATCHES "^WITH_*|^BUILD_TESTING*|^WINPR_HAVE_*")
|
||||
LIST(APPEND FREERDP_BUILD_CONFIG_LIST "${var}=${${var}}")
|
||||
ENDIF()
|
||||
ENDFOREACH()
|
||||
|
54
ChangeLog
54
ChangeLog
@ -1,3 +1,57 @@
|
||||
# 2024-10-21 Version 3.9.0
|
||||
|
||||
We're proud to present the newest release of FreeRDP.
|
||||
This one brings some major code cleanup (we've addressed lots of clang-tidy
|
||||
warnings) as well as some highly anticipated new features.
|
||||
We also did update the API documentation quite a bit (still incomplete though,
|
||||
help always welcome ;))
|
||||
|
||||
So, what is new:
|
||||
* Support for RDPEAR (remote credential guard) /remoteGuard option for non windows clients
|
||||
* Global configuration file support, allowing to configure certificate
|
||||
accept/ignore/... default settings for all users
|
||||
* Simplified manpage generation, eliminates docbook and xmlto dependencies
|
||||
speeding up builds
|
||||
* New API for client channels to run tasks on RDP thread
|
||||
* New extended transport layer API
|
||||
* RDPECAM MJPEG support
|
||||
* the first updates of timezone definitions from our automated ci
|
||||
|
||||
Noteworthy changes:
|
||||
* Fix bugs in SSE4.1/AVX2 image copy (#10720)
|
||||
* Add warnings for invalid monitor settings during connect (#10672)
|
||||
* Fix ALSA microphone support (#10664)
|
||||
* Fix modal windows in RAILS mode (#10629)
|
||||
* Update experimental SDL3 client (SDL3 API should now have been stabilized,
|
||||
various pull requests)
|
||||
* Fix keyboard layouts, the external JSON did miss a few (#10639)
|
||||
|
||||
For a complete and detailed change log since the last release run:
|
||||
git log 3.9.0...3.8.0
|
||||
|
||||
# 2024-08-30 Version 3.8.0
|
||||
|
||||
This is a bugfix release. Due to additional exports required by a bugfix the minor version was incremented
|
||||
|
||||
Noteworthy changes:
|
||||
* Reduce number of warnings on CI build (make dependency includes SYSTEM) (#10509)
|
||||
* Fix possible crashes with P11 certificate parsing (#10462, #10463)
|
||||
* Various clipboard related fixes (#10472, #10476, #10477, #10480, #10484)
|
||||
* Fix a race condition on DesktopResize (xfreerdp) (#10488)
|
||||
* Improve certificate warnings (#10489)
|
||||
* Try all possible resolved IP addresses for a DNS name on connect (#10491)
|
||||
* Fix an issue with GFX SolidFill alpha data (#10498)
|
||||
* Various fixes for SDL clients (#10504, #10492, #10471)
|
||||
* Fix serial and parallel redirection crashes (#10510)
|
||||
* Fix android build issues with NDK 27 (#10529)
|
||||
* Improve performance of some WinPR primitives (#10528)
|
||||
* Fix an issue with autoreconnect (#10523)
|
||||
* Support ssh-askpass like password reading (#10516)
|
||||
* Lots of code cleanups to reduce clang-tidy warnings (#10531, #10525, #10521, #10520, #10519, #10518)
|
||||
|
||||
For a complete and detailed change log since the last release run:
|
||||
git log 3.8.0...3.7.0
|
||||
|
||||
# 2024-08-08 Version 3.7.0
|
||||
|
||||
This release has accumulated quite a number of changes. Along bugfixes for 3.6.3 it also
|
||||
|
@ -175,7 +175,7 @@ static const IWTSVirtualChannelCallback ainput_functions = { ainput_on_data_rece
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT ainput_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE ainput_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, AINPUT_DVC_CHANNEL_NAME,
|
||||
sizeof(AINPUT_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
|
||||
|
@ -176,8 +176,8 @@ static UINT ainput_server_send_version(ainput_server* ainput)
|
||||
Stream_Write_UINT32(s, AINPUT_VERSION_MAJOR); /* Version (4 bytes) */
|
||||
Stream_Write_UINT32(s, AINPUT_VERSION_MINOR); /* Version (4 bytes) */
|
||||
|
||||
WINPR_ASSERT(Stream_GetPosition(s) <= ULONG_MAX);
|
||||
if (!WTSVirtualChannelWrite(ainput->ainput_channel, (PCHAR)Stream_Buffer(s),
|
||||
WINPR_ASSERT(Stream_GetPosition(s) <= UINT32_MAX);
|
||||
if (!WTSVirtualChannelWrite(ainput->ainput_channel, Stream_BufferAs(s, char),
|
||||
(ULONG)Stream_GetPosition(s), &written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
@ -292,7 +292,7 @@ static DWORD WINAPI ainput_server_thread_func(LPVOID arg)
|
||||
}
|
||||
}
|
||||
|
||||
WTSVirtualChannelClose(ainput->ainput_channel);
|
||||
(void)WTSVirtualChannelClose(ainput->ainput_channel);
|
||||
ainput->ainput_channel = NULL;
|
||||
|
||||
if (error && ainput->context.rdpcontext)
|
||||
@ -327,7 +327,7 @@ static UINT ainput_server_open(ainput_server_context* context)
|
||||
if (!ainput->thread)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
CloseHandle(ainput->stopEvent);
|
||||
(void)CloseHandle(ainput->stopEvent);
|
||||
ainput->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -351,7 +351,7 @@ static UINT ainput_server_close(ainput_server_context* context)
|
||||
|
||||
if (!ainput->externalThread && ainput->thread)
|
||||
{
|
||||
SetEvent(ainput->stopEvent);
|
||||
(void)SetEvent(ainput->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(ainput->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -360,8 +360,8 @@ static UINT ainput_server_close(ainput_server_context* context)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(ainput->thread);
|
||||
CloseHandle(ainput->stopEvent);
|
||||
(void)CloseHandle(ainput->thread);
|
||||
(void)CloseHandle(ainput->stopEvent);
|
||||
ainput->thread = NULL;
|
||||
ainput->stopEvent = NULL;
|
||||
}
|
||||
@ -369,7 +369,7 @@ static UINT ainput_server_close(ainput_server_context* context)
|
||||
{
|
||||
if (ainput->state != AINPUT_INITIAL)
|
||||
{
|
||||
WTSVirtualChannelClose(ainput->ainput_channel);
|
||||
(void)WTSVirtualChannelClose(ainput->ainput_channel);
|
||||
ainput->ainput_channel = NULL;
|
||||
ainput->state = AINPUT_INITIAL;
|
||||
}
|
||||
@ -467,7 +467,7 @@ static UINT ainput_process_message(ainput_server* ainput)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(ainput->ainput_channel, 0, (PCHAR)Stream_Buffer(s),
|
||||
if (WTSVirtualChannelRead(ainput->ainput_channel, 0, Stream_BufferAs(s, char),
|
||||
(ULONG)Stream_Capacity(s), &ActualBytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
|
@ -30,6 +30,6 @@ set(${MODULE_PREFIX}_LIBS
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${ALSA_INCLUDE_DIRS})
|
||||
include_directories(SYSTEM ${ALSA_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
@ -75,12 +75,6 @@ static snd_pcm_format_t audin_alsa_format(UINT32 wFormatTag, UINT32 bitPerChanne
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
case WAVE_FORMAT_ALAW:
|
||||
return SND_PCM_FORMAT_A_LAW;
|
||||
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return SND_PCM_FORMAT_MU_LAW;
|
||||
|
||||
default:
|
||||
return SND_PCM_FORMAT_UNKNOWN;
|
||||
}
|
||||
@ -121,16 +115,19 @@ static BOOL audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_hand
|
||||
|
||||
static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
||||
{
|
||||
int error = 0;
|
||||
DWORD error = CHANNEL_RC_OK;
|
||||
BYTE* buffer = NULL;
|
||||
snd_pcm_t* capture_handle = NULL;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*)arg;
|
||||
DWORD status = 0;
|
||||
|
||||
WINPR_ASSERT(alsa);
|
||||
|
||||
WLog_Print(alsa->log, WLOG_DEBUG, "in");
|
||||
|
||||
if ((error = snd_pcm_open(&capture_handle, alsa->device_name, SND_PCM_STREAM_CAPTURE, 0)) < 0)
|
||||
snd_pcm_t* capture_handle = NULL;
|
||||
const int rc = snd_pcm_open(&capture_handle, alsa->device_name, SND_PCM_STREAM_CAPTURE, 0);
|
||||
if (rc < 0)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_open (%s)", snd_strerror(error));
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_open (%s)", snd_strerror(rc));
|
||||
error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
goto out;
|
||||
}
|
||||
@ -154,35 +151,43 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
||||
while (1)
|
||||
{
|
||||
size_t frames = alsa->frames_per_packet;
|
||||
status = WaitForSingleObject(alsa->stopEvent, 0);
|
||||
const DWORD status = WaitForSingleObject(alsa->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "WaitForSingleObject failed with error %ld!", error);
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!",
|
||||
error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
error = snd_pcm_readi(capture_handle, buffer, frames);
|
||||
|
||||
if (error == 0)
|
||||
continue;
|
||||
|
||||
if (error == -EPIPE)
|
||||
{
|
||||
snd_pcm_recover(capture_handle, error, 0);
|
||||
continue;
|
||||
}
|
||||
else if (error < 0)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_readi (%s)", snd_strerror(error));
|
||||
WLog_Print(alsa->log, WLOG_DEBUG, "alsa->stopEvent requests termination");
|
||||
break;
|
||||
}
|
||||
|
||||
error = alsa->receive(&alsa->aformat, buffer, (long)error * alsa->bytes_per_frame,
|
||||
snd_pcm_sframes_t framesRead = snd_pcm_readi(capture_handle, buffer, frames);
|
||||
|
||||
if (framesRead == 0)
|
||||
continue;
|
||||
|
||||
if (framesRead == -EPIPE)
|
||||
{
|
||||
const int res = snd_pcm_recover(capture_handle, (int)framesRead, 0);
|
||||
if (res < 0)
|
||||
WLog_Print(alsa->log, WLOG_WARN, "snd_pcm_recover (%s)", snd_strerror(res));
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (framesRead < 0)
|
||||
{
|
||||
WLog_Print(alsa->log, WLOG_ERROR, "snd_pcm_readi (%s)", snd_strerror((int)framesRead));
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
error = alsa->receive(&alsa->aformat, buffer, (long)framesRead * alsa->bytes_per_frame,
|
||||
alsa->user_data);
|
||||
|
||||
if (error)
|
||||
@ -196,7 +201,11 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
|
||||
free(buffer);
|
||||
|
||||
if (capture_handle)
|
||||
snd_pcm_close(capture_handle);
|
||||
{
|
||||
const int res = snd_pcm_close(capture_handle);
|
||||
if (res < 0)
|
||||
WLog_Print(alsa->log, WLOG_WARN, "snd_pcm_close (%s)", snd_strerror(res));
|
||||
}
|
||||
|
||||
out:
|
||||
WLog_Print(alsa->log, WLOG_DEBUG, "out");
|
||||
@ -299,7 +308,7 @@ static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive, void* us
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
CloseHandle(alsa->stopEvent);
|
||||
(void)CloseHandle(alsa->stopEvent);
|
||||
alsa->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -319,7 +328,7 @@ static UINT audin_alsa_close(IAudinDevice* device)
|
||||
|
||||
if (alsa->stopEvent)
|
||||
{
|
||||
SetEvent(alsa->stopEvent);
|
||||
(void)SetEvent(alsa->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(alsa->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -329,9 +338,9 @@ static UINT audin_alsa_close(IAudinDevice* device)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(alsa->stopEvent);
|
||||
(void)CloseHandle(alsa->stopEvent);
|
||||
alsa->stopEvent = NULL;
|
||||
CloseHandle(alsa->thread);
|
||||
(void)CloseHandle(alsa->thread);
|
||||
alsa->thread = NULL;
|
||||
}
|
||||
|
||||
@ -350,7 +359,7 @@ static UINT audin_alsa_parse_addin_args(AudinALSADevice* device, const ADDIN_ARG
|
||||
int status = 0;
|
||||
DWORD flags = 0;
|
||||
const COMMAND_LINE_ARGUMENT_A* arg = NULL;
|
||||
AudinALSADevice* alsa = (AudinALSADevice*)device;
|
||||
AudinALSADevice* alsa = device;
|
||||
COMMAND_LINE_ARGUMENT_A audin_alsa_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
|
||||
NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
|
||||
@ -390,8 +399,8 @@ static UINT audin_alsa_parse_addin_args(AudinALSADevice* device, const ADDIN_ARG
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(
|
||||
UINT alsa_freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE alsa_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
const ADDIN_ARGV* args = NULL;
|
||||
AudinALSADevice* alsa = NULL;
|
||||
|
@ -113,7 +113,7 @@ static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStre
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
Stream_SealLength(out);
|
||||
WINPR_ASSERT(Stream_Length(out) <= ULONG_MAX);
|
||||
WINPR_ASSERT(Stream_Length(out) <= UINT32_MAX);
|
||||
const UINT error = callback->channel->Write(callback->channel, (ULONG)Stream_Length(out),
|
||||
Stream_Buffer(out), NULL);
|
||||
|
||||
@ -810,9 +810,9 @@ static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin, const char* name, cons
|
||||
|
||||
FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints = { 0 };
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
const PFREERDP_AUDIN_DEVICE_ENTRY entry =
|
||||
(const PFREERDP_AUDIN_DEVICE_ENTRY)freerdp_load_channel_addin_entry(AUDIN_CHANNEL_NAME,
|
||||
name, NULL, 0);
|
||||
|
||||
PVIRTUALCHANNELENTRY pvce = freerdp_load_channel_addin_entry(AUDIN_CHANNEL_NAME, name, NULL, 0);
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY entry = WINPR_FUNC_PTR_CAST(pvce, PFREERDP_AUDIN_DEVICE_ENTRY);
|
||||
|
||||
if (entry == NULL)
|
||||
{
|
||||
@ -827,7 +827,8 @@ static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin, const char* name, cons
|
||||
entryPoints.args = args;
|
||||
entryPoints.rdpcontext = audin->rdpcontext;
|
||||
|
||||
if ((error = entry(&entryPoints)))
|
||||
error = entry(&entryPoints);
|
||||
if (error)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "%s entry returned error %" PRIu32 ".", name, error);
|
||||
return error;
|
||||
@ -940,12 +941,12 @@ BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, const ADDIN_ARGV* args)
|
||||
}
|
||||
CommandLineSwitchCase(arg, "rate")
|
||||
{
|
||||
long val = strtol(arg->Value, NULL, 0);
|
||||
unsigned long val = strtoul(arg->Value, NULL, 0);
|
||||
|
||||
if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
|
||||
if ((errno != 0) || (val == 0) || (val > UINT32_MAX))
|
||||
return FALSE;
|
||||
|
||||
audin->fixed_format->nSamplesPerSec = val;
|
||||
audin->fixed_format->nSamplesPerSec = (UINT32)val;
|
||||
}
|
||||
CommandLineSwitchCase(arg, "channel")
|
||||
{
|
||||
@ -968,7 +969,7 @@ BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, const ADDIN_ARGV* args)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT audin_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE audin_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
struct SubsystemEntry
|
||||
{
|
||||
|
@ -34,6 +34,6 @@ set(${MODULE_PREFIX}_LIBS
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${MAC_INCLUDE_DIRS})
|
||||
include_directories(SYSTEM ${MAC_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
@ -295,8 +295,8 @@ static UINT audin_ios_free(IAudinDevice *device)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
FREERDP_ENTRY_POINT(
|
||||
UINT ios_freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE ios_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
|
@ -36,6 +36,6 @@ set(${MODULE_PREFIX}_LIBS
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${MAC_INCLUDE_DIRS})
|
||||
include_directories(SYSTEM ${MAC_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
@ -380,8 +380,8 @@ static UINT audin_mac_parse_addin_args(AudinMacDevice *device, const ADDIN_ARGV
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
FREERDP_ENTRY_POINT(
|
||||
UINT mac_freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE mac_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
|
@ -31,6 +31,6 @@ set(${MODULE_PREFIX}_LIBS
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${OpenSLES_INCLUDE_DIRS})
|
||||
include_directories(SYSTEM ${OpenSLES_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
@ -292,7 +292,7 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device, const A
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT opensles_freerdp_audin_client_subsystem_entry(
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE opensles_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
const ADDIN_ARGV* args;
|
||||
|
@ -31,11 +31,12 @@ set(${MODULE_PREFIX}_LIBS
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
include_directories(${OSS_INCLUDE_DIRS})
|
||||
configure_file(
|
||||
include_directories(SYSTEM ${OSS_INCLUDE_DIRS})
|
||||
cleaning_configure_file(
|
||||
${CMAKE_SOURCE_DIR}/cmake/oss-includes.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/oss-includes.h
|
||||
@ONLY
|
||||
)
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
|
@ -85,15 +85,14 @@ static UINT32 audin_oss_get_format(const AUDIO_FORMAT* format)
|
||||
|
||||
case 16:
|
||||
return AFMT_S16_LE;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ALAW:
|
||||
return AFMT_A_LAW;
|
||||
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return AFMT_MU_LAW;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -160,8 +159,8 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
|
||||
|
||||
if (oss->dev_unit != -1)
|
||||
{
|
||||
sprintf_s(dev_name, (PATH_MAX - 1), "/dev/dsp%i", oss->dev_unit);
|
||||
sprintf_s(mixer_name, PATH_MAX - 1, "/dev/mixer%i", oss->dev_unit);
|
||||
(void)sprintf_s(dev_name, (PATH_MAX - 1), "/dev/dsp%i", oss->dev_unit);
|
||||
(void)sprintf_s(mixer_name, PATH_MAX - 1, "/dev/mixer%i", oss->dev_unit);
|
||||
}
|
||||
|
||||
WLog_INFO(TAG, "open: %s", dev_name);
|
||||
@ -309,7 +308,7 @@ static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive, void* use
|
||||
if (!(oss->thread = CreateThread(NULL, 0, audin_oss_thread_func, oss, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(oss->stopEvent);
|
||||
(void)CloseHandle(oss->stopEvent);
|
||||
oss->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -332,7 +331,7 @@ static UINT audin_oss_close(IAudinDevice* device)
|
||||
|
||||
if (oss->stopEvent != NULL)
|
||||
{
|
||||
SetEvent(oss->stopEvent);
|
||||
(void)SetEvent(oss->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -341,9 +340,9 @@ static UINT audin_oss_close(IAudinDevice* device)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(oss->stopEvent);
|
||||
(void)CloseHandle(oss->stopEvent);
|
||||
oss->stopEvent = NULL;
|
||||
CloseHandle(oss->thread);
|
||||
(void)CloseHandle(oss->thread);
|
||||
oss->thread = NULL;
|
||||
}
|
||||
|
||||
@ -386,7 +385,7 @@ static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, const ADDIN_ARGV*
|
||||
char* eptr = NULL;
|
||||
DWORD flags = 0;
|
||||
const COMMAND_LINE_ARGUMENT_A* arg = NULL;
|
||||
AudinOSSDevice* oss = (AudinOSSDevice*)device;
|
||||
AudinOSSDevice* oss = device;
|
||||
COMMAND_LINE_ARGUMENT_A audin_oss_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
|
||||
NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
|
||||
@ -445,8 +444,8 @@ static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, const ADDIN_ARGV*
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(
|
||||
UINT oss_freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
const ADDIN_ARGV* args = NULL;
|
||||
AudinOSSDevice* oss = NULL;
|
||||
|
@ -30,6 +30,6 @@ set(${MODULE_PREFIX}_LIBS
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${PULSEAUDIO_INCLUDE_DIR})
|
||||
include_directories(SYSTEM ${PULSEAUDIO_INCLUDE_DIR})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
@ -112,9 +112,6 @@ static void audin_pulse_context_state_callback(pa_context* context, void* userda
|
||||
switch (state)
|
||||
{
|
||||
case PA_CONTEXT_READY:
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_CONTEXT_FAILED:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
@ -283,14 +280,6 @@ static UINT audin_pulse_set_format(IAudinDevice* device, const AUDIO_FORMAT* for
|
||||
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ALAW: /* A-LAW */
|
||||
sample_spec.format = PA_SAMPLE_ALAW;
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_MULAW: /* U-LAW */
|
||||
sample_spec.format = PA_SAMPLE_ULAW;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -303,15 +292,14 @@ static UINT audin_pulse_set_format(IAudinDevice* device, const AUDIO_FORMAT* for
|
||||
static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
|
||||
{
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)userdata;
|
||||
WINPR_ASSERT(pulse);
|
||||
|
||||
pa_stream_state_t state = pa_stream_get_state(stream);
|
||||
|
||||
WLog_Print(pulse->log, WLOG_DEBUG, "stream state %s", pulse_stream_state_string(state));
|
||||
switch (state)
|
||||
{
|
||||
case PA_STREAM_READY:
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
break;
|
||||
|
||||
case PA_STREAM_FAILED:
|
||||
case PA_STREAM_TERMINATED:
|
||||
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||
@ -405,7 +393,9 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
buffer_attr.prebuf = (UINT32)-1;
|
||||
buffer_attr.minreq = (UINT32)-1;
|
||||
/* 500ms latency */
|
||||
buffer_attr.fragsize = pulse->bytes_per_frame * pulse->frames_per_packet;
|
||||
const size_t frag = pulse->bytes_per_frame * pulse->frames_per_packet;
|
||||
WINPR_ASSERT(frag <= UINT32_MAX);
|
||||
buffer_attr.fragsize = (uint32_t)frag;
|
||||
|
||||
if (buffer_attr.fragsize % pulse->format.nBlockAlign)
|
||||
buffer_attr.fragsize +=
|
||||
@ -420,7 +410,7 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
return pa_context_errno(pulse->context);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
while (pulse->stream)
|
||||
{
|
||||
state = pa_stream_get_state(pulse->stream);
|
||||
|
||||
@ -455,7 +445,7 @@ static UINT audin_pulse_parse_addin_args(AudinPulseDevice* device, const ADDIN_A
|
||||
int status = 0;
|
||||
DWORD flags = 0;
|
||||
const COMMAND_LINE_ARGUMENT_A* arg = NULL;
|
||||
AudinPulseDevice* pulse = (AudinPulseDevice*)device;
|
||||
AudinPulseDevice* pulse = device;
|
||||
COMMAND_LINE_ARGUMENT_A audin_pulse_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
|
||||
NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
|
||||
@ -496,7 +486,7 @@ static UINT audin_pulse_parse_addin_args(AudinPulseDevice* device, const ADDIN_A
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT pulse_freerdp_audin_client_subsystem_entry(
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE pulse_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
const ADDIN_ARGV* args = NULL;
|
||||
|
@ -30,7 +30,7 @@ set(${MODULE_PREFIX}_LIBS
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${SNDIO_INCLUDE_DIRS})
|
||||
include_directories(SYSTEM ${SNDIO_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
|
@ -204,7 +204,7 @@ static UINT audin_sndio_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
sndio, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed");
|
||||
CloseHandle(sndio->stopEvent);
|
||||
(void)CloseHandle(sndio->stopEvent);
|
||||
sndio->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -227,7 +227,7 @@ static UINT audin_sndio_close(IAudinDevice* device)
|
||||
|
||||
if (sndio->stopEvent != NULL)
|
||||
{
|
||||
SetEvent(sndio->stopEvent);
|
||||
(void)SetEvent(sndio->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(sndio->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -236,9 +236,9 @@ static UINT audin_sndio_close(IAudinDevice* device)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(sndio->stopEvent);
|
||||
(void)CloseHandle(sndio->stopEvent);
|
||||
sndio->stopEvent = NULL;
|
||||
CloseHandle(sndio->thread);
|
||||
(void)CloseHandle(sndio->thread);
|
||||
sndio->thread = NULL;
|
||||
}
|
||||
|
||||
@ -309,7 +309,7 @@ static UINT audin_sndio_parse_addin_args(AudinSndioDevice* device, ADDIN_ARGV* a
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT sndio_freerdp_audin_client_subsystem_entry(
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE sndio_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
ADDIN_ARGV* args;
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <mmsystem.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/wtsapi.h>
|
||||
#include <winpr/cmdline.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/addin.h>
|
||||
@ -176,7 +177,6 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||
|
||||
if (!winmm->hWaveIn)
|
||||
{
|
||||
MMRESULT rc;
|
||||
rc = waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur, (DWORD_PTR)waveInProc,
|
||||
(DWORD_PTR)winmm,
|
||||
CALLBACK_FUNCTION | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
|
||||
@ -293,7 +293,7 @@ static UINT audin_winmm_close(IAudinDevice* device)
|
||||
if (!winmm)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
SetEvent(winmm->stopEvent);
|
||||
(void)SetEvent(winmm->stopEvent);
|
||||
status = WaitForSingleObject(winmm->thread, INFINITE);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
@ -304,8 +304,8 @@ static UINT audin_winmm_close(IAudinDevice* device)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(winmm->thread);
|
||||
CloseHandle(winmm->stopEvent);
|
||||
(void)CloseHandle(winmm->thread);
|
||||
(void)CloseHandle(winmm->stopEvent);
|
||||
winmm->thread = NULL;
|
||||
winmm->stopEvent = NULL;
|
||||
winmm->receive = NULL;
|
||||
@ -436,7 +436,7 @@ static UINT audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* u
|
||||
if (!(winmm->thread = CreateThread(NULL, 0, audin_winmm_thread_func, winmm, 0, NULL)))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "CreateThread failed!");
|
||||
CloseHandle(winmm->stopEvent);
|
||||
(void)CloseHandle(winmm->stopEvent);
|
||||
winmm->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -491,7 +491,7 @@ static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, const ADDIN_A
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT winmm_freerdp_audin_client_subsystem_entry(
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE winmm_freerdp_audin_client_subsystem_entry(
|
||||
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
const ADDIN_ARGV* args;
|
||||
|
@ -378,8 +378,8 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
||||
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
|
||||
break;
|
||||
|
||||
WINPR_ASSERT(Stream_Capacity(s) <= ULONG_MAX);
|
||||
if (WTSVirtualChannelRead(audin->audin_channel, 0, (PCHAR)Stream_Buffer(s),
|
||||
WINPR_ASSERT(Stream_Capacity(s) <= UINT32_MAX);
|
||||
if (WTSVirtualChannelRead(audin->audin_channel, 0, Stream_BufferAs(s, char),
|
||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelRead failed!");
|
||||
@ -430,7 +430,7 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg)
|
||||
out_capacity:
|
||||
Stream_Free(s, TRUE);
|
||||
out:
|
||||
WTSVirtualChannelClose(audin->audin_channel);
|
||||
(void)WTSVirtualChannelClose(audin->audin_channel);
|
||||
audin->audin_channel = NULL;
|
||||
|
||||
if (error && audin->context.rdpcontext)
|
||||
@ -489,7 +489,7 @@ static BOOL audin_server_open(audin_server_context* context)
|
||||
CreateThread(NULL, 0, audin_server_thread_func, (void*)audin, 0, NULL)))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "CreateThread failed!");
|
||||
CloseHandle(audin->stopEvent);
|
||||
(void)CloseHandle(audin->stopEvent);
|
||||
audin->stopEvent = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
@ -516,7 +516,7 @@ static BOOL audin_server_close(audin_server_context* context)
|
||||
|
||||
if (audin->thread)
|
||||
{
|
||||
SetEvent(audin->stopEvent);
|
||||
(void)SetEvent(audin->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(audin->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -525,15 +525,15 @@ static BOOL audin_server_close(audin_server_context* context)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CloseHandle(audin->thread);
|
||||
CloseHandle(audin->stopEvent);
|
||||
(void)CloseHandle(audin->thread);
|
||||
(void)CloseHandle(audin->stopEvent);
|
||||
audin->thread = NULL;
|
||||
audin->stopEvent = NULL;
|
||||
}
|
||||
|
||||
if (audin->audin_channel)
|
||||
{
|
||||
WTSVirtualChannelClose(audin->audin_channel);
|
||||
(void)WTSVirtualChannelClose(audin->audin_channel);
|
||||
audin->audin_channel = NULL;
|
||||
}
|
||||
|
||||
@ -569,10 +569,8 @@ static UINT audin_server_packet_send(audin_server_context* context, wStream* s)
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
if (pos > UINT32_MAX)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!WTSVirtualChannelWrite(audin->audin_channel, (PCHAR)Stream_Buffer(s), (UINT32)pos,
|
||||
WINPR_ASSERT(pos <= UINT32_MAX);
|
||||
if (!WTSVirtualChannelWrite(audin->audin_channel, Stream_BufferAs(s, char), (UINT32)pos,
|
||||
&written))
|
||||
{
|
||||
WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelWrite failed!");
|
||||
|
@ -2,6 +2,8 @@
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
# Copyright 2024 Armin Novak <anovak@thincast.com>
|
||||
# Copyright 2024 Thincast Technologies GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -29,23 +31,13 @@ if(CHANNEL_STATIC_CLIENT_ENTRIES)
|
||||
list(REMOVE_DUPLICATES CHANNEL_STATIC_CLIENT_ENTRIES)
|
||||
endif()
|
||||
|
||||
set(CLIENT_STATIC_TYPEDEFS "#if __GNUC__\n")
|
||||
set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}#pragma GCC diagnostic push\n")
|
||||
set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"\n")
|
||||
set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}#endif\n")
|
||||
set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}typedef UINT (*static_entry_fkt)();\n")
|
||||
set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}typedef UINT (*static_addin_fkt)();\n")
|
||||
set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}#if __GNUC__\n")
|
||||
set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}#pragma GCC diagnostic pop\n")
|
||||
set(CLIENT_STATIC_TYPEDEFS "${CLIENT_STATIC_TYPEDEFS}#endif\n")
|
||||
|
||||
foreach(STATIC_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
|
||||
foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
|
||||
foreach(ENTRY ${${STATIC_MODULE}_CLIENT_ENTRY})
|
||||
if(${ENTRY} STREQUAL ${STATIC_ENTRY})
|
||||
set(STATIC_MODULE_NAME ${${STATIC_MODULE}_CLIENT_NAME})
|
||||
set(STATIC_MODULE_CHANNEL ${${STATIC_MODULE}_CLIENT_CHANNEL})
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${STATIC_MODULE_NAME})
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS ${STATIC_MODULE_NAME})
|
||||
|
||||
set(ENTRY_POINT_NAME "${STATIC_MODULE_CHANNEL}_${ENTRY}")
|
||||
if(${ENTRY} STREQUAL "VirtualChannelEntry")
|
||||
@ -53,32 +45,54 @@ foreach(STATIC_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
|
||||
elseif(${ENTRY} STREQUAL "VirtualChannelEntryEx")
|
||||
set(ENTRY_POINT_IMPORT "extern BOOL VCAPITYPE ${ENTRY_POINT_NAME}(PCHANNEL_ENTRY_POINTS,PVOID);")
|
||||
elseif(${ENTRY} MATCHES "DVCPluginEntry$")
|
||||
set(ENTRY_POINT_IMPORT "extern UINT ${ENTRY_POINT_NAME}(IDRDYNVC_ENTRY_POINTS* pEntryPoints);")
|
||||
set(ENTRY_POINT_IMPORT "extern UINT VCAPITYPE ${ENTRY_POINT_NAME}(IDRDYNVC_ENTRY_POINTS* pEntryPoints);")
|
||||
elseif(${ENTRY} MATCHES "DeviceServiceEntry$")
|
||||
set(ENTRY_POINT_IMPORT "extern UINT ${ENTRY_POINT_NAME}(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints);")
|
||||
set(ENTRY_POINT_IMPORT "extern UINT VCAPITYPE ${ENTRY_POINT_NAME}(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints);")
|
||||
else()
|
||||
set(ENTRY_POINT_IMPORT "extern UINT ${ENTRY_POINT_NAME}(void);")
|
||||
set(ENTRY_POINT_IMPORT "extern UINT VCAPITYPE ${ENTRY_POINT_NAME}(void);")
|
||||
endif()
|
||||
set(${STATIC_ENTRY}_IMPORTS "${${STATIC_ENTRY}_IMPORTS}\n${ENTRY_POINT_IMPORT}")
|
||||
set(${STATIC_ENTRY}_TABLE "${${STATIC_ENTRY}_TABLE}\n\t{ \"${STATIC_MODULE_CHANNEL}\", (static_entry_fkt)${ENTRY_POINT_NAME} },")
|
||||
|
||||
string(APPEND ${STATIC_ENTRY}_IMPORTS "\n${ENTRY_POINT_IMPORT}")
|
||||
string(APPEND ${STATIC_ENTRY}_TABLE "\n\t{ \"${STATIC_MODULE_CHANNEL}\", ${ENTRY_POINT_NAME} },")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
set(CLIENT_STATIC_ENTRY_TABLES_LIST "${CLIENT_STATIC_ENTRY_TABLES_LIST}\nextern const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[];\nconst STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[] =\n{")
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES_LIST "\nextern const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[];\n")
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES_LIST "const STATIC_ENTRY_TABLE CLIENT_STATIC_ENTRY_TABLES[] =\n{")
|
||||
|
||||
foreach(STATIC_ENTRY ${CHANNEL_STATIC_CLIENT_ENTRIES})
|
||||
set(CLIENT_STATIC_ENTRY_IMPORTS "${CLIENT_STATIC_ENTRY_IMPORTS}\n${${STATIC_ENTRY}_IMPORTS}")
|
||||
set(CLIENT_STATIC_ENTRY_TABLES "${CLIENT_STATIC_ENTRY_TABLES}\nextern const STATIC_ENTRY CLIENT_${STATIC_ENTRY}_TABLE[];\nconst STATIC_ENTRY CLIENT_${STATIC_ENTRY}_TABLE[] =\n{")
|
||||
set(CLIENT_STATIC_ENTRY_TABLES "${CLIENT_STATIC_ENTRY_TABLES}\n${${STATIC_ENTRY}_TABLE}")
|
||||
set(CLIENT_STATIC_ENTRY_TABLES "${CLIENT_STATIC_ENTRY_TABLES}\n\t{ NULL, NULL }\n};")
|
||||
set(CLIENT_STATIC_ENTRY_TABLES_LIST "${CLIENT_STATIC_ENTRY_TABLES_LIST}\n\t{ \"${STATIC_ENTRY}\", CLIENT_${STATIC_ENTRY}_TABLE },")
|
||||
if(${STATIC_ENTRY} STREQUAL "VirtualChannelEntry")
|
||||
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY_VC")
|
||||
set(CLIENT_STATIC_ENTRY_INITIALIZER ".csevc")
|
||||
elseif(${STATIC_ENTRY} STREQUAL "VirtualChannelEntryEx")
|
||||
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY_VCEX")
|
||||
set(CLIENT_STATIC_ENTRY_INITIALIZER ".csevcex")
|
||||
elseif(${STATIC_ENTRY} MATCHES "DVCPluginEntry$")
|
||||
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY_DVC")
|
||||
set(CLIENT_STATIC_ENTRY_INITIALIZER ".csedvc")
|
||||
elseif(${STATIC_ENTRY} MATCHES "DeviceServiceEntry$")
|
||||
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY_DSE")
|
||||
set(CLIENT_STATIC_ENTRY_INITIALIZER ".csedse")
|
||||
else()
|
||||
set(CLIENT_STATIC_ENTRY_TYPE "STATIC_ENTRY")
|
||||
set(CLIENT_STATIC_ENTRY_INITIALIZER ".cse")
|
||||
endif()
|
||||
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES "\nextern const ${CLIENT_STATIC_ENTRY_TYPE} CLIENT_${STATIC_ENTRY}_TABLE[];\n")
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES "const ${CLIENT_STATIC_ENTRY_TYPE} CLIENT_${STATIC_ENTRY}_TABLE[] =\n{")
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES "\n${${STATIC_ENTRY}_TABLE}")
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES "\n\t{ NULL, NULL }\n};")
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES_LIST "\n\t{ \"${STATIC_ENTRY}\", { ${CLIENT_STATIC_ENTRY_INITIALIZER} = CLIENT_${STATIC_ENTRY}_TABLE } },")
|
||||
endforeach()
|
||||
|
||||
set(CLIENT_STATIC_ENTRY_TABLES_LIST "${CLIENT_STATIC_ENTRY_TABLES_LIST}\n\t{ NULL, NULL }\n};")
|
||||
string(APPEND CLIENT_STATIC_ENTRY_TABLES_LIST "\n\t{ NULL, { .cse = NULL } }\n};")
|
||||
|
||||
set(CLIENT_STATIC_ADDIN_TABLE "extern const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[];\n")
|
||||
string(APPEND CLIENT_STATIC_ADDIN_TABLE "const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[] =\n{")
|
||||
|
||||
set(CLIENT_STATIC_ADDIN_TABLE "extern const STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[];\nconst STATIC_ADDIN_TABLE CLIENT_STATIC_ADDIN_TABLE[] =\n{")
|
||||
foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
|
||||
set(STATIC_MODULE_NAME ${${STATIC_MODULE}_CLIENT_NAME})
|
||||
set(STATIC_MODULE_CHANNEL ${${STATIC_MODULE}_CLIENT_CHANNEL})
|
||||
@ -98,26 +112,38 @@ foreach(STATIC_MODULE ${CHANNEL_STATIC_CLIENT_MODULES})
|
||||
endif()
|
||||
string(LENGTH "${STATIC_SUBSYSTEM_TYPE}" _type_length)
|
||||
set(SUBSYSTEM_MODULE_NAME "${STATIC_MODULE_NAME}-${STATIC_SUBSYSTEM}")
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${SUBSYSTEM_MODULE_NAME})
|
||||
list(APPEND ${MODULE_PREFIX}_LIBS ${SUBSYSTEM_MODULE_NAME})
|
||||
if(_type_length GREATER 0)
|
||||
set(STATIC_SUBSYSTEM_ENTRY "${STATIC_SUBSYSTEM_NAME}_freerdp_${STATIC_MODULE_CHANNEL}_client_${STATIC_SUBSYSTEM_TYPE}_subsystem_entry")
|
||||
else()
|
||||
set(STATIC_SUBSYSTEM_ENTRY "${STATIC_SUBSYSTEM_NAME}_freerdp_${STATIC_MODULE_CHANNEL}_client_subsystem_entry")
|
||||
endif()
|
||||
set(SUBSYSTEM_TABLE "${SUBSYSTEM_TABLE}\n\t{ \"${STATIC_SUBSYSTEM_NAME}\", \"${STATIC_SUBSYSTEM_TYPE}\", ${STATIC_SUBSYSTEM_ENTRY} },")
|
||||
set(SUBSYSTEM_IMPORT "extern UINT ${STATIC_SUBSYSTEM_ENTRY}(void*);")
|
||||
set(CLIENT_STATIC_SUBSYSTEM_IMPORTS "${CLIENT_STATIC_SUBSYSTEM_IMPORTS}\n${SUBSYSTEM_IMPORT}")
|
||||
string(APPEND SUBSYSTEM_TABLE "\n\t{ \"${STATIC_SUBSYSTEM_NAME}\", \"${STATIC_SUBSYSTEM_TYPE}\", ${STATIC_SUBSYSTEM_ENTRY} },")
|
||||
set(SUBSYSTEM_IMPORT "extern UINT VCAPITYPE ${STATIC_SUBSYSTEM_ENTRY}(void*);")
|
||||
string(APPEND CLIENT_STATIC_SUBSYSTEM_IMPORTS "\n${SUBSYSTEM_IMPORT}")
|
||||
endforeach()
|
||||
set(SUBSYSTEM_TABLE "${SUBSYSTEM_TABLE}\n\t{ NULL, NULL, NULL }\n};")
|
||||
set(CLIENT_STATIC_SUBSYSTEM_TABLES "${CLIENT_STATIC_SUBSYSTEM_TABLES}\n${SUBSYSTEM_TABLE}")
|
||||
string(APPEND SUBSYSTEM_TABLE "\n\t{ NULL, NULL, NULL }\n};")
|
||||
string(APPEND CLIENT_STATIC_SUBSYSTEM_TABLES "\n${SUBSYSTEM_TABLE}")
|
||||
|
||||
foreach(ENTRY ${${STATIC_MODULE}_CLIENT_ENTRY})
|
||||
set (ENTRY_POINT_NAME ${STATIC_MODULE_CHANNEL}_${ENTRY})
|
||||
set(CLIENT_STATIC_ADDIN_TABLE "${CLIENT_STATIC_ADDIN_TABLE}\n\t{ \"${STATIC_MODULE_CHANNEL}\", \"${ENTRY}\", (static_addin_fkt)${ENTRY_POINT_NAME}, ${SUBSYSTEM_TABLE_NAME} },")
|
||||
if(${ENTRY} STREQUAL "VirtualChannelEntry")
|
||||
set(ENTRY_INITIALIZER ".csevc")
|
||||
elseif(${ENTRY} STREQUAL "VirtualChannelEntryEx")
|
||||
set(ENTRY_INITIALIZER ".csevcex")
|
||||
elseif(${ENTRY} MATCHES "DVCPluginEntry$")
|
||||
set(ENTRY_INITIALIZER ".csedvc")
|
||||
elseif(${ENTRY} MATCHES "DeviceServiceEntry$")
|
||||
set(ENTRY_INITIALIZER ".csedse")
|
||||
else()
|
||||
set(ENTRY_INITIALIZER ".cse")
|
||||
endif()
|
||||
string(APPEND CLIENT_STATIC_ADDIN_TABLE "\n\t{ \"${STATIC_MODULE_CHANNEL}\", \"${ENTRY}\", { ${ENTRY_INITIALIZER} = ${ENTRY_POINT_NAME} }, ${SUBSYSTEM_TABLE_NAME} },")
|
||||
endforeach()
|
||||
endforeach()
|
||||
set(CLIENT_STATIC_ADDIN_TABLE "${CLIENT_STATIC_ADDIN_TABLE}\n\t{ NULL, NULL, NULL, NULL }\n};")
|
||||
string(APPEND CLIENT_STATIC_ADDIN_TABLE "\n\t{ NULL, NULL, { .cse = NULL }, NULL }\n};")
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tables.c.in ${CMAKE_CURRENT_BINARY_DIR}/tables.c)
|
||||
cleaning_configure_file(${CMAKE_CURRENT_SOURCE_DIR}/tables.c.in ${CMAKE_CURRENT_BINARY_DIR}/tables.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp winpr)
|
||||
|
||||
|
@ -48,16 +48,15 @@ static void* freerdp_channels_find_static_entry_in_table(const STATIC_ENTRY_TABL
|
||||
const char* identifier)
|
||||
{
|
||||
size_t index = 0;
|
||||
const STATIC_ENTRY* pEntry = (const STATIC_ENTRY*)&table->table[index++];
|
||||
const STATIC_ENTRY* pEntry = &table->table.cse[index++];
|
||||
|
||||
while (pEntry->entry != NULL)
|
||||
{
|
||||
static_entry_fn_t fkt = pEntry->entry;
|
||||
if (strcmp(pEntry->name, identifier) == 0)
|
||||
{
|
||||
return (void*)pEntry->entry;
|
||||
}
|
||||
return WINPR_FUNC_PTR_CAST(fkt, void*);
|
||||
|
||||
pEntry = (const STATIC_ENTRY*)&table->table[index++];
|
||||
pEntry = &table->table.cse[index++];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@ -68,7 +67,7 @@ void* freerdp_channels_client_find_static_entry(const char* name, const char* id
|
||||
size_t index = 0;
|
||||
const STATIC_ENTRY_TABLE* pEntry = &CLIENT_STATIC_ENTRY_TABLES[index++];
|
||||
|
||||
while (pEntry->table != NULL)
|
||||
while (pEntry->table.cse != NULL)
|
||||
{
|
||||
if (strcmp(pEntry->name, name) == 0)
|
||||
{
|
||||
@ -111,7 +110,7 @@ static FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPCSTR pszName
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", table->name);
|
||||
(void)sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", table->name);
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
@ -128,8 +127,9 @@ static FREERDP_ADDIN** freerdp_channels_list_client_static_addins(LPCSTR pszName
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", table->name);
|
||||
sprintf_s(pAddin->cSubsystem, ARRAYSIZE(pAddin->cSubsystem), "%s", subsystems[j].name);
|
||||
(void)sprintf_s(pAddin->cName, ARRAYSIZE(pAddin->cName), "%s", table->name);
|
||||
(void)sprintf_s(pAddin->cSubsystem, ARRAYSIZE(pAddin->cSubsystem), "%s",
|
||||
subsystems[j].name);
|
||||
pAddin->dwFlags = FREERDP_ADDIN_CLIENT;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_STATIC;
|
||||
pAddin->dwFlags |= FREERDP_ADDIN_NAME;
|
||||
@ -190,23 +190,23 @@ static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCS
|
||||
|
||||
if (pszName && pszSubsystem && pszType)
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-%s-%s.%s",
|
||||
pszName, pszSubsystem, pszType, pszExtension);
|
||||
(void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-%s-%s.%s",
|
||||
pszName, pszSubsystem, pszType, pszExtension);
|
||||
}
|
||||
else if (pszName && pszType)
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-?-%s.%s",
|
||||
pszName, pszType, pszExtension);
|
||||
(void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client-?-%s.%s",
|
||||
pszName, pszType, pszExtension);
|
||||
}
|
||||
else if (pszName)
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client*.%s", pszName,
|
||||
pszExtension);
|
||||
(void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "%s-client*.%s",
|
||||
pszName, pszExtension);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "?-client*.%s",
|
||||
pszExtension);
|
||||
(void)sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX "?-client*.%s",
|
||||
pszExtension);
|
||||
}
|
||||
|
||||
cchPattern = strnlen(pszPattern, cchPattern);
|
||||
@ -473,7 +473,7 @@ PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LP
|
||||
{
|
||||
if (strncmp(table->name, pszName, MAX_PATH) == 0)
|
||||
{
|
||||
if (type && strncmp(table->type, type, MAX_PATH))
|
||||
if (type && (strncmp(table->type, type, MAX_PATH) != 0))
|
||||
continue;
|
||||
|
||||
if (pszSubsystem != NULL)
|
||||
@ -487,15 +487,15 @@ PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LP
|
||||
0) || /* we only want to know if strnlen is > 0 */
|
||||
(strncmp(subsystems->name, pszSubsystem, MAX_PATH) == 0))
|
||||
{
|
||||
static_subsystem_entry_fn_t fkt = subsystems->entry;
|
||||
|
||||
if (pszType)
|
||||
{
|
||||
if (strncmp(subsystems->type, pszType, MAX_PATH) == 0)
|
||||
return (PVIRTUALCHANNELENTRY)subsystems->entry;
|
||||
return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (PVIRTUALCHANNELENTRY)subsystems->entry;
|
||||
}
|
||||
return WINPR_FUNC_PTR_CAST(fkt, PVIRTUALCHANNELENTRY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -507,7 +507,7 @@ PVIRTUALCHANNELENTRY freerdp_channels_load_static_addin_entry(LPCSTR pszName, LP
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (PVIRTUALCHANNELENTRY)table->entry;
|
||||
return table->entry.csevc;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -567,10 +567,10 @@ static DWORD WINAPI channel_client_thread_proc(LPVOID userdata)
|
||||
if (error && internals->ctx)
|
||||
{
|
||||
char msg[128];
|
||||
_snprintf(msg, 127,
|
||||
"%s_virtual_channel_client_thread reported an"
|
||||
" error",
|
||||
internals->channel_name);
|
||||
(void)_snprintf(msg, 127,
|
||||
"%s_virtual_channel_client_thread reported an"
|
||||
" error",
|
||||
internals->channel_name);
|
||||
setChannelError(internals->ctx, error, msg);
|
||||
}
|
||||
ExitThread(error);
|
||||
@ -594,7 +594,7 @@ static void channel_client_handler_free(msg_proc_internals* internals)
|
||||
return;
|
||||
|
||||
if (internals->thread)
|
||||
CloseHandle(internals->thread);
|
||||
(void)CloseHandle(internals->thread);
|
||||
MessageQueue_Free(internals->queue);
|
||||
Stream_Free(internals->data_in, TRUE);
|
||||
free(internals->channel_name);
|
||||
@ -696,7 +696,8 @@ UINT channel_client_post_message(void* MsgsHandle, LPVOID pData, UINT32 dataLeng
|
||||
if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
|
||||
{
|
||||
char msg[128];
|
||||
_snprintf(msg, 127, "%s_plugin_process_received: read error", internals->channel_name);
|
||||
(void)_snprintf(msg, 127, "%s_plugin_process_received: read error",
|
||||
internals->channel_name);
|
||||
WLog_ERR(TAG, msg);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
@ -25,9 +25,9 @@
|
||||
|
||||
${CLIENT_STATIC_TYPEDEFS}
|
||||
${CLIENT_STATIC_ENTRY_IMPORTS}
|
||||
${CLIENT_STATIC_SUBSYSTEM_IMPORTS}
|
||||
${CLIENT_STATIC_ENTRY_TABLES}
|
||||
${CLIENT_STATIC_ENTRY_TABLES_LIST}
|
||||
${CLIENT_STATIC_SUBSYSTEM_IMPORTS}
|
||||
${CLIENT_STATIC_SUBSYSTEM_TABLES}
|
||||
${CLIENT_STATIC_ADDIN_TABLE}
|
||||
|
||||
|
@ -18,36 +18,87 @@
|
||||
*/
|
||||
|
||||
#include <winpr/platform.h>
|
||||
#include <winpr/wtsapi.h>
|
||||
#include <freerdp/svc.h>
|
||||
#include <freerdp/dvc.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
|
||||
/* The 'entry' function pointers have variable arguments. */
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_STRICT_PROTOTYPES
|
||||
|
||||
typedef UINT(VCAPITYPE* static_entry_fn_t)();
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
UINT (*entry)();
|
||||
static_entry_fn_t entry;
|
||||
} STATIC_ENTRY;
|
||||
|
||||
typedef BOOL(VCAPITYPE* static_entry_vc_fn_t)(PCHANNEL_ENTRY_POINTS);
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
const STATIC_ENTRY* table;
|
||||
static_entry_vc_fn_t entry;
|
||||
} STATIC_ENTRY_VC;
|
||||
|
||||
typedef BOOL(VCAPITYPE* static_entry_vcex_fn_t)(PCHANNEL_ENTRY_POINTS, PVOID);
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
static_entry_vcex_fn_t entry;
|
||||
} STATIC_ENTRY_VCEX;
|
||||
|
||||
typedef UINT(VCAPITYPE* static_entry_dvc_fn_t)(IDRDYNVC_ENTRY_POINTS*);
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
static_entry_dvc_fn_t entry;
|
||||
} STATIC_ENTRY_DVC;
|
||||
|
||||
typedef UINT(VCAPITYPE* static_entry_dse_fn_t)(PDEVICE_SERVICE_ENTRY_POINTS);
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
static_entry_dse_fn_t entry;
|
||||
} STATIC_ENTRY_DSE;
|
||||
|
||||
typedef union
|
||||
{
|
||||
const STATIC_ENTRY* cse;
|
||||
const STATIC_ENTRY_VC* csevc;
|
||||
const STATIC_ENTRY_VCEX* csevcex;
|
||||
const STATIC_ENTRY_DVC* csedvc;
|
||||
const STATIC_ENTRY_DSE* csedse;
|
||||
} static_entry_u;
|
||||
|
||||
typedef union
|
||||
{
|
||||
static_entry_fn_t cse;
|
||||
static_entry_vc_fn_t csevc;
|
||||
static_entry_vcex_fn_t csevcex;
|
||||
static_entry_dvc_fn_t csedvc;
|
||||
static_entry_dse_fn_t csedse;
|
||||
} static_entry_fn_u;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
static_entry_u table;
|
||||
} STATIC_ENTRY_TABLE;
|
||||
|
||||
typedef UINT(VCAPITYPE* static_subsystem_entry_fn_t)(void*);
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
const char* type;
|
||||
UINT (*entry)();
|
||||
static_subsystem_entry_fn_t entry;
|
||||
} STATIC_SUBSYSTEM_ENTRY;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
const char* type;
|
||||
UINT (*entry)();
|
||||
static_entry_fn_u entry;
|
||||
const STATIC_SUBSYSTEM_ENTRY* table;
|
||||
} STATIC_ADDIN_TABLE;
|
||||
|
||||
|
@ -98,7 +98,8 @@ CLIPRDR_FORMAT_LIST cliprdr_filter_format_list(const CLIPRDR_FORMAT_LIST* list,
|
||||
}
|
||||
}
|
||||
}
|
||||
filtered.numFormats = wpos;
|
||||
WINPR_ASSERT(wpos <= UINT32_MAX);
|
||||
filtered.numFormats = (UINT32)wpos;
|
||||
return filtered;
|
||||
}
|
||||
|
||||
|
@ -38,8 +38,8 @@
|
||||
#include "cliprdr_format.h"
|
||||
#include "../cliprdr_common.h"
|
||||
|
||||
const char* type_FileGroupDescriptorW = "FileGroupDescriptorW";
|
||||
const char* type_FileContents = "FileContents";
|
||||
const char type_FileGroupDescriptorW[] = "FileGroupDescriptorW";
|
||||
const char type_FileContents[] = "FileContents";
|
||||
|
||||
CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr)
|
||||
{
|
||||
@ -59,17 +59,17 @@ CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr)
|
||||
*/
|
||||
static UINT cliprdr_packet_send(cliprdrPlugin* cliprdr, wStream* s)
|
||||
{
|
||||
size_t pos = 0;
|
||||
UINT32 dataLen = 0;
|
||||
UINT status = CHANNEL_RC_OK;
|
||||
|
||||
WINPR_ASSERT(cliprdr);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
pos = Stream_GetPosition(s);
|
||||
dataLen = pos - 8;
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
const size_t dataLen = pos - 8;
|
||||
WINPR_ASSERT(dataLen <= UINT32_MAX);
|
||||
|
||||
Stream_SetPosition(s, 4);
|
||||
Stream_Write_UINT32(s, dataLen);
|
||||
Stream_Write_UINT32(s, (UINT32)dataLen);
|
||||
Stream_SetPosition(s, pos);
|
||||
|
||||
WLog_DBG(TAG, "Cliprdr Sending (%" PRIu32 " bytes)", dataLen + 8);
|
||||
@ -677,7 +677,7 @@ static UINT cliprdr_client_format_list(CliprdrClientContext* context,
|
||||
}
|
||||
cliprdr->initialFormatListSent = TRUE;
|
||||
|
||||
s = cliprdr_packet_format_list_new(&filterList, cliprdr->useLongFormatNames);
|
||||
s = cliprdr_packet_format_list_new(&filterList, cliprdr->useLongFormatNames, FALSE);
|
||||
cliprdr_free_format_list(&filterList);
|
||||
|
||||
if (!s)
|
||||
@ -790,13 +790,10 @@ cliprdr_client_unlock_clipboard_data(CliprdrClientContext* context,
|
||||
static UINT cliprdr_client_format_data_request(CliprdrClientContext* context,
|
||||
const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
|
||||
{
|
||||
wStream* s = NULL;
|
||||
cliprdrPlugin* cliprdr = NULL;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(formatDataRequest);
|
||||
|
||||
cliprdr = (cliprdrPlugin*)context->handle;
|
||||
cliprdrPlugin* cliprdr = (cliprdrPlugin*)context->handle;
|
||||
WINPR_ASSERT(cliprdr);
|
||||
|
||||
const UINT32 mask =
|
||||
@ -806,8 +803,8 @@ static UINT cliprdr_client_format_data_request(CliprdrClientContext* context,
|
||||
WLog_WARN(TAG, "remote -> local copy disabled, ignoring request");
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
s = cliprdr_packet_new(CB_FORMAT_DATA_REQUEST, 0, 4);
|
||||
|
||||
wStream* s = cliprdr_packet_new(CB_FORMAT_DATA_REQUEST, 0, 4);
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "cliprdr_packet_new failed!");
|
||||
@ -815,7 +812,8 @@ static UINT cliprdr_client_format_data_request(CliprdrClientContext* context,
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(s, formatDataRequest->requestedFormatId); /* requestedFormatId (4 bytes) */
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientFormatDataRequest");
|
||||
WLog_Print(cliprdr->log, WLOG_DEBUG, "ClientFormatDataRequest(0x%08" PRIx32 ")",
|
||||
formatDataRequest->requestedFormatId);
|
||||
return cliprdr_packet_send(cliprdr, s);
|
||||
}
|
||||
|
||||
@ -828,21 +826,18 @@ static UINT
|
||||
cliprdr_client_format_data_response(CliprdrClientContext* context,
|
||||
const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
|
||||
{
|
||||
wStream* s = NULL;
|
||||
cliprdrPlugin* cliprdr = NULL;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(formatDataResponse);
|
||||
|
||||
cliprdr = (cliprdrPlugin*)context->handle;
|
||||
cliprdrPlugin* cliprdr = (cliprdrPlugin*)context->handle;
|
||||
WINPR_ASSERT(cliprdr);
|
||||
|
||||
const UINT32 mask =
|
||||
freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask);
|
||||
WINPR_ASSERT((mask & (CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES)) != 0);
|
||||
WINPR_ASSERT(
|
||||
(freerdp_settings_get_uint32(context->rdpcontext->settings, FreeRDP_ClipboardFeatureMask) &
|
||||
(CLIPRDR_FLAG_LOCAL_TO_REMOTE | CLIPRDR_FLAG_LOCAL_TO_REMOTE_FILES)) != 0);
|
||||
|
||||
s = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, formatDataResponse->common.msgFlags,
|
||||
formatDataResponse->common.dataLen);
|
||||
wStream* s = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, formatDataResponse->common.msgFlags,
|
||||
formatDataResponse->common.dataLen);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
@ -972,6 +967,8 @@ static VOID VCAPITYPE cliprdr_virtual_channel_open_event_ex(LPVOID lpUserParam,
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (error && cliprdr && cliprdr->context->rdpcontext)
|
||||
@ -1091,7 +1088,8 @@ static VOID VCAPITYPE cliprdr_virtual_channel_init_event_ex(LPVOID lpUserParam,
|
||||
WLog_ERR(TAG,
|
||||
"cliprdr_virtual_channel_event_terminated failed with error %" PRIu32 "!",
|
||||
error);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1120,8 +1118,8 @@ FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS p
|
||||
|
||||
cliprdr->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||
sprintf_s(cliprdr->channelDef.name, ARRAYSIZE(cliprdr->channelDef.name),
|
||||
CLIPRDR_SVC_CHANNEL_NAME);
|
||||
(void)sprintf_s(cliprdr->channelDef.name, ARRAYSIZE(cliprdr->channelDef.name),
|
||||
CLIPRDR_SVC_CHANNEL_NAME);
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
|
||||
WINPR_ASSERT(pEntryPointsEx);
|
||||
|
||||
|
@ -55,7 +55,7 @@ typedef struct
|
||||
CliprdrClientContext* cliprdr_get_client_interface(cliprdrPlugin* cliprdr);
|
||||
UINT cliprdr_send_error_response(cliprdrPlugin* cliprdr, UINT16 type);
|
||||
|
||||
extern const char* type_FileGroupDescriptorW;
|
||||
extern const char* type_FileContents;
|
||||
extern const char type_FileGroupDescriptorW[];
|
||||
extern const char type_FileContents[];
|
||||
|
||||
#endif /* FREERDP_CHANNEL_CLIPRDR_CLIENT_MAIN_H */
|
||||
|
@ -32,6 +32,8 @@ static const char* CB_MSG_TYPE_STR(UINT32 type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CB_TYPE_NONE:
|
||||
return "CB_TYPE_NONE";
|
||||
case CB_MONITOR_READY:
|
||||
return "CB_MONITOR_READY";
|
||||
case CB_FORMAT_LIST:
|
||||
@ -61,7 +63,7 @@ static const char* CB_MSG_TYPE_STR(UINT32 type)
|
||||
|
||||
const char* CB_MSG_TYPE_STRING(UINT16 type, char* buffer, size_t size)
|
||||
{
|
||||
_snprintf(buffer, size, "%s [0x%04" PRIx16 "]", CB_MSG_TYPE_STR(type), type);
|
||||
(void)_snprintf(buffer, size, "%s [0x%04" PRIx16 "]", CB_MSG_TYPE_STR(type), type);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@ -75,16 +77,11 @@ const char* CB_MSG_FLAGS_STRING(UINT16 msgFlags, char* buffer, size_t size)
|
||||
winpr_str_append("CB_ASCII_NAMES", buffer, size, "|");
|
||||
|
||||
const size_t len = strnlen(buffer, size);
|
||||
if (len > 0)
|
||||
{
|
||||
/* remove trailing | */
|
||||
buffer[len - 1] = '\0';
|
||||
}
|
||||
else
|
||||
if (!len)
|
||||
winpr_str_append("NONE", buffer, size, "");
|
||||
|
||||
char val[32] = { 0 };
|
||||
_snprintf(val, sizeof(val), "[0x%04" PRIx16 "]", msgFlags);
|
||||
(void)_snprintf(val, sizeof(val), "[0x%04" PRIx16 "]", msgFlags);
|
||||
winpr_str_append(val, buffer, size, "|");
|
||||
return buffer;
|
||||
}
|
||||
@ -242,141 +239,76 @@ wStream* cliprdr_packet_file_contents_response_new(const CLIPRDR_FILE_CONTENTS_R
|
||||
}
|
||||
|
||||
wStream* cliprdr_packet_format_list_new(const CLIPRDR_FORMAT_LIST* formatList,
|
||||
BOOL useLongFormatNames)
|
||||
BOOL useLongFormatNames, BOOL useAsciiNames)
|
||||
{
|
||||
wStream* s = NULL;
|
||||
size_t formatNameSize = 0;
|
||||
char* szFormatName = NULL;
|
||||
WCHAR* wszFormatName = NULL;
|
||||
BOOL asciiNames = FALSE;
|
||||
CLIPRDR_FORMAT* format = NULL;
|
||||
UINT32 length = 0;
|
||||
WINPR_ASSERT(formatList);
|
||||
|
||||
if (formatList->common.msgType != CB_FORMAT_LIST)
|
||||
WLog_WARN(TAG, "called with invalid type %08" PRIx32, formatList->common.msgType);
|
||||
|
||||
if (!useLongFormatNames)
|
||||
if (useLongFormatNames && useAsciiNames)
|
||||
WLog_WARN(TAG, "called with invalid arguments useLongFormatNames=true && "
|
||||
"useAsciiNames=true. useAsciiNames requires "
|
||||
"useLongFormatNames=false, ignoring argument.");
|
||||
|
||||
const UINT32 length = formatList->numFormats * 36;
|
||||
const size_t formatNameCharSize =
|
||||
(useLongFormatNames || !useAsciiNames) ? sizeof(WCHAR) : sizeof(CHAR);
|
||||
|
||||
wStream* s = cliprdr_packet_new(CB_FORMAT_LIST, 0, length);
|
||||
if (!s)
|
||||
{
|
||||
length = formatList->numFormats * 36;
|
||||
s = cliprdr_packet_new(CB_FORMAT_LIST, 0, length);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "cliprdr_packet_new failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (UINT32 index = 0; index < formatList->numFormats; index++)
|
||||
{
|
||||
size_t formatNameLength = 0;
|
||||
format = (CLIPRDR_FORMAT*)&(formatList->formats[index]);
|
||||
Stream_Write_UINT32(s, format->formatId); /* formatId (4 bytes) */
|
||||
formatNameSize = 0;
|
||||
|
||||
szFormatName = format->formatName;
|
||||
|
||||
if (asciiNames)
|
||||
{
|
||||
if (szFormatName)
|
||||
formatNameLength = strnlen(szFormatName, 32);
|
||||
|
||||
if (formatNameLength > 31)
|
||||
formatNameLength = 31;
|
||||
|
||||
Stream_Write(s, szFormatName, formatNameLength);
|
||||
Stream_Zero(s, 32 - formatNameLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
wszFormatName = NULL;
|
||||
|
||||
if (szFormatName)
|
||||
{
|
||||
wszFormatName = ConvertUtf8ToWCharAlloc(szFormatName, &formatNameSize);
|
||||
|
||||
if (!wszFormatName)
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
formatNameSize += 1; /* append terminating '\0' */
|
||||
}
|
||||
|
||||
if (formatNameSize > 15)
|
||||
formatNameSize = 15;
|
||||
|
||||
/* size in bytes instead of wchar */
|
||||
formatNameSize *= sizeof(WCHAR);
|
||||
|
||||
if (wszFormatName)
|
||||
Stream_Write(s, wszFormatName, (size_t)formatNameSize);
|
||||
|
||||
Stream_Zero(s, (size_t)(32 - formatNameSize));
|
||||
free(wszFormatName);
|
||||
}
|
||||
}
|
||||
WLog_ERR(TAG, "cliprdr_packet_new failed!");
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
|
||||
for (UINT32 index = 0; index < formatList->numFormats; index++)
|
||||
{
|
||||
length = 0;
|
||||
for (UINT32 index = 0; index < formatList->numFormats; index++)
|
||||
const CLIPRDR_FORMAT* format = &(formatList->formats[index]);
|
||||
|
||||
const char* szFormatName = format->formatName;
|
||||
size_t formatNameLength = 0;
|
||||
if (szFormatName)
|
||||
formatNameLength = strlen(szFormatName);
|
||||
|
||||
size_t formatNameMaxLength = formatNameLength + 1; /* Ensure '\0' termination in output */
|
||||
if (!Stream_EnsureRemainingCapacity(s,
|
||||
4 + MAX(32, formatNameMaxLength * formatNameCharSize)))
|
||||
goto fail;
|
||||
|
||||
Stream_Write_UINT32(s, format->formatId); /* formatId (4 bytes) */
|
||||
|
||||
if (!useLongFormatNames)
|
||||
{
|
||||
format = (CLIPRDR_FORMAT*)&(formatList->formats[index]);
|
||||
length += 4;
|
||||
formatNameSize = sizeof(WCHAR);
|
||||
|
||||
if (format->formatName)
|
||||
{
|
||||
SSIZE_T size = ConvertUtf8ToWChar(format->formatName, NULL, 0);
|
||||
if (size < 0)
|
||||
return NULL;
|
||||
formatNameSize = (size_t)(size + 1) * sizeof(WCHAR);
|
||||
}
|
||||
|
||||
length += (UINT32)formatNameSize;
|
||||
formatNameMaxLength = useAsciiNames ? 32 : 16;
|
||||
formatNameLength = MIN(formatNameMaxLength - 1, formatNameLength);
|
||||
}
|
||||
|
||||
s = cliprdr_packet_new(CB_FORMAT_LIST, 0, length);
|
||||
|
||||
if (!s)
|
||||
if (szFormatName && (formatNameLength > 0))
|
||||
{
|
||||
WLog_ERR(TAG, "cliprdr_packet_new failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (UINT32 index = 0; index < formatList->numFormats; index++)
|
||||
{
|
||||
format = (CLIPRDR_FORMAT*)&(formatList->formats[index]);
|
||||
Stream_Write_UINT32(s, format->formatId); /* formatId (4 bytes) */
|
||||
|
||||
if (format->formatName)
|
||||
if (useAsciiNames)
|
||||
{
|
||||
const size_t cap = Stream_Capacity(s);
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
const size_t rem = cap - pos;
|
||||
if ((cap < pos) || ((rem / 2) > INT_MAX))
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const size_t len = strnlen(format->formatName, rem / sizeof(WCHAR));
|
||||
if (Stream_Write_UTF16_String_From_UTF8(s, len + 1, format->formatName, len, TRUE) <
|
||||
0)
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
Stream_Write(s, szFormatName, formatNameLength);
|
||||
Stream_Zero(s, formatNameMaxLength - formatNameLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream_Write_UINT16(s, 0);
|
||||
if (Stream_Write_UTF16_String_From_UTF8(s, formatNameMaxLength, szFormatName,
|
||||
formatNameLength, TRUE) < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
Stream_Zero(s, formatNameMaxLength * formatNameCharSize);
|
||||
}
|
||||
|
||||
return s;
|
||||
|
||||
fail:
|
||||
Stream_Free(s, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UINT cliprdr_read_unlock_clipdata(wStream* s, CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
@ -473,14 +405,13 @@ UINT cliprdr_read_format_list(wStream* s, CLIPRDR_FORMAT_LIST* formatList, BOOL
|
||||
}
|
||||
else if (!useLongFormatNames)
|
||||
{
|
||||
const size_t cap = Stream_Capacity(sub1);
|
||||
formatList->numFormats = (cap / 36);
|
||||
|
||||
if ((formatList->numFormats * 36) != cap)
|
||||
const size_t cap = Stream_Capacity(sub1) / 36ULL;
|
||||
if (cap > UINT32_MAX)
|
||||
{
|
||||
WLog_ERR(TAG, "Invalid short format list length: %" PRIuz "", cap);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
formatList->numFormats = (UINT32)cap;
|
||||
|
||||
if (formatList->numFormats)
|
||||
formats = (CLIPRDR_FORMAT*)calloc(formatList->numFormats, sizeof(CLIPRDR_FORMAT));
|
||||
|
@ -42,7 +42,7 @@ cliprdr_packet_file_contents_request_new(const CLIPRDR_FILE_CONTENTS_REQUEST* re
|
||||
FREERDP_LOCAL wStream*
|
||||
cliprdr_packet_file_contents_response_new(const CLIPRDR_FILE_CONTENTS_RESPONSE* response);
|
||||
FREERDP_LOCAL wStream* cliprdr_packet_format_list_new(const CLIPRDR_FORMAT_LIST* formatList,
|
||||
BOOL useLongFormatNames);
|
||||
BOOL useLongFormatNames, BOOL useAsciiNames);
|
||||
|
||||
FREERDP_LOCAL UINT cliprdr_read_lock_clipdata(wStream* s,
|
||||
CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData);
|
||||
|
@ -96,13 +96,9 @@ static UINT cliprdr_server_packet_send(CliprdrServerPrivate* cliprdr, wStream* s
|
||||
dataLen = (UINT32)(pos - 8);
|
||||
Stream_SetPosition(s, 4);
|
||||
Stream_Write_UINT32(s, dataLen);
|
||||
if (pos > UINT32_MAX)
|
||||
{
|
||||
rc = ERROR_INVALID_DATA;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
status = WTSVirtualChannelWrite(cliprdr->ChannelHandle, (PCHAR)Stream_Buffer(s), (UINT32)pos,
|
||||
WINPR_ASSERT(pos <= UINT32_MAX);
|
||||
status = WTSVirtualChannelWrite(cliprdr->ChannelHandle, Stream_BufferAs(s, char), (UINT32)pos,
|
||||
&written);
|
||||
rc = status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
fail:
|
||||
@ -230,7 +226,7 @@ static UINT cliprdr_server_format_list(CliprdrServerContext* context,
|
||||
|
||||
cliprdr = (CliprdrServerPrivate*)context->handle;
|
||||
|
||||
s = cliprdr_packet_format_list_new(formatList, context->useLongFormatNames);
|
||||
s = cliprdr_packet_format_list_new(formatList, context->useLongFormatNames, FALSE);
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "cliprdr_packet_format_list_new failed!");
|
||||
@ -1355,7 +1351,7 @@ static UINT cliprdr_server_close(CliprdrServerContext* context)
|
||||
|
||||
if (cliprdr->ChannelHandle)
|
||||
{
|
||||
WTSVirtualChannelClose(cliprdr->ChannelHandle);
|
||||
(void)WTSVirtualChannelClose(cliprdr->ChannelHandle);
|
||||
cliprdr->ChannelHandle = NULL;
|
||||
}
|
||||
|
||||
@ -1395,7 +1391,7 @@ static UINT cliprdr_server_start(CliprdrServerContext* context)
|
||||
if (!(cliprdr->Thread = CreateThread(NULL, 0, cliprdr_server_thread, (void*)context, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(cliprdr->StopEvent);
|
||||
(void)CloseHandle(cliprdr->StopEvent);
|
||||
cliprdr->StopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -1420,7 +1416,7 @@ static UINT cliprdr_server_stop(CliprdrServerContext* context)
|
||||
|
||||
if (cliprdr->StopEvent)
|
||||
{
|
||||
SetEvent(cliprdr->StopEvent);
|
||||
(void)SetEvent(cliprdr->StopEvent);
|
||||
|
||||
if (WaitForSingleObject(cliprdr->Thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -1429,8 +1425,8 @@ static UINT cliprdr_server_stop(CliprdrServerContext* context)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(cliprdr->Thread);
|
||||
CloseHandle(cliprdr->StopEvent);
|
||||
(void)CloseHandle(cliprdr->Thread);
|
||||
(void)CloseHandle(cliprdr->StopEvent);
|
||||
}
|
||||
|
||||
if (cliprdr->ChannelHandle)
|
||||
|
@ -119,8 +119,8 @@ disp_send_display_control_monitor_layout_pdu(GENERIC_CHANNEL_CALLBACK* callback,
|
||||
current.Height = 8192;
|
||||
|
||||
Stream_Write_UINT32(s, current.Flags); /* Flags (4 bytes) */
|
||||
Stream_Write_UINT32(s, current.Left); /* Left (4 bytes) */
|
||||
Stream_Write_UINT32(s, current.Top); /* Top (4 bytes) */
|
||||
Stream_Write_INT32(s, current.Left); /* Left (4 bytes) */
|
||||
Stream_Write_INT32(s, current.Top); /* Top (4 bytes) */
|
||||
Stream_Write_UINT32(s, current.Width); /* Width (4 bytes) */
|
||||
Stream_Write_UINT32(s, current.Height); /* Height (4 bytes) */
|
||||
Stream_Write_UINT32(s, current.PhysicalWidth); /* PhysicalWidth (4 bytes) */
|
||||
@ -314,7 +314,7 @@ static const IWTSVirtualChannelCallback disp_callbacks = { disp_on_data_received
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT disp_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE disp_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, DISP_DVC_CHANNEL_NAME,
|
||||
sizeof(DISP_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
|
||||
|
@ -311,7 +311,11 @@ static UINT disp_server_handle_messages(DispServerContext* context)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(priv->disp_channel, 0, (PCHAR)Stream_Buffer(s), Stream_Capacity(s),
|
||||
const size_t cap = Stream_Capacity(s);
|
||||
if (cap > UINT32_MAX)
|
||||
return CHANNEL_RC_NO_BUFFER;
|
||||
|
||||
if (WTSVirtualChannelRead(priv->disp_channel, 0, Stream_BufferAs(s, char), (ULONG)cap,
|
||||
&BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
@ -412,8 +416,8 @@ static UINT disp_server_open(DispServerContext* context)
|
||||
|
||||
priv->SessionId = (DWORD)*pSessionId;
|
||||
WTSFreeMemory(pSessionId);
|
||||
priv->disp_channel = (HANDLE)WTSVirtualChannelOpenEx(priv->SessionId, DISP_DVC_CHANNEL_NAME,
|
||||
WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
priv->disp_channel =
|
||||
WTSVirtualChannelOpenEx(priv->SessionId, DISP_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
|
||||
|
||||
if (!priv->disp_channel)
|
||||
{
|
||||
@ -465,7 +469,7 @@ static UINT disp_server_open(DispServerContext* context)
|
||||
CreateThread(NULL, 0, disp_server_thread_func, (void*)context, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
CloseHandle(priv->stopEvent);
|
||||
(void)CloseHandle(priv->stopEvent);
|
||||
priv->stopEvent = NULL;
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
goto out_close;
|
||||
@ -474,7 +478,7 @@ static UINT disp_server_open(DispServerContext* context)
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
out_close:
|
||||
WTSVirtualChannelClose(priv->disp_channel);
|
||||
(void)WTSVirtualChannelClose(priv->disp_channel);
|
||||
priv->disp_channel = NULL;
|
||||
priv->channelEvent = NULL;
|
||||
return rc;
|
||||
@ -488,8 +492,11 @@ static UINT disp_server_packet_send(DispServerContext* context, wStream* s)
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
if (!WTSVirtualChannelWrite(context->priv->disp_channel, (PCHAR)Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written))
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
|
||||
WINPR_ASSERT(pos <= UINT32_MAX);
|
||||
if (!WTSVirtualChannelWrite(context->priv->disp_channel, Stream_BufferAs(s, char), (UINT32)pos,
|
||||
&written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
ret = ERROR_INTERNAL_ERROR;
|
||||
@ -550,7 +557,7 @@ static UINT disp_server_close(DispServerContext* context)
|
||||
|
||||
if (priv->thread)
|
||||
{
|
||||
SetEvent(priv->stopEvent);
|
||||
(void)SetEvent(priv->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -559,15 +566,15 @@ static UINT disp_server_close(DispServerContext* context)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(priv->thread);
|
||||
CloseHandle(priv->stopEvent);
|
||||
(void)CloseHandle(priv->thread);
|
||||
(void)CloseHandle(priv->stopEvent);
|
||||
priv->thread = NULL;
|
||||
priv->stopEvent = NULL;
|
||||
}
|
||||
|
||||
if (priv->disp_channel)
|
||||
{
|
||||
WTSVirtualChannelClose(priv->disp_channel);
|
||||
(void)WTSVirtualChannelClose(priv->disp_channel);
|
||||
priv->disp_channel = NULL;
|
||||
}
|
||||
|
||||
|
@ -352,16 +352,16 @@ fail:
|
||||
static UINT dvcman_load_addin(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr,
|
||||
const ADDIN_ARGV* args, rdpContext* context)
|
||||
{
|
||||
PDVC_PLUGIN_ENTRY pDVCPluginEntry = NULL;
|
||||
|
||||
WINPR_ASSERT(drdynvc);
|
||||
WINPR_ASSERT(pChannelMgr);
|
||||
WINPR_ASSERT(args);
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
WLog_Print(drdynvc->log, WLOG_INFO, "Loading Dynamic Virtual Channel %s", args->argv[0]);
|
||||
pDVCPluginEntry = (PDVC_PLUGIN_ENTRY)freerdp_load_channel_addin_entry(
|
||||
args->argv[0], NULL, NULL, FREERDP_ADDIN_CHANNEL_DYNAMIC);
|
||||
|
||||
PVIRTUALCHANNELENTRY pvce =
|
||||
freerdp_load_channel_addin_entry(args->argv[0], NULL, NULL, FREERDP_ADDIN_CHANNEL_DYNAMIC);
|
||||
PDVC_PLUGIN_ENTRY pDVCPluginEntry = WINPR_FUNC_PTR_CAST(pvce, PDVC_PLUGIN_ENTRY);
|
||||
|
||||
if (pDVCPluginEntry)
|
||||
{
|
||||
@ -509,6 +509,8 @@ static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL
|
||||
break;
|
||||
case DVC_CHANNEL_CLOSED:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
@ -1371,7 +1373,7 @@ static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cb
|
||||
"process_close_request: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp, cbChId,
|
||||
ChannelId);
|
||||
|
||||
channel = (DVCMAN_CHANNEL*)dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
|
||||
channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
|
||||
if (!channel)
|
||||
{
|
||||
WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_close_request channel %" PRIu32 " not present",
|
||||
@ -1543,6 +1545,8 @@ static void VCAPITYPE drdynvc_virtual_channel_open_event_ex(LPVOID lpUserParam,
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (error && drdynvc && drdynvc->rdpcontext)
|
||||
@ -1770,7 +1774,7 @@ static UINT drdynvc_virtual_channel_event_disconnected(drdynvcPlugin* drdynvc)
|
||||
return status;
|
||||
}
|
||||
|
||||
CloseHandle(drdynvc->thread);
|
||||
(void)CloseHandle(drdynvc->thread);
|
||||
drdynvc->thread = NULL;
|
||||
}
|
||||
else
|
||||
@ -1997,8 +2001,8 @@ FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_E
|
||||
|
||||
drdynvc->channelDef.options =
|
||||
CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
|
||||
sprintf_s(drdynvc->channelDef.name, ARRAYSIZE(drdynvc->channelDef.name),
|
||||
DRDYNVC_SVC_CHANNEL_NAME);
|
||||
(void)sprintf_s(drdynvc->channelDef.name, ARRAYSIZE(drdynvc->channelDef.name),
|
||||
DRDYNVC_SVC_CHANNEL_NAME);
|
||||
drdynvc->state = DRDYNVC_STATE_INITIAL;
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
|
||||
|
||||
|
@ -97,7 +97,7 @@ static DWORD WINAPI drdynvc_server_thread(LPVOID arg)
|
||||
}
|
||||
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0,
|
||||
(PCHAR) Stream_Buffer(s), Stream_Capacity(s), &BytesReturned))
|
||||
Stream_BufferAs(s, char), Stream_Capacity(s), &BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
break;
|
||||
@ -138,7 +138,7 @@ static UINT drdynvc_server_start(DrdynvcServerContext* context)
|
||||
CreateThread(NULL, 0, drdynvc_server_thread, (void*)context, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(context->priv->StopEvent);
|
||||
(void)CloseHandle(context->priv->StopEvent);
|
||||
context->priv->StopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -154,7 +154,7 @@ static UINT drdynvc_server_start(DrdynvcServerContext* context)
|
||||
static UINT drdynvc_server_stop(DrdynvcServerContext* context)
|
||||
{
|
||||
UINT error = 0;
|
||||
SetEvent(context->priv->StopEvent);
|
||||
(void)SetEvent(context->priv->StopEvent);
|
||||
|
||||
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -163,7 +163,7 @@ static UINT drdynvc_server_stop(DrdynvcServerContext* context)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(context->priv->Thread);
|
||||
(void)CloseHandle(context->priv->Thread);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
|
@ -45,12 +45,12 @@
|
||||
#include "drive_file.h"
|
||||
|
||||
#ifdef WITH_DEBUG_RDPDR
|
||||
#define DEBUG_WSTR(msg, wstr) \
|
||||
do \
|
||||
{ \
|
||||
char lpstr[1024] = { 0 }; \
|
||||
ConvertWCharToUtf8(wstr, lpstr, ARRAYSIZE(lpstr)); \
|
||||
WLog_DBG(TAG, msg, lpstr); \
|
||||
#define DEBUG_WSTR(msg, wstr) \
|
||||
do \
|
||||
{ \
|
||||
char lpstr[1024] = { 0 }; \
|
||||
(void)ConvertWCharToUtf8(wstr, lpstr, ARRAYSIZE(lpstr)); \
|
||||
WLog_DBG(TAG, msg, lpstr); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DEBUG_WSTR(msg, wstr) \
|
||||
@ -96,6 +96,9 @@ static BOOL contains_dotdot(const WCHAR* path, size_t base_length, size_t path_l
|
||||
const WCHAR* dotdot = InitializeConstWCharFromUtf8("..", dotdotbuffer, ARRAYSIZE(dotdotbuffer));
|
||||
const WCHAR* tst = path;
|
||||
|
||||
if (path_length < 2)
|
||||
return FALSE;
|
||||
|
||||
do
|
||||
{
|
||||
tst = _wcsstr(tst, dotdot);
|
||||
@ -122,13 +125,12 @@ static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* p
|
||||
{
|
||||
BOOL ok = FALSE;
|
||||
WCHAR* fullpath = NULL;
|
||||
size_t length = 0;
|
||||
|
||||
if (!base_path || (!path && (PathWCharLength > 0)))
|
||||
goto fail;
|
||||
|
||||
const size_t base_path_length = _wcsnlen(base_path, MAX_PATH);
|
||||
length = base_path_length + PathWCharLength + 1;
|
||||
const size_t length = base_path_length + PathWCharLength + 1;
|
||||
fullpath = (WCHAR*)calloc(length, sizeof(WCHAR));
|
||||
|
||||
if (!fullpath)
|
||||
@ -145,7 +147,7 @@ static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* p
|
||||
if (contains_dotdot(&fullpath[base_path_length], base_path_length, PathWCharLength))
|
||||
{
|
||||
char abuffer[MAX_PATH] = { 0 };
|
||||
ConvertWCharToUtf8(&fullpath[base_path_length], abuffer, ARRAYSIZE(abuffer));
|
||||
(void)ConvertWCharToUtf8(&fullpath[base_path_length], abuffer, ARRAYSIZE(abuffer));
|
||||
|
||||
WLog_WARN(TAG, "[rdpdr] received invalid file path '%s' from server, aborting!",
|
||||
&abuffer[base_path_length]);
|
||||
@ -359,7 +361,7 @@ BOOL drive_file_free(DRIVE_FILE* file)
|
||||
|
||||
if (file->file_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(file->file_handle);
|
||||
(void)CloseHandle(file->file_handle);
|
||||
file->file_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
@ -390,7 +392,7 @@ fail:
|
||||
|
||||
BOOL drive_file_seek(DRIVE_FILE* file, UINT64 Offset)
|
||||
{
|
||||
LARGE_INTEGER loffset;
|
||||
LARGE_INTEGER loffset = { 0 };
|
||||
|
||||
if (!file)
|
||||
return FALSE;
|
||||
@ -583,7 +585,7 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w
|
||||
if (hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
status = GetFileInformationByHandle(hFile, &fileInformation);
|
||||
CloseHandle(hFile);
|
||||
(void)CloseHandle(hFile);
|
||||
if (!status)
|
||||
goto out_fail;
|
||||
|
||||
@ -614,10 +616,10 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
{
|
||||
INT64 size = 0;
|
||||
WCHAR* fullpath = NULL;
|
||||
ULARGE_INTEGER liCreationTime;
|
||||
ULARGE_INTEGER liLastAccessTime;
|
||||
ULARGE_INTEGER liLastWriteTime;
|
||||
ULARGE_INTEGER liChangeTime;
|
||||
ULARGE_INTEGER liCreationTime = { 0 };
|
||||
ULARGE_INTEGER liLastAccessTime = { 0 };
|
||||
ULARGE_INTEGER liLastWriteTime = { 0 };
|
||||
ULARGE_INTEGER liChangeTime = { 0 };
|
||||
FILETIME ftCreationTime;
|
||||
FILETIME ftLastAccessTime;
|
||||
FILETIME ftLastWriteTime;
|
||||
@ -626,7 +628,7 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
FILETIME* pftLastWriteTime = NULL;
|
||||
UINT32 FileAttributes = 0;
|
||||
UINT32 FileNameLength = 0;
|
||||
LARGE_INTEGER liSize;
|
||||
LARGE_INTEGER liSize = { 0 };
|
||||
UINT8 delete_pending = 0;
|
||||
UINT8 ReplaceIfExists = 0;
|
||||
DWORD attr = 0;
|
||||
@ -788,7 +790,7 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN
|
||||
|
||||
if (file->file_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(file->file_handle);
|
||||
(void)CloseHandle(file->file_handle);
|
||||
file->file_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
|
@ -470,7 +470,10 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, IRP*
|
||||
const size_t volumeLabelLen = (_wcslen(volumeLabel) + 1) * sizeof(WCHAR);
|
||||
const size_t length = 17ul + volumeLabelLen;
|
||||
|
||||
Stream_Write_UINT32(output, length); /* Length */
|
||||
if ((length > UINT32_MAX) || (volumeLabelLen > UINT32_MAX))
|
||||
return CHANNEL_RC_NO_BUFFER;
|
||||
|
||||
Stream_Write_UINT32(output, (UINT32)length); /* Length */
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(output, length))
|
||||
{
|
||||
@ -483,7 +486,7 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, IRP*
|
||||
Stream_Write_UINT32(output,
|
||||
wfad.ftCreationTime.dwHighDateTime); /* VolumeCreationTime */
|
||||
Stream_Write_UINT32(output, lpNumberOfFreeClusters & 0xffff); /* VolumeSerialNumber */
|
||||
Stream_Write_UINT32(output, volumeLabelLen); /* VolumeLabelLength */
|
||||
Stream_Write_UINT32(output, (UINT32)volumeLabelLen); /* VolumeLabelLength */
|
||||
Stream_Write_UINT8(output, 0); /* SupportsObjects */
|
||||
/* Reserved(1), MUST NOT be added! */
|
||||
Stream_Write(output, volumeLabel, volumeLabelLen); /* VolumeLabel (Unicode) */
|
||||
@ -513,7 +516,11 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, IRP*
|
||||
InitializeConstWCharFromUtf8("FAT32", LabelBuffer, ARRAYSIZE(LabelBuffer));
|
||||
const size_t diskTypeLen = (_wcslen(diskType) + 1) * sizeof(WCHAR);
|
||||
const size_t length = 12ul + diskTypeLen;
|
||||
Stream_Write_UINT32(output, length); /* Length */
|
||||
|
||||
if ((length > UINT32_MAX) || (diskTypeLen > UINT32_MAX))
|
||||
return CHANNEL_RC_NO_BUFFER;
|
||||
|
||||
Stream_Write_UINT32(output, (UINT32)length); /* Length */
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(output, length))
|
||||
{
|
||||
@ -524,7 +531,7 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, IRP*
|
||||
Stream_Write_UINT32(output, FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES |
|
||||
FILE_UNICODE_ON_DISK); /* FileSystemAttributes */
|
||||
Stream_Write_UINT32(output, MAX_PATH); /* MaximumComponentNameLength */
|
||||
Stream_Write_UINT32(output, diskTypeLen); /* FileSystemNameLength */
|
||||
Stream_Write_UINT32(output, (UINT32)diskTypeLen); /* FileSystemNameLength */
|
||||
Stream_Write(output, diskType, diskTypeLen); /* FileSystemName (Unicode) */
|
||||
}
|
||||
break;
|
||||
@ -822,7 +829,7 @@ static UINT drive_free_int(DRIVE_DEVICE* drive)
|
||||
if (!drive)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
CloseHandle(drive->thread);
|
||||
(void)CloseHandle(drive->thread);
|
||||
ListDictionary_Free(drive->files);
|
||||
MessageQueue_Free(drive->IrpQueue);
|
||||
Stream_Free(drive->device.data, TRUE);
|
||||
@ -945,7 +952,7 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
|
||||
}
|
||||
Stream_Write_UINT8(drive->device.data, '\0');
|
||||
|
||||
drive->device.name = (const char*)Stream_Buffer(drive->device.data);
|
||||
drive->device.name = Stream_BufferAs(drive->device.data, char);
|
||||
if (!drive->device.name)
|
||||
goto out_error;
|
||||
|
||||
@ -1009,7 +1016,8 @@ out_error:
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT drive_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(
|
||||
UINT VCAPITYPE drive_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
RDPDR_DRIVE* drive = NULL;
|
||||
UINT error = 0;
|
||||
|
@ -84,7 +84,7 @@ static const IWTSVirtualChannelCallback echo_callbacks = { echo_on_data_received
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT echo_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE echo_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, ECHO_DVC_CHANNEL_NAME,
|
||||
sizeof(ECHO_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
|
||||
|
@ -219,7 +219,7 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
WTSVirtualChannelClose(echo->echo_channel);
|
||||
(void)WTSVirtualChannelClose(echo->echo_channel);
|
||||
ExitThread(ERROR_NOT_ENOUGH_MEMORY);
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
@ -239,7 +239,12 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
||||
break;
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
WTSVirtualChannelRead(echo->echo_channel, 0, NULL, 0, &BytesReturned);
|
||||
if (!WTSVirtualChannelRead(echo->echo_channel, 0, NULL, 0, &BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (BytesReturned < 1)
|
||||
continue;
|
||||
@ -251,7 +256,7 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
||||
break;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(echo->echo_channel, 0, (PCHAR)Stream_Buffer(s),
|
||||
if (WTSVirtualChannelRead(echo->echo_channel, 0, Stream_BufferAs(s, char),
|
||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
@ -259,8 +264,7 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
||||
break;
|
||||
}
|
||||
|
||||
IFCALLRET(echo->context.Response, error, &echo->context, (BYTE*)Stream_Buffer(s),
|
||||
BytesReturned);
|
||||
IFCALLRET(echo->context.Response, error, &echo->context, Stream_Buffer(s), BytesReturned);
|
||||
|
||||
if (error)
|
||||
{
|
||||
@ -270,7 +274,7 @@ static DWORD WINAPI echo_server_thread_func(LPVOID arg)
|
||||
}
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
WTSVirtualChannelClose(echo->echo_channel);
|
||||
(void)WTSVirtualChannelClose(echo->echo_channel);
|
||||
echo->echo_channel = NULL;
|
||||
out:
|
||||
|
||||
@ -302,7 +306,7 @@ static UINT echo_server_open(echo_server_context* context)
|
||||
if (!(echo->thread = CreateThread(NULL, 0, echo_server_thread_func, (void*)echo, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
CloseHandle(echo->stopEvent);
|
||||
(void)CloseHandle(echo->stopEvent);
|
||||
echo->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -323,7 +327,7 @@ static UINT echo_server_close(echo_server_context* context)
|
||||
|
||||
if (echo->thread)
|
||||
{
|
||||
SetEvent(echo->stopEvent);
|
||||
(void)SetEvent(echo->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(echo->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -332,8 +336,8 @@ static UINT echo_server_close(echo_server_context* context)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(echo->thread);
|
||||
CloseHandle(echo->stopEvent);
|
||||
(void)CloseHandle(echo->thread);
|
||||
(void)CloseHandle(echo->stopEvent);
|
||||
echo->thread = NULL;
|
||||
echo->stopEvent = NULL;
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ static UINT encomsp_read_unicode_string(wStream* s, ENCOMSP_UNICODE_STRING* str)
|
||||
if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, str->cchString, sizeof(WCHAR)))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read(s, &(str->wString), (str->cchString * 2)); /* String (variable) */
|
||||
Stream_Read(s, &(str->wString), (sizeof(WCHAR) * str->cchString)); /* String (variable) */
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -1026,13 +1026,13 @@ static VOID VCAPITYPE encomsp_virtual_channel_open_event_ex(LPVOID lpUserParam,
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (error && encomsp && encomsp->rdpcontext)
|
||||
setChannelError(encomsp->rdpcontext, error,
|
||||
"encomsp_virtual_channel_open_event reported an error");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static DWORD WINAPI encomsp_virtual_channel_client_thread(LPVOID arg)
|
||||
@ -1139,7 +1139,7 @@ static UINT encomsp_virtual_channel_event_disconnected(encomspPlugin* encomsp)
|
||||
}
|
||||
|
||||
MessageQueue_Free(encomsp->queue);
|
||||
CloseHandle(encomsp->thread);
|
||||
(void)CloseHandle(encomsp->thread);
|
||||
encomsp->queue = NULL;
|
||||
encomsp->thread = NULL;
|
||||
|
||||
@ -1244,8 +1244,8 @@ FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_E
|
||||
|
||||
encomsp->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||
sprintf_s(encomsp->channelDef.name, ARRAYSIZE(encomsp->channelDef.name),
|
||||
ENCOMSP_SVC_CHANNEL_NAME);
|
||||
(void)sprintf_s(encomsp->channelDef.name, ARRAYSIZE(encomsp->channelDef.name),
|
||||
ENCOMSP_SVC_CHANNEL_NAME);
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx =
|
||||
(CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
|
||||
WINPR_ASSERT(pEntryPointsEx);
|
||||
|
@ -232,7 +232,12 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
||||
break;
|
||||
}
|
||||
|
||||
WTSVirtualChannelRead(context->priv->ChannelHandle, 0, NULL, 0, &BytesReturned);
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, NULL, 0, &BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (BytesReturned < 1)
|
||||
continue;
|
||||
@ -244,8 +249,10 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, (PCHAR)Stream_Buffer(s),
|
||||
Stream_Capacity(s), &BytesReturned))
|
||||
const size_t cap = Stream_Capacity(s);
|
||||
if ((cap > UINT32_MAX) ||
|
||||
!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, Stream_BufferAs(s, char),
|
||||
(ULONG)cap, &BytesReturned))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
@ -254,7 +261,7 @@ static DWORD WINAPI encomsp_server_thread(LPVOID arg)
|
||||
|
||||
if (Stream_GetPosition(s) >= ENCOMSP_ORDER_HEADER_SIZE)
|
||||
{
|
||||
header = (ENCOMSP_ORDER_HEADER*)Stream_Buffer(s);
|
||||
header = Stream_BufferAs(s, ENCOMSP_ORDER_HEADER);
|
||||
|
||||
if (header->Length >= Stream_GetPosition(s))
|
||||
{
|
||||
@ -306,7 +313,7 @@ static UINT encomsp_server_start(EncomspServerContext* context)
|
||||
CreateThread(NULL, 0, encomsp_server_thread, (void*)context, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(context->priv->StopEvent);
|
||||
(void)CloseHandle(context->priv->StopEvent);
|
||||
context->priv->StopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -322,7 +329,7 @@ static UINT encomsp_server_start(EncomspServerContext* context)
|
||||
static UINT encomsp_server_stop(EncomspServerContext* context)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
SetEvent(context->priv->StopEvent);
|
||||
(void)SetEvent(context->priv->StopEvent);
|
||||
|
||||
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -331,8 +338,8 @@ static UINT encomsp_server_stop(EncomspServerContext* context)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(context->priv->Thread);
|
||||
CloseHandle(context->priv->StopEvent);
|
||||
(void)CloseHandle(context->priv->Thread);
|
||||
(void)CloseHandle(context->priv->StopEvent);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -364,7 +371,7 @@ void encomsp_server_context_free(EncomspServerContext* context)
|
||||
if (context)
|
||||
{
|
||||
if (context->priv->ChannelHandle != INVALID_HANDLE_VALUE)
|
||||
WTSVirtualChannelClose(context->priv->ChannelHandle);
|
||||
(void)WTSVirtualChannelClose(context->priv->ChannelHandle);
|
||||
|
||||
free(context->priv);
|
||||
free(context);
|
||||
|
@ -394,7 +394,7 @@ static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT geometry_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE geometry_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, GEOMETRY_DVC_CHANNEL_NAME,
|
||||
sizeof(GEOMETRY_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
|
||||
|
@ -679,7 +679,7 @@ static UINT gfxredir_server_open(GfxRedirServerContext* context)
|
||||
CreateThread(NULL, 0, gfxredir_server_thread_func, (void*)context, 0, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
CloseHandle(priv->stopEvent);
|
||||
(void)CloseHandle(priv->stopEvent);
|
||||
priv->stopEvent = NULL;
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -705,7 +705,7 @@ static UINT gfxredir_server_close(GfxRedirServerContext* context)
|
||||
|
||||
if (priv->thread)
|
||||
{
|
||||
SetEvent(priv->stopEvent);
|
||||
(void)SetEvent(priv->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -714,8 +714,8 @@ static UINT gfxredir_server_close(GfxRedirServerContext* context)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(priv->thread);
|
||||
CloseHandle(priv->stopEvent);
|
||||
(void)CloseHandle(priv->thread);
|
||||
(void)CloseHandle(priv->stopEvent);
|
||||
priv->thread = NULL;
|
||||
priv->stopEvent = NULL;
|
||||
}
|
||||
|
@ -99,12 +99,15 @@ static BOOL location_read_server_ready_pdu(LOCATION_CALLBACK* callback, wStream*
|
||||
static UINT location_channel_send(IWTSVirtualChannel* channel, wStream* s)
|
||||
{
|
||||
const size_t len = Stream_GetPosition(s);
|
||||
if (len > UINT32_MAX)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
Stream_SetPosition(s, 2);
|
||||
Stream_Write_UINT32(s, len);
|
||||
Stream_Write_UINT32(s, (UINT32)len);
|
||||
|
||||
WINPR_ASSERT(channel);
|
||||
WINPR_ASSERT(channel->Write);
|
||||
return channel->Write(channel, len, Stream_Buffer(s), NULL);
|
||||
return channel->Write(channel, (UINT32)len, Stream_Buffer(s), NULL);
|
||||
}
|
||||
|
||||
static UINT location_send_client_ready_pdu(const LOCATION_CALLBACK* callback)
|
||||
@ -138,7 +141,7 @@ static const char* location_version_str(UINT32 version, char* buffer, size_t siz
|
||||
break;
|
||||
}
|
||||
|
||||
_snprintf(buffer, size, "%s [0x%08" PRIx32 "]", str, version);
|
||||
(void)_snprintf(buffer, size, "%s [0x%08" PRIx32 "]", str, version);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@ -335,11 +338,12 @@ static UINT location_send(LocationClientContext* context, LOCATION_PDUTYPE type,
|
||||
IWTSVirtualChannel* channel = cb->channel;
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
LOCATION_CALLBACK* callback = (LOCATION_CALLBACK*)loc->baseDynPlugin.channel_callbacks;
|
||||
const LOCATION_CALLBACK* callback =
|
||||
(const LOCATION_CALLBACK*)loc->baseDynPlugin.channel_callbacks;
|
||||
WINPR_ASSERT(callback);
|
||||
|
||||
UINT32 res = ERROR_INTERNAL_ERROR;
|
||||
va_list ap;
|
||||
va_list ap = { 0 };
|
||||
va_start(ap, count);
|
||||
switch (type)
|
||||
{
|
||||
@ -465,7 +469,7 @@ static const IWTSVirtualChannelCallback location_callbacks = { location_on_data_
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT location_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE location_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, LOCATION_DVC_CHANNEL_NAME,
|
||||
sizeof(LOCATION_PLUGIN), sizeof(LOCATION_CALLBACK),
|
||||
|
@ -296,7 +296,7 @@ static UINT location_process_message(location_server* location)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(location->location_channel, 0, (PCHAR)Stream_Buffer(s),
|
||||
if (WTSVirtualChannelRead(location->location_channel, 0, Stream_BufferAs(s, char),
|
||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
@ -356,6 +356,8 @@ static UINT location_server_context_poll_int(LocationServerContext* context)
|
||||
case LOCATION_OPENED:
|
||||
error = location_process_message(location);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
@ -423,10 +425,12 @@ static DWORD WINAPI location_server_thread_func(LPVOID arg)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WTSVirtualChannelClose(location->location_channel);
|
||||
(void)WTSVirtualChannelClose(location->location_channel);
|
||||
location->location_channel = NULL;
|
||||
|
||||
if (error && location->context.rdpcontext)
|
||||
@ -456,7 +460,7 @@ static UINT location_server_open(LocationServerContext* context)
|
||||
if (!location->thread)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(location->stopEvent);
|
||||
(void)CloseHandle(location->stopEvent);
|
||||
location->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -475,7 +479,7 @@ static UINT location_server_close(LocationServerContext* context)
|
||||
|
||||
if (!location->externalThread && location->thread)
|
||||
{
|
||||
SetEvent(location->stopEvent);
|
||||
(void)SetEvent(location->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(location->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -484,8 +488,8 @@ static UINT location_server_close(LocationServerContext* context)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(location->thread);
|
||||
CloseHandle(location->stopEvent);
|
||||
(void)CloseHandle(location->thread);
|
||||
(void)CloseHandle(location->stopEvent);
|
||||
location->thread = NULL;
|
||||
location->stopEvent = NULL;
|
||||
}
|
||||
@ -493,7 +497,7 @@ static UINT location_server_close(LocationServerContext* context)
|
||||
{
|
||||
if (location->state != LOCATION_INITIAL)
|
||||
{
|
||||
WTSVirtualChannelClose(location->location_channel);
|
||||
(void)WTSVirtualChannelClose(location->location_channel);
|
||||
location->location_channel = NULL;
|
||||
location->state = LOCATION_INITIAL;
|
||||
}
|
||||
@ -541,8 +545,10 @@ static UINT location_server_packet_send(LocationServerContext* context, wStream*
|
||||
WINPR_ASSERT(location);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
if (!WTSVirtualChannelWrite(location->location_channel, (PCHAR)Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written))
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
WINPR_ASSERT(pos <= UINT32_MAX);
|
||||
if (!WTSVirtualChannelWrite(location->location_channel, Stream_BufferAs(s, char), (ULONG)pos,
|
||||
&written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
|
@ -6,11 +6,19 @@ set(OPTION_SERVER_DEFAULT OFF)
|
||||
if(WIN32)
|
||||
set(OPTION_CLIENT_DEFAULT OFF)
|
||||
set(OPTION_SERVER_DEFAULT OFF)
|
||||
message("Serial redirection not supported on windows")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(OPTION_CLIENT_DEFAULT OFF)
|
||||
set(OPTION_SERVER_DEFAULT OFF)
|
||||
message("Serial redirection not supported on apple")
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
set(OPTION_CLIENT_DEFAULT OFF)
|
||||
set(OPTION_SERVER_DEFAULT OFF)
|
||||
message("Serial redirection not supported on android")
|
||||
endif()
|
||||
|
||||
define_channel_options(NAME "parallel" TYPE "device"
|
||||
|
@ -53,6 +53,7 @@
|
||||
#include <freerdp/constants.h>
|
||||
#include <freerdp/channels/rdpdr.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/utils/rdpdr_utils.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("drive.client")
|
||||
|
||||
@ -67,6 +68,7 @@ typedef struct
|
||||
HANDLE thread;
|
||||
wMessageQueue* queue;
|
||||
rdpContext* rdpcontext;
|
||||
wLog* log;
|
||||
} PARALLEL_DEVICE;
|
||||
|
||||
/**
|
||||
@ -130,12 +132,7 @@ static UINT parallel_process_irp_close(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
WINPR_ASSERT(parallel);
|
||||
WINPR_ASSERT(irp);
|
||||
|
||||
if (close(parallel->file) < 0)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
(void)close(parallel->file);
|
||||
|
||||
Stream_Zero(irp->output, 5); /* Padding(5) */
|
||||
return irp->Complete(irp);
|
||||
@ -166,13 +163,13 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
WLog_ERR(TAG, "malloc failed!");
|
||||
WLog_Print(parallel->log, WLOG_ERROR, "malloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = read(parallel->file, buffer, Length);
|
||||
|
||||
if (status < 0)
|
||||
if ((status < 0) || (status > UINT32_MAX))
|
||||
{
|
||||
irp->IoStatus = STATUS_UNSUCCESSFUL;
|
||||
free(buffer);
|
||||
@ -181,7 +178,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
}
|
||||
else
|
||||
{
|
||||
Length = status;
|
||||
Length = (UINT32)status;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(irp->output, Length);
|
||||
@ -190,7 +187,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
{
|
||||
if (!Stream_EnsureRemainingCapacity(irp->output, Length))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
WLog_Print(parallel->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
|
||||
free(buffer);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
@ -212,7 +209,6 @@ static UINT parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
UINT32 len = 0;
|
||||
UINT32 Length = 0;
|
||||
UINT64 Offset = 0;
|
||||
ssize_t status = 0;
|
||||
|
||||
WINPR_ASSERT(parallel);
|
||||
WINPR_ASSERT(irp);
|
||||
@ -233,7 +229,7 @@ static UINT parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
status = write(parallel->file, ptr, len);
|
||||
const ssize_t status = write(parallel->file, ptr, len);
|
||||
|
||||
if ((status < 0) || (status > len))
|
||||
{
|
||||
@ -272,7 +268,7 @@ static UINT parallel_process_irp_device_control(PARALLEL_DEVICE* parallel, IRP*
|
||||
*/
|
||||
static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
{
|
||||
UINT error = 0;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
|
||||
WINPR_ASSERT(parallel);
|
||||
WINPR_ASSERT(irp);
|
||||
@ -280,57 +276,42 @@ static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
|
||||
switch (irp->MajorFunction)
|
||||
{
|
||||
case IRP_MJ_CREATE:
|
||||
if ((error = parallel_process_irp_create(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_create failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = parallel_process_irp_create(parallel, irp);
|
||||
break;
|
||||
|
||||
case IRP_MJ_CLOSE:
|
||||
if ((error = parallel_process_irp_close(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_close failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = parallel_process_irp_close(parallel, irp);
|
||||
break;
|
||||
|
||||
case IRP_MJ_READ:
|
||||
if ((error = parallel_process_irp_read(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_read failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = parallel_process_irp_read(parallel, irp);
|
||||
break;
|
||||
|
||||
case IRP_MJ_WRITE:
|
||||
if ((error = parallel_process_irp_write(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_write failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = parallel_process_irp_write(parallel, irp);
|
||||
break;
|
||||
|
||||
case IRP_MJ_DEVICE_CONTROL:
|
||||
if ((error = parallel_process_irp_device_control(parallel, irp)))
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp_device_control failed with error %" PRIu32 "!",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = parallel_process_irp_device_control(parallel, irp);
|
||||
break;
|
||||
|
||||
default:
|
||||
irp->IoStatus = STATUS_NOT_SUPPORTED;
|
||||
return irp->Complete(irp);
|
||||
error = irp->Complete(irp);
|
||||
break;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
DWORD level = WLOG_TRACE;
|
||||
if (error)
|
||||
level = WLOG_WARN;
|
||||
|
||||
WLog_Print(parallel->log, level,
|
||||
"[%s|0x%08" PRIx32 "] completed with %s [0x%08" PRIx32 "] (IoStatus %s [0x%08" PRIx32
|
||||
"])",
|
||||
rdpdr_irp_string(irp->MajorFunction), irp->MajorFunction, WTSErrorToString(error),
|
||||
error, NtStatus2Tag(irp->IoStatus), irp->IoStatus);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static DWORD WINAPI parallel_thread_func(LPVOID arg)
|
||||
@ -343,7 +324,7 @@ static DWORD WINAPI parallel_thread_func(LPVOID arg)
|
||||
{
|
||||
if (!MessageQueue_Wait(parallel->queue))
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_Wait failed!");
|
||||
WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Wait failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
@ -351,7 +332,7 @@ static DWORD WINAPI parallel_thread_func(LPVOID arg)
|
||||
wMessage message = { 0 };
|
||||
if (!MessageQueue_Peek(parallel->queue, &message, TRUE))
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_Peek failed!");
|
||||
WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Peek failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
@ -361,9 +342,11 @@ static DWORD WINAPI parallel_thread_func(LPVOID arg)
|
||||
|
||||
IRP* irp = (IRP*)message.wParam;
|
||||
|
||||
if ((error = parallel_process_irp(parallel, irp)))
|
||||
error = parallel_process_irp(parallel, irp);
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "parallel_process_irp failed with error %" PRIu32 "!", error);
|
||||
WLog_Print(parallel->log, WLOG_ERROR,
|
||||
"parallel_process_irp failed with error %" PRIu32 "!", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -384,9 +367,11 @@ static UINT parallel_irp_request(DEVICE* device, IRP* irp)
|
||||
{
|
||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
|
||||
|
||||
WINPR_ASSERT(parallel);
|
||||
|
||||
if (!MessageQueue_Post(parallel->queue, NULL, 0, (void*)irp, NULL))
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_Post failed!");
|
||||
WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Post failed!");
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
@ -398,23 +383,30 @@ static UINT parallel_irp_request(DEVICE* device, IRP* irp)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT parallel_free_int(PARALLEL_DEVICE* parallel)
|
||||
{
|
||||
if (parallel)
|
||||
{
|
||||
if (!MessageQueue_PostQuit(parallel->queue, 0) ||
|
||||
(WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
const UINT error = GetLastError();
|
||||
WLog_Print(parallel->log, WLOG_ERROR,
|
||||
"WaitForSingleObject failed with error %" PRIu32 "!", error);
|
||||
}
|
||||
|
||||
(void)CloseHandle(parallel->thread);
|
||||
Stream_Free(parallel->device.data, TRUE);
|
||||
MessageQueue_Free(parallel->queue);
|
||||
}
|
||||
free(parallel);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT parallel_free(DEVICE* device)
|
||||
{
|
||||
UINT error = 0;
|
||||
PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
|
||||
|
||||
if (!MessageQueue_PostQuit(parallel->queue, 0) ||
|
||||
(WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(parallel->thread);
|
||||
Stream_Free(parallel->device.data, TRUE);
|
||||
MessageQueue_Free(parallel->queue);
|
||||
free(parallel);
|
||||
if (device)
|
||||
return parallel_free_int((PARALLEL_DEVICE*)device);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -438,26 +430,27 @@ static void parallel_message_free(void* obj)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(
|
||||
UINT VCAPITYPE parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
char* name = NULL;
|
||||
char* path = NULL;
|
||||
size_t length = 0;
|
||||
RDPDR_PARALLEL* device = NULL;
|
||||
PARALLEL_DEVICE* parallel = NULL;
|
||||
UINT error = 0;
|
||||
|
||||
WINPR_ASSERT(pEntryPoints);
|
||||
|
||||
device = (RDPDR_PARALLEL*)pEntryPoints->device;
|
||||
RDPDR_PARALLEL* device = (RDPDR_PARALLEL*)pEntryPoints->device;
|
||||
WINPR_ASSERT(device);
|
||||
|
||||
name = device->device.Name;
|
||||
path = device->Path;
|
||||
wLog* log = WLog_Get(TAG);
|
||||
WINPR_ASSERT(log);
|
||||
|
||||
char* name = device->device.Name;
|
||||
char* path = device->Path;
|
||||
|
||||
if (!name || (name[0] == '*') || !path)
|
||||
{
|
||||
/* TODO: implement auto detection of parallel ports */
|
||||
WLog_Print(log, WLOG_WARN, "Autodetection not implemented, no ports will be redirected");
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
}
|
||||
|
||||
@ -467,21 +460,22 @@ FREERDP_ENTRY_POINT(UINT parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINT
|
||||
|
||||
if (!parallel)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
WLog_Print(log, WLOG_ERROR, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
parallel->log = log;
|
||||
parallel->device.type = RDPDR_DTYP_PARALLEL;
|
||||
parallel->device.name = name;
|
||||
parallel->device.IRPRequest = parallel_irp_request;
|
||||
parallel->device.Free = parallel_free;
|
||||
parallel->rdpcontext = pEntryPoints->rdpcontext;
|
||||
length = strlen(name);
|
||||
const size_t length = strlen(name);
|
||||
parallel->device.data = Stream_New(NULL, length + 1);
|
||||
|
||||
if (!parallel->device.data)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
WLog_Print(parallel->log, WLOG_ERROR, "Stream_New failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
@ -494,7 +488,7 @@ FREERDP_ENTRY_POINT(UINT parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINT
|
||||
|
||||
if (!parallel->queue)
|
||||
{
|
||||
WLog_ERR(TAG, "MessageQueue_New failed!");
|
||||
WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_New failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
@ -503,16 +497,18 @@ FREERDP_ENTRY_POINT(UINT parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINT
|
||||
WINPR_ASSERT(obj);
|
||||
obj->fnObjectFree = parallel_message_free;
|
||||
|
||||
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)parallel)))
|
||||
error = pEntryPoints->RegisterDevice(pEntryPoints->devman, ¶llel->device);
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
|
||||
WLog_Print(parallel->log, WLOG_ERROR, "RegisterDevice failed with error %" PRIu32 "!",
|
||||
error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (!(parallel->thread =
|
||||
CreateThread(NULL, 0, parallel_thread_func, (void*)parallel, 0, NULL)))
|
||||
parallel->thread = CreateThread(NULL, 0, parallel_thread_func, parallel, 0, NULL);
|
||||
if (!parallel->thread)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
WLog_Print(parallel->log, WLOG_ERROR, "CreateThread failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto error_out;
|
||||
}
|
||||
@ -520,8 +516,6 @@ FREERDP_ENTRY_POINT(UINT parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINT
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
error_out:
|
||||
MessageQueue_Free(parallel->queue);
|
||||
Stream_Free(parallel->device.data, TRUE);
|
||||
free(parallel);
|
||||
parallel_free_int(parallel);
|
||||
return error;
|
||||
}
|
||||
|
@ -28,6 +28,6 @@ set(${MODULE_PREFIX}_LIBS
|
||||
)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${CUPS_INCLUDE_DIRS})
|
||||
include_directories(SYSTEM ${CUPS_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
@ -65,6 +65,8 @@ static bool is_mac_os_sonoma_or_later(void)
|
||||
int major = 0;
|
||||
int minor = 0;
|
||||
int patch = 0;
|
||||
|
||||
// NOLINTNEXTLINE(cert-err34-c)
|
||||
const int rc = sscanf(str, "%d.%d.%d", &major, &minor, &patch);
|
||||
if (rc != 3)
|
||||
{
|
||||
@ -110,8 +112,9 @@ static void printer_cups_get_printjob_name(char* buf, size_t size, size_t id)
|
||||
WINPR_ASSERT(buf);
|
||||
WINPR_ASSERT(size > 0);
|
||||
|
||||
sprintf_s(buf, size - 1, "FreeRDP Print %04d-%02d-%02d %02d-%02d-%02d - Job %" PRIdz,
|
||||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, id);
|
||||
(void)sprintf_s(buf, size - 1, "FreeRDP Print %04d-%02d-%02d %02d-%02d-%02d - Job %" PRIuz,
|
||||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec,
|
||||
id);
|
||||
}
|
||||
|
||||
static bool http_status_ok(http_status_t status)
|
||||
@ -146,10 +149,6 @@ static UINT write_printjob(rdpPrintJob* printjob, const BYTE* data, size_t size)
|
||||
*/
|
||||
static UINT printer_cups_write_printjob(rdpPrintJob* printjob, const BYTE* data, size_t size)
|
||||
{
|
||||
rdpCupsPrintJob* cups_printjob = (rdpCupsPrintJob*)printjob;
|
||||
|
||||
WINPR_ASSERT(cups_printjob);
|
||||
|
||||
return write_printjob(printjob, data, size);
|
||||
}
|
||||
|
||||
@ -365,7 +364,7 @@ static rdpPrinter** printer_cups_enum_printers(rdpPrinterDriver* driver)
|
||||
if (!printers)
|
||||
return NULL;
|
||||
|
||||
for (size_t i = 0; i < num_dests; i++)
|
||||
for (size_t i = 0; i < (size_t)num_dests; i++)
|
||||
{
|
||||
const cups_dest_t* dest = &dests[i];
|
||||
if (dest->instance == NULL)
|
||||
@ -431,7 +430,7 @@ static void printer_cups_release_ref_driver(rdpPrinterDriver* driver)
|
||||
cups_driver->references--;
|
||||
}
|
||||
|
||||
FREERDP_ENTRY_POINT(UINT cups_freerdp_printer_client_subsystem_entry(void* arg))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE cups_freerdp_printer_client_subsystem_entry(void* arg))
|
||||
{
|
||||
rdpPrinterDriver** ppPrinter = (rdpPrinterDriver**)arg;
|
||||
if (!ppPrinter)
|
||||
|
@ -103,7 +103,6 @@ static BOOL printer_write_setting(const char* path, prn_conf_t type, const void*
|
||||
DWORD written = 0;
|
||||
BOOL rc = FALSE;
|
||||
HANDLE file = NULL;
|
||||
size_t b64len = 0;
|
||||
char* base64 = NULL;
|
||||
const char* name = filemap[type];
|
||||
char* abs = GetCombinedPath(path, name);
|
||||
@ -129,8 +128,8 @@ static BOOL printer_write_setting(const char* path, prn_conf_t type, const void*
|
||||
|
||||
/* base64 char represents 6bit -> 4*(n/3) is the length which is
|
||||
* always smaller than 2*n */
|
||||
b64len = strnlen(base64, 2 * length);
|
||||
rc = WriteFile(file, base64, b64len, &written, NULL);
|
||||
const size_t b64len = strnlen(base64, 2 * length);
|
||||
rc = WriteFile(file, base64, (UINT32)b64len, &written, NULL);
|
||||
|
||||
if (b64len != written)
|
||||
rc = FALSE;
|
||||
@ -139,7 +138,7 @@ static BOOL printer_write_setting(const char* path, prn_conf_t type, const void*
|
||||
rc = TRUE;
|
||||
|
||||
fail:
|
||||
CloseHandle(file);
|
||||
(void)CloseHandle(file);
|
||||
free(base64);
|
||||
return rc;
|
||||
}
|
||||
@ -194,7 +193,7 @@ static BOOL printer_read_setting(const char* path, prn_conf_t type, void** data,
|
||||
}
|
||||
|
||||
fail:
|
||||
CloseHandle(file);
|
||||
(void)CloseHandle(file);
|
||||
|
||||
if (rc && (lowSize <= INT_MAX))
|
||||
{
|
||||
@ -313,7 +312,12 @@ static BOOL printer_load_from_config(const rdpSettings* settings, rdpPrinter* pr
|
||||
|
||||
wlen++;
|
||||
path = get_printer_config_path(settings, wname, wlen * sizeof(WCHAR));
|
||||
PrinterNameLen = wlen * sizeof(WCHAR);
|
||||
{
|
||||
const size_t plen = wlen * sizeof(WCHAR);
|
||||
if (plen > UINT32_MAX)
|
||||
goto fail;
|
||||
PrinterNameLen = (UINT32)plen;
|
||||
}
|
||||
|
||||
if (!path)
|
||||
goto fail;
|
||||
@ -331,7 +335,10 @@ static BOOL printer_load_from_config(const rdpSettings* settings, rdpPrinter* pr
|
||||
DriverName = ConvertUtf8ToWCharAlloc(printer->driver, &len);
|
||||
if (!DriverName)
|
||||
goto fail;
|
||||
DriverNameLen = (len + 1) * sizeof(WCHAR);
|
||||
const size_t dlen = (len + 1) * sizeof(WCHAR);
|
||||
if (dlen > UINT32_MAX)
|
||||
goto fail;
|
||||
DriverNameLen = (UINT32)dlen;
|
||||
}
|
||||
|
||||
if (!printer_read_setting(path, PRN_CONF_DATA, &CachedPrinterConfigData, &CachedFieldsLen))
|
||||
@ -651,7 +658,7 @@ static DWORD WINAPI printer_thread_func(LPVOID arg)
|
||||
else if (rc != WAIT_OBJECT_0)
|
||||
continue;
|
||||
|
||||
ResetEvent(printer_dev->event);
|
||||
(void)ResetEvent(printer_dev->event);
|
||||
irp = (IRP*)InterlockedPopEntrySList(printer_dev->pIrpList);
|
||||
|
||||
if (irp == NULL)
|
||||
@ -690,7 +697,7 @@ static UINT printer_irp_request(DEVICE* device, IRP* irp)
|
||||
if (printer_dev->async)
|
||||
{
|
||||
InterlockedPushEntrySList(printer_dev->pIrpList, &(irp->ItemEntry));
|
||||
SetEvent(printer_dev->event);
|
||||
(void)SetEvent(printer_dev->event);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -908,7 +915,7 @@ static UINT printer_free(DEVICE* device)
|
||||
|
||||
if (printer_dev->async)
|
||||
{
|
||||
SetEvent(printer_dev->stopEvent);
|
||||
(void)SetEvent(printer_dev->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(printer_dev->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -929,9 +936,9 @@ static UINT printer_free(DEVICE* device)
|
||||
irp->Discard(irp);
|
||||
}
|
||||
|
||||
CloseHandle(printer_dev->thread);
|
||||
CloseHandle(printer_dev->stopEvent);
|
||||
CloseHandle(printer_dev->event);
|
||||
(void)CloseHandle(printer_dev->thread);
|
||||
(void)CloseHandle(printer_dev->stopEvent);
|
||||
(void)CloseHandle(printer_dev->event);
|
||||
winpr_aligned_free(printer_dev->pIrpList);
|
||||
}
|
||||
|
||||
@ -972,7 +979,7 @@ static UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, rdpPrint
|
||||
if (!printer_dev->device.data)
|
||||
goto error_out;
|
||||
|
||||
sprintf_s(printer_dev->port, sizeof(printer_dev->port), "PRN%" PRIdz, printer->id);
|
||||
(void)sprintf_s(printer_dev->port, sizeof(printer_dev->port), "PRN%" PRIuz, printer->id);
|
||||
printer_dev->device.type = RDPDR_DTYP_PRINT;
|
||||
printer_dev->device.name = printer_dev->port;
|
||||
printer_dev->device.IRPRequest = printer_irp_request;
|
||||
@ -1048,19 +1055,14 @@ error_out:
|
||||
|
||||
static rdpPrinterDriver* printer_load_backend(const char* backend)
|
||||
{
|
||||
typedef UINT (*backend_load_t)(rdpPrinterDriver**);
|
||||
union
|
||||
{
|
||||
PVIRTUALCHANNELENTRY entry;
|
||||
backend_load_t backend;
|
||||
} fktconv;
|
||||
|
||||
fktconv.entry = freerdp_load_channel_addin_entry("printer", backend, NULL, 0);
|
||||
if (!fktconv.entry)
|
||||
typedef UINT(VCAPITYPE * backend_load_t)(rdpPrinterDriver**);
|
||||
PVIRTUALCHANNELENTRY entry = freerdp_load_channel_addin_entry("printer", backend, NULL, 0);
|
||||
backend_load_t func = WINPR_FUNC_PTR_CAST(entry, backend_load_t);
|
||||
if (!func)
|
||||
return NULL;
|
||||
|
||||
rdpPrinterDriver* printer = NULL;
|
||||
const UINT rc = fktconv.backend(&printer);
|
||||
const UINT rc = func(&printer);
|
||||
if (rc != CHANNEL_RC_OK)
|
||||
return NULL;
|
||||
|
||||
@ -1072,7 +1074,8 @@ static rdpPrinterDriver* printer_load_backend(const char* backend)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT printer_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(
|
||||
UINT VCAPITYPE printer_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
char* name = NULL;
|
||||
char* driver_name = NULL;
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/wtsapi.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/windows.h>
|
||||
|
||||
@ -436,7 +437,7 @@ static void printer_win_release_ref_driver(rdpPrinterDriver* driver)
|
||||
win->references--;
|
||||
}
|
||||
|
||||
FREERDP_ENTRY_POINT(UINT win_freerdp_printer_client_subsystem_entry(void* arg))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE win_freerdp_printer_client_subsystem_entry(void* arg))
|
||||
{
|
||||
rdpPrinterDriver** ppPrinter = (rdpPrinterDriver**)arg;
|
||||
if (!ppPrinter)
|
||||
|
@ -82,8 +82,8 @@ UINT client_rail_server_start_cmd(RailClientContext* context)
|
||||
freerdp_settings_get_string(settings, FreeRDP_RemoteApplicationCmdLine);
|
||||
if (RemoteApplicationFile && RemoteApplicationCmdLine)
|
||||
{
|
||||
_snprintf(argsAndFile, ARRAYSIZE(argsAndFile), "%s %s", RemoteApplicationCmdLine,
|
||||
RemoteApplicationFile);
|
||||
(void)_snprintf(argsAndFile, ARRAYSIZE(argsAndFile), "%s %s", RemoteApplicationCmdLine,
|
||||
RemoteApplicationFile);
|
||||
exec.RemoteApplicationArguments = argsAndFile;
|
||||
}
|
||||
else if (RemoteApplicationFile)
|
||||
|
@ -82,23 +82,13 @@ static UINT rail_send(railPlugin* rail, wStream* s)
|
||||
*/
|
||||
UINT rail_send_channel_data(railPlugin* rail, wStream* src)
|
||||
{
|
||||
wStream* s = NULL;
|
||||
size_t length = 0;
|
||||
|
||||
if (!rail || !src)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
length = Stream_GetPosition(src);
|
||||
s = Stream_New(NULL, length);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
Stream_Free(src, TRUE);
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Stream_Write(s, Stream_Buffer(src), length);
|
||||
return rail_send(rail, s);
|
||||
return rail_send(rail, src);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -229,13 +219,7 @@ static UINT rail_send_client_sysparam(RailClientContext* context, RAIL_SYSPARAM_
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = rail_send_pdu(rail, s, TS_RAIL_ORDER_SYSPARAM)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_pdu failed with error %" PRIu32 "!", error);
|
||||
}
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_SYSPARAM);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -514,6 +498,24 @@ static UINT rail_client_snap_arrange(RailClientContext* context, const RAIL_SNAP
|
||||
return rail_send_client_snap_arrange_order(rail, snap);
|
||||
}
|
||||
|
||||
static UINT rail_client_text_scale(RailClientContext* context, UINT32 textScale)
|
||||
{
|
||||
if (!context || !context->handle)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
railPlugin* rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_text_scale_order(rail, textScale);
|
||||
}
|
||||
|
||||
static UINT rail_client_caret_blink_rate(RailClientContext* context, UINT32 rate)
|
||||
{
|
||||
if (!context || !context->handle)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
railPlugin* rail = (railPlugin*)context->handle;
|
||||
return rail_send_client_caret_blink_rate_order(rail, rate);
|
||||
}
|
||||
|
||||
static VOID VCAPITYPE rail_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
|
||||
UINT event, LPVOID pData,
|
||||
UINT32 dataLength, UINT32 totalLength,
|
||||
@ -552,13 +554,13 @@ static VOID VCAPITYPE rail_virtual_channel_open_event_ex(LPVOID lpUserParam, DWO
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (error && rail && rail->rdpcontext)
|
||||
setChannelError(rail->rdpcontext, error,
|
||||
"rail_virtual_channel_open_event reported an error");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -693,7 +695,7 @@ FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS p
|
||||
rail->sendHandshake = TRUE;
|
||||
rail->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
|
||||
CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL;
|
||||
sprintf_s(rail->channelDef.name, ARRAYSIZE(rail->channelDef.name), RAIL_SVC_CHANNEL_NAME);
|
||||
(void)sprintf_s(rail->channelDef.name, ARRAYSIZE(rail->channelDef.name), RAIL_SVC_CHANNEL_NAME);
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
|
||||
|
||||
if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
|
||||
@ -725,6 +727,8 @@ FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS p
|
||||
context->ClientSnapArrange = rail_client_snap_arrange;
|
||||
context->ClientCloak = rail_client_cloak;
|
||||
context->ClientCompartmentInfo = rail_client_compartment_info;
|
||||
context->ClientTextScale = rail_client_text_scale;
|
||||
context->ClientCaretBlinkRate = rail_client_caret_blink_rate;
|
||||
rail->rdpcontext = pEntryPointsEx->context;
|
||||
rail->context = context;
|
||||
isFreerdp = TRUE;
|
||||
|
@ -44,7 +44,10 @@ UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType)
|
||||
UINT16 orderLength = 0;
|
||||
|
||||
if (!rail || !s)
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
orderLength = (UINT16)Stream_GetPosition(s);
|
||||
Stream_SetPosition(s, 0);
|
||||
@ -1114,13 +1117,10 @@ UINT rail_order_recv(LPVOID userdata, wStream* s)
|
||||
*/
|
||||
UINT rail_send_handshake_order(railPlugin* rail, const RAIL_HANDSHAKE_ORDER* handshake)
|
||||
{
|
||||
wStream* s = NULL;
|
||||
UINT error = 0;
|
||||
|
||||
if (!rail || !handshake)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
s = rail_pdu_init(RAIL_HANDSHAKE_ORDER_LENGTH);
|
||||
wStream* s = rail_pdu_init(RAIL_HANDSHAKE_ORDER_LENGTH);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
@ -1129,9 +1129,7 @@ UINT rail_send_handshake_order(railPlugin* rail, const RAIL_HANDSHAKE_ORDER* han
|
||||
}
|
||||
|
||||
rail_write_handshake_order(s, handshake);
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_HANDSHAKE);
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_HANDSHAKE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1141,13 +1139,10 @@ UINT rail_send_handshake_order(railPlugin* rail, const RAIL_HANDSHAKE_ORDER* han
|
||||
*/
|
||||
UINT rail_send_handshake_ex_order(railPlugin* rail, const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
|
||||
{
|
||||
wStream* s = NULL;
|
||||
UINT error = 0;
|
||||
|
||||
if (!rail || !handshakeEx)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
s = rail_pdu_init(RAIL_HANDSHAKE_EX_ORDER_LENGTH);
|
||||
wStream* s = rail_pdu_init(RAIL_HANDSHAKE_EX_ORDER_LENGTH);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
@ -1156,9 +1151,7 @@ UINT rail_send_handshake_ex_order(railPlugin* rail, const RAIL_HANDSHAKE_EX_ORDE
|
||||
}
|
||||
|
||||
rail_write_handshake_ex_order(s, handshakeEx);
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_HANDSHAKE_EX);
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_HANDSHAKE_EX);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1185,11 +1178,14 @@ UINT rail_send_client_status_order(railPlugin* rail, const RAIL_CLIENT_STATUS_OR
|
||||
|
||||
error = rail_write_client_status_order(s, clientStatus);
|
||||
|
||||
if (error == ERROR_SUCCESS)
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_CLIENTSTATUS);
|
||||
if (ERROR_SUCCESS != error)
|
||||
{
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_CLIENTSTATUS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1224,11 +1220,7 @@ UINT rail_send_client_exec_order(railPlugin* rail, UINT16 flags,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((error = rail_send_pdu(rail, s, TS_RAIL_ORDER_EXEC)))
|
||||
{
|
||||
WLog_ERR(TAG, "rail_send_pdu failed with error %" PRIu32 "!", error);
|
||||
goto out;
|
||||
}
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_EXEC);
|
||||
|
||||
out:
|
||||
Stream_Free(s, TRUE);
|
||||
@ -1258,11 +1250,14 @@ UINT rail_send_client_activate_order(railPlugin* rail, const RAIL_ACTIVATE_ORDER
|
||||
|
||||
error = rail_write_client_activate_order(s, activate);
|
||||
|
||||
if (error == ERROR_SUCCESS)
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_ACTIVATE);
|
||||
if (ERROR_SUCCESS != error)
|
||||
{
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_ACTIVATE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1288,11 +1283,14 @@ UINT rail_send_client_sysmenu_order(railPlugin* rail, const RAIL_SYSMENU_ORDER*
|
||||
|
||||
error = rail_write_client_sysmenu_order(s, sysmenu);
|
||||
|
||||
if (error == ERROR_SUCCESS)
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_SYSMENU);
|
||||
if (ERROR_SUCCESS != error)
|
||||
{
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_SYSMENU);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1318,11 +1316,14 @@ UINT rail_send_client_syscommand_order(railPlugin* rail, const RAIL_SYSCOMMAND_O
|
||||
|
||||
error = rail_write_client_syscommand_order(s, syscommand);
|
||||
|
||||
if (error == ERROR_SUCCESS)
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_SYSCOMMAND);
|
||||
if (ERROR_SUCCESS != error)
|
||||
{
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_SYSCOMMAND);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1349,11 +1350,14 @@ UINT rail_send_client_notify_event_order(railPlugin* rail,
|
||||
|
||||
error = rail_write_client_notify_event_order(s, notifyEvent);
|
||||
|
||||
if (ERROR_SUCCESS == error)
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_NOTIFY_EVENT);
|
||||
if (ERROR_SUCCESS != error)
|
||||
{
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_NOTIFY_EVENT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1379,11 +1383,14 @@ UINT rail_send_client_window_move_order(railPlugin* rail, const RAIL_WINDOW_MOVE
|
||||
|
||||
error = rail_write_client_window_move_order(s, windowMove);
|
||||
|
||||
if (error == ERROR_SUCCESS)
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_WINDOWMOVE);
|
||||
if (ERROR_SUCCESS != error)
|
||||
{
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_WINDOWMOVE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1410,11 +1417,13 @@ UINT rail_send_client_get_appid_req_order(railPlugin* rail,
|
||||
|
||||
error = rail_write_client_get_appid_req_order(s, getAppIdReq);
|
||||
|
||||
if (error == ERROR_SUCCESS)
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_GET_APPID_REQ);
|
||||
if (ERROR_SUCCESS != error)
|
||||
{
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_GET_APPID_REQ);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1444,11 +1453,13 @@ UINT rail_send_client_langbar_info_order(railPlugin* rail,
|
||||
|
||||
error = rail_write_langbar_info_order(s, langBarInfo);
|
||||
|
||||
if (ERROR_SUCCESS == error)
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_LANGBARINFO);
|
||||
if (ERROR_SUCCESS != error)
|
||||
{
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_LANGBARINFO);
|
||||
}
|
||||
|
||||
UINT rail_send_client_languageime_info_order(railPlugin* rail,
|
||||
@ -1473,11 +1484,13 @@ UINT rail_send_client_languageime_info_order(railPlugin* rail,
|
||||
|
||||
error = rail_write_languageime_info_order(s, langImeInfo);
|
||||
|
||||
if (ERROR_SUCCESS == error)
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_LANGUAGEIMEINFO);
|
||||
if (ERROR_SUCCESS != error)
|
||||
{
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_LANGUAGEIMEINFO);
|
||||
}
|
||||
|
||||
UINT rail_send_client_compartment_info_order(railPlugin* rail,
|
||||
@ -1502,22 +1515,20 @@ UINT rail_send_client_compartment_info_order(railPlugin* rail,
|
||||
|
||||
error = rail_write_compartment_info_order(s, compartmentInfo);
|
||||
|
||||
if (ERROR_SUCCESS == error)
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_COMPARTMENTINFO);
|
||||
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
if (ERROR_SUCCESS != error)
|
||||
{
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
}
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_COMPARTMENTINFO);
|
||||
}
|
||||
|
||||
UINT rail_send_client_cloak_order(railPlugin* rail, const RAIL_CLOAK* cloak)
|
||||
{
|
||||
wStream* s = NULL;
|
||||
UINT error = 0;
|
||||
|
||||
if (!rail || !cloak)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
s = rail_pdu_init(5);
|
||||
wStream* s = rail_pdu_init(5);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
@ -1527,16 +1538,11 @@ UINT rail_send_client_cloak_order(railPlugin* rail, const RAIL_CLOAK* cloak)
|
||||
|
||||
Stream_Write_UINT32(s, cloak->windowId);
|
||||
Stream_Write_UINT8(s, cloak->cloak ? 1 : 0);
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_CLOAK);
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_CLOAK);
|
||||
}
|
||||
|
||||
UINT rail_send_client_snap_arrange_order(railPlugin* rail, const RAIL_SNAP_ARRANGE* snap)
|
||||
{
|
||||
wStream* s = NULL;
|
||||
UINT error = 0;
|
||||
|
||||
if (!rail)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
@ -1552,7 +1558,7 @@ UINT rail_send_client_snap_arrange_order(railPlugin* rail, const RAIL_SNAP_ARRAN
|
||||
return rail_send_client_window_move_order(rail, &move);
|
||||
}
|
||||
|
||||
s = rail_pdu_init(12);
|
||||
wStream* s = rail_pdu_init(12);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
@ -1565,7 +1571,39 @@ UINT rail_send_client_snap_arrange_order(railPlugin* rail, const RAIL_SNAP_ARRAN
|
||||
Stream_Write_INT16(s, snap->top);
|
||||
Stream_Write_INT16(s, snap->right);
|
||||
Stream_Write_INT16(s, snap->bottom);
|
||||
error = rail_send_pdu(rail, s, TS_RAIL_ORDER_SNAP_ARRANGE);
|
||||
Stream_Free(s, TRUE);
|
||||
return error;
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_SNAP_ARRANGE);
|
||||
}
|
||||
|
||||
UINT rail_send_client_text_scale_order(railPlugin* rail, UINT32 textScale)
|
||||
{
|
||||
if (!rail)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
wStream* s = rail_pdu_init(4);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "rail_pdu_init failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(s, textScale);
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_TEXTSCALEINFO);
|
||||
}
|
||||
|
||||
UINT rail_send_client_caret_blink_rate_order(railPlugin* rail, UINT32 rate)
|
||||
{
|
||||
if (!rail)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
wStream* s = rail_pdu_init(4);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "rail_pdu_init failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(s, rate);
|
||||
return rail_send_pdu(rail, s, TS_RAIL_ORDER_CARETBLINKINFO);
|
||||
}
|
||||
|
@ -56,5 +56,7 @@ UINT rail_send_client_cloak_order(railPlugin* rail, const RAIL_CLOAK* cloak);
|
||||
UINT rail_send_client_snap_arrange_order(railPlugin* rail, const RAIL_SNAP_ARRANGE* snap);
|
||||
UINT rail_send_client_compartment_info_order(railPlugin* rail,
|
||||
const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo);
|
||||
UINT rail_send_client_text_scale_order(railPlugin* rail, UINT32 textScale);
|
||||
UINT rail_send_client_caret_blink_rate_order(railPlugin* rail, UINT32 rate);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RAIL_CLIENT_ORDERS_H */
|
||||
|
@ -90,8 +90,8 @@ const char* rail_get_order_type_string(UINT16 orderType)
|
||||
|
||||
const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length)
|
||||
{
|
||||
_snprintf(buffer, length, "%s[0x%04" PRIx16 "]", rail_get_order_type_string(orderType),
|
||||
orderType);
|
||||
(void)_snprintf(buffer, length, "%s[0x%04" PRIx16 "]", rail_get_order_type_string(orderType),
|
||||
orderType);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@ -121,8 +121,7 @@ void rail_write_pdu_header(wStream* s, UINT16 orderType, UINT16 orderLength)
|
||||
|
||||
wStream* rail_pdu_init(size_t length)
|
||||
{
|
||||
wStream* s = NULL;
|
||||
s = Stream_New(NULL, length + RAIL_PDU_HEADER_LENGTH);
|
||||
wStream* s = Stream_New(NULL, length + RAIL_PDU_HEADER_LENGTH);
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
@ -595,7 +594,7 @@ const char* rail_handshake_ex_flags_to_string(UINT32 flags, char* buffer, size_t
|
||||
if (len < 1)
|
||||
return NULL;
|
||||
|
||||
_snprintf(buffer, len, "{");
|
||||
(void)_snprintf(buffer, len, "{");
|
||||
char* fbuffer = &buffer[1];
|
||||
len--;
|
||||
|
||||
@ -613,7 +612,7 @@ const char* rail_handshake_ex_flags_to_string(UINT32 flags, char* buffer, size_t
|
||||
winpr_str_append("EXTENDED_SPI_2_SUPPORTED", fbuffer, len, "|");
|
||||
|
||||
char number[16] = { 0 };
|
||||
_snprintf(number, sizeof(number), "[0x%08" PRIx32 "]", flags);
|
||||
(void)_snprintf(number, sizeof(number), "[0x%08" PRIx32 "]", flags);
|
||||
winpr_str_append(number, buffer, len, "}");
|
||||
return buffer;
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ UINT rail_write_unicode_string_value(wStream* s, const RAIL_UNICODE_STRING* unic
|
||||
UINT rail_read_sysparam_order(wStream* s, RAIL_SYSPARAM_ORDER* sysparam, BOOL extendedSpiSupported);
|
||||
UINT rail_write_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam,
|
||||
BOOL extendedSpiSupported);
|
||||
BOOL rail_is_extended_spi_supported(UINT32 channelsFlags);
|
||||
BOOL rail_is_extended_spi_supported(UINT32 channelFlags);
|
||||
const char* rail_get_order_type_string(UINT16 orderType);
|
||||
const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length);
|
||||
|
||||
|
@ -46,7 +46,7 @@ static UINT rail_send(RailServerContext* context, wStream* s, ULONG length)
|
||||
if (!context)
|
||||
return CHANNEL_RC_BAD_INIT_HANDLE;
|
||||
|
||||
if (!WTSVirtualChannelWrite(context->priv->rail_channel, (PCHAR)Stream_Buffer(s), length,
|
||||
if (!WTSVirtualChannelWrite(context->priv->rail_channel, Stream_BufferAs(s, char), length,
|
||||
&written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
@ -670,14 +670,14 @@ static UINT rail_read_exec_order(wStream* s, RAIL_EXEC_ORDER* exec, char* args[]
|
||||
|
||||
if (exeLen > 0)
|
||||
{
|
||||
const SSIZE_T len = exeLen / sizeof(WCHAR);
|
||||
const size_t len = exeLen / sizeof(WCHAR);
|
||||
exec->RemoteApplicationProgram = args[0] = Stream_Read_UTF16_String_As_UTF8(s, len, NULL);
|
||||
if (!exec->RemoteApplicationProgram)
|
||||
goto fail;
|
||||
}
|
||||
if (workLen > 0)
|
||||
{
|
||||
const SSIZE_T len = workLen / sizeof(WCHAR);
|
||||
const size_t len = workLen / sizeof(WCHAR);
|
||||
exec->RemoteApplicationWorkingDir = args[1] =
|
||||
Stream_Read_UTF16_String_As_UTF8(s, len, NULL);
|
||||
if (!exec->RemoteApplicationWorkingDir)
|
||||
@ -685,7 +685,7 @@ static UINT rail_read_exec_order(wStream* s, RAIL_EXEC_ORDER* exec, char* args[]
|
||||
}
|
||||
if (argLen > 0)
|
||||
{
|
||||
const SSIZE_T len = argLen / sizeof(WCHAR);
|
||||
const size_t len = argLen / sizeof(WCHAR);
|
||||
exec->RemoteApplicationArguments = args[2] = Stream_Read_UTF16_String_As_UTF8(s, len, NULL);
|
||||
if (!exec->RemoteApplicationArguments)
|
||||
goto fail;
|
||||
@ -1459,21 +1459,21 @@ static UINT rail_server_start(RailServerContext* context)
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
out_stop_event:
|
||||
CloseHandle(context->priv->stopEvent);
|
||||
(void)CloseHandle(context->priv->stopEvent);
|
||||
context->priv->stopEvent = NULL;
|
||||
out_close:
|
||||
WTSVirtualChannelClose(context->priv->rail_channel);
|
||||
(void)WTSVirtualChannelClose(context->priv->rail_channel);
|
||||
context->priv->rail_channel = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
static BOOL rail_server_stop(RailServerContext* context)
|
||||
{
|
||||
RailServerPrivate* priv = (RailServerPrivate*)context->priv;
|
||||
RailServerPrivate* priv = context->priv;
|
||||
|
||||
if (priv->thread)
|
||||
{
|
||||
SetEvent(priv->stopEvent);
|
||||
(void)SetEvent(priv->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -1481,15 +1481,15 @@ static BOOL rail_server_stop(RailServerContext* context)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CloseHandle(priv->thread);
|
||||
CloseHandle(priv->stopEvent);
|
||||
(void)CloseHandle(priv->thread);
|
||||
(void)CloseHandle(priv->stopEvent);
|
||||
priv->thread = NULL;
|
||||
priv->stopEvent = NULL;
|
||||
}
|
||||
|
||||
if (priv->rail_channel)
|
||||
{
|
||||
WTSVirtualChannelClose(priv->rail_channel);
|
||||
(void)WTSVirtualChannelClose(priv->rail_channel);
|
||||
priv->rail_channel = NULL;
|
||||
}
|
||||
|
||||
|
@ -103,9 +103,9 @@ static int init_external_addin(Plugin* plugin)
|
||||
}
|
||||
|
||||
plugin->hProcess = procInfo.hProcess;
|
||||
CloseHandle(procInfo.hThread);
|
||||
CloseHandle(siStartInfo.hStdOutput);
|
||||
CloseHandle(siStartInfo.hStdInput);
|
||||
(void)CloseHandle(procInfo.hThread);
|
||||
(void)CloseHandle(siStartInfo.hStdOutput);
|
||||
(void)CloseHandle(siStartInfo.hStdInput);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ static DWORD WINAPI copyThread(void* data)
|
||||
{
|
||||
DWORD status = WAIT_OBJECT_0;
|
||||
Plugin* plugin = (Plugin*)data;
|
||||
size_t const bufsize = 16 * 1024;
|
||||
size_t const bufsize = 16ULL * 1024ULL;
|
||||
|
||||
while (status == WAIT_OBJECT_0)
|
||||
{
|
||||
@ -145,7 +145,7 @@ static DWORD WINAPI copyThread(void* data)
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
fprintf(stderr, "rdp2tcp copyThread: malloc failed\n");
|
||||
(void)fprintf(stderr, "rdp2tcp copyThread: malloc failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ static DWORD WINAPI copyThread(void* data)
|
||||
plugin->initHandle, plugin->openHandle, buffer, dwRead, buffer) != CHANNEL_RC_OK)
|
||||
{
|
||||
free(buffer);
|
||||
fprintf(stderr, "rdp2tcp copyThread failed %i\n", (int)dwRead);
|
||||
(void)fprintf(stderr, "rdp2tcp copyThread failed %i\n", (int)dwRead);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -175,7 +175,7 @@ static DWORD WINAPI copyThread(void* data)
|
||||
handles[1] = freerdp_abort_event(plugin->channelEntryPoints.context);
|
||||
status = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
|
||||
if (status == WAIT_OBJECT_0)
|
||||
ResetEvent(plugin->writeComplete);
|
||||
(void)ResetEvent(plugin->writeComplete);
|
||||
}
|
||||
|
||||
fail:
|
||||
@ -246,9 +246,11 @@ static void VCAPITYPE VirtualChannelOpenEventEx(LPVOID lpUserParam, DWORD openHa
|
||||
free(pData);
|
||||
break;
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
SetEvent(plugin->writeComplete);
|
||||
(void)SetEvent(plugin->writeComplete);
|
||||
free(pData);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,14 +263,14 @@ static void channel_terminated(Plugin* plugin)
|
||||
return;
|
||||
|
||||
if (plugin->copyThread)
|
||||
TerminateThread(plugin->copyThread, 0);
|
||||
(void)TerminateThread(plugin->copyThread, 0);
|
||||
if (plugin->writeComplete)
|
||||
CloseHandle(plugin->writeComplete);
|
||||
(void)CloseHandle(plugin->writeComplete);
|
||||
|
||||
CloseHandle(plugin->hStdInputWrite);
|
||||
CloseHandle(plugin->hStdOutputRead);
|
||||
(void)CloseHandle(plugin->hStdInputWrite);
|
||||
(void)CloseHandle(plugin->hStdOutputRead);
|
||||
TerminateProcess(plugin->hProcess, 0);
|
||||
CloseHandle(plugin->hProcess);
|
||||
(void)CloseHandle(plugin->hProcess);
|
||||
free(plugin->commandline);
|
||||
free(plugin);
|
||||
}
|
||||
@ -312,6 +314,8 @@ static VOID VCAPITYPE VirtualChannelInitEventEx(LPVOID lpUserParam, LPVOID pInit
|
||||
case CHANNEL_EVENT_TERMINATED:
|
||||
channel_terminated(plugin);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,12 @@
|
||||
|
||||
define_channel_client("rdpdr")
|
||||
|
||||
include (CheckFunctionExists)
|
||||
check_function_exists(getmntent_r FREERDP_HAVE_GETMNTENT_R)
|
||||
if (FREERDP_HAVE_GETMNTENT_R)
|
||||
add_definitions(-DFREERDP_HAVE_GETMNTENT_R)
|
||||
endif()
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
irp.c
|
||||
irp.h
|
||||
|
@ -185,8 +185,7 @@ static const char PARALLEL_SERVICE_NAME[] = "parallel";
|
||||
UINT devman_load_device_service(DEVMAN* devman, const RDPDR_DEVICE* device, rdpContext* rdpcontext)
|
||||
{
|
||||
const char* ServiceName = NULL;
|
||||
DEVICE_SERVICE_ENTRY_POINTS ep;
|
||||
PDEVICE_SERVICE_ENTRY entry = NULL;
|
||||
DEVICE_SERVICE_ENTRY_POINTS ep = { 0 };
|
||||
union
|
||||
{
|
||||
const RDPDR_DEVICE* cdp;
|
||||
@ -219,8 +218,9 @@ UINT devman_load_device_service(DEVMAN* devman, const RDPDR_DEVICE* device, rdpC
|
||||
else
|
||||
WLog_INFO(TAG, "Loading device service %s (static)", ServiceName);
|
||||
|
||||
entry = (PDEVICE_SERVICE_ENTRY)freerdp_load_channel_addin_entry(ServiceName, NULL,
|
||||
"DeviceServiceEntry", 0);
|
||||
PVIRTUALCHANNELENTRY pvce =
|
||||
freerdp_load_channel_addin_entry(ServiceName, NULL, "DeviceServiceEntry", 0);
|
||||
PDEVICE_SERVICE_ENTRY entry = WINPR_FUNC_PTR_CAST(pvce, PDEVICE_SERVICE_ENTRY);
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/stream.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/utils/rdpdr_utils.h>
|
||||
|
||||
#include "rdpdr_main.h"
|
||||
@ -38,16 +39,21 @@
|
||||
#define RDPDR_CAPABILITY_HEADER_LENGTH 8
|
||||
|
||||
/* Output device direction general capability set */
|
||||
static void rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static BOOL rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
WINPR_UNUSED(rdpdr);
|
||||
WINPR_ASSERT(rdpdr);
|
||||
const RDPDR_CAPABILITY_HEADER header = { CAP_GENERAL_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH + 36,
|
||||
GENERAL_CAPABILITY_VERSION_02 };
|
||||
|
||||
const UINT32 ioCode1 = rdpdr->clientIOCode1 & rdpdr->serverIOCode1;
|
||||
const UINT32 ioCode2 = rdpdr->clientIOCode2 & rdpdr->serverIOCode2;
|
||||
|
||||
rdpdr_write_capset_header(rdpdr->log, s, &header);
|
||||
if (rdpdr_write_capset_header(rdpdr->log, s, &header) != CHANNEL_RC_OK)
|
||||
return FALSE;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 36))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT32(s, rdpdr->clientOsType); /* osType, ignored on receipt */
|
||||
Stream_Write_UINT32(s, rdpdr->clientOsVersion); /* osVersion, unused and must be set to zero */
|
||||
Stream_Write_UINT16(s, rdpdr->clientVersionMajor); /* protocolMajorVersion, must be set to 1 */
|
||||
@ -62,6 +68,7 @@ static void rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
Stream_Write_UINT32(
|
||||
s, rdpdr->clientSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to
|
||||
be redirected before logon */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Process device direction general capability set */
|
||||
@ -99,12 +106,12 @@ static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s,
|
||||
}
|
||||
|
||||
/* Output printer direction capability set */
|
||||
static void rdpdr_write_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static BOOL rdpdr_write_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
WINPR_UNUSED(rdpdr);
|
||||
const RDPDR_CAPABILITY_HEADER header = { CAP_PRINTER_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH,
|
||||
PRINT_CAPABILITY_VERSION_01 };
|
||||
rdpdr_write_capset_header(rdpdr->log, s, &header);
|
||||
return rdpdr_write_capset_header(rdpdr->log, s, &header) == CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/* Process printer direction capability set */
|
||||
@ -117,12 +124,12 @@ static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s,
|
||||
}
|
||||
|
||||
/* Output port redirection capability set */
|
||||
static void rdpdr_write_port_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static BOOL rdpdr_write_port_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
WINPR_UNUSED(rdpdr);
|
||||
const RDPDR_CAPABILITY_HEADER header = { CAP_PORT_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH,
|
||||
PORT_CAPABILITY_VERSION_01 };
|
||||
rdpdr_write_capset_header(rdpdr->log, s, &header);
|
||||
return rdpdr_write_capset_header(rdpdr->log, s, &header) == CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/* Process port redirection capability set */
|
||||
@ -135,12 +142,12 @@ static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s,
|
||||
}
|
||||
|
||||
/* Output drive redirection capability set */
|
||||
static void rdpdr_write_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static BOOL rdpdr_write_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
WINPR_UNUSED(rdpdr);
|
||||
const RDPDR_CAPABILITY_HEADER header = { CAP_DRIVE_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH,
|
||||
DRIVE_CAPABILITY_VERSION_02 };
|
||||
rdpdr_write_capset_header(rdpdr->log, s, &header);
|
||||
return rdpdr_write_capset_header(rdpdr->log, s, &header) == CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/* Process drive redirection capability set */
|
||||
@ -153,12 +160,12 @@ static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s,
|
||||
}
|
||||
|
||||
/* Output smart card redirection capability set */
|
||||
static void rdpdr_write_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static BOOL rdpdr_write_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
WINPR_UNUSED(rdpdr);
|
||||
const RDPDR_CAPABILITY_HEADER header = { CAP_SMARTCARD_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH,
|
||||
SMARTCARD_CAPABILITY_VERSION_01 };
|
||||
rdpdr_write_capset_header(rdpdr->log, s, &header);
|
||||
return rdpdr_write_capset_header(rdpdr->log, s, &header) == CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/* Process smartcard redirection capability set */
|
||||
@ -187,6 +194,7 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
|
||||
Stream_Read_UINT16(s, numCapabilities);
|
||||
Stream_Seek(s, 2); /* pad (2 bytes) */
|
||||
|
||||
memset(rdpdr->capabilities, 0, sizeof(rdpdr->capabilities));
|
||||
for (UINT16 i = 0; i < numCapabilities; i++)
|
||||
{
|
||||
RDPDR_CAPABILITY_HEADER header = { 0 };
|
||||
@ -197,27 +205,31 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
|
||||
switch (header.CapabilityType)
|
||||
{
|
||||
case CAP_GENERAL_TYPE:
|
||||
rdpdr->capabilities[header.CapabilityType] = TRUE;
|
||||
status = rdpdr_process_general_capset(rdpdr, s, &header);
|
||||
break;
|
||||
|
||||
case CAP_PRINTER_TYPE:
|
||||
rdpdr->capabilities[header.CapabilityType] = TRUE;
|
||||
status = rdpdr_process_printer_capset(rdpdr, s, &header);
|
||||
break;
|
||||
|
||||
case CAP_PORT_TYPE:
|
||||
rdpdr->capabilities[header.CapabilityType] = TRUE;
|
||||
status = rdpdr_process_port_capset(rdpdr, s, &header);
|
||||
break;
|
||||
|
||||
case CAP_DRIVE_TYPE:
|
||||
rdpdr->capabilities[header.CapabilityType] = TRUE;
|
||||
status = rdpdr_process_drive_capset(rdpdr, s, &header);
|
||||
break;
|
||||
|
||||
case CAP_SMARTCARD_TYPE:
|
||||
rdpdr->capabilities[header.CapabilityType] = TRUE;
|
||||
status = rdpdr_process_smartcard_capset(rdpdr, s, &header);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -235,10 +247,13 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
|
||||
*/
|
||||
UINT rdpdr_send_capability_response(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
wStream* s = NULL;
|
||||
|
||||
WINPR_ASSERT(rdpdr);
|
||||
s = StreamPool_Take(rdpdr->pool, 256);
|
||||
WINPR_ASSERT(rdpdr->rdpcontext);
|
||||
|
||||
rdpSettings* settings = rdpdr->rdpcontext->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
wStream* s = StreamPool_Take(rdpdr->pool, 256);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
@ -246,14 +261,62 @@ UINT rdpdr_send_capability_response(rdpdrPlugin* rdpdr)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
const RDPDR_DEVICE* cdrives =
|
||||
freerdp_device_collection_find_type(settings, RDPDR_DTYP_FILESYSTEM);
|
||||
const RDPDR_DEVICE* cserial = freerdp_device_collection_find_type(settings, RDPDR_DTYP_SERIAL);
|
||||
const RDPDR_DEVICE* cparallel =
|
||||
freerdp_device_collection_find_type(settings, RDPDR_DTYP_PARALLEL);
|
||||
const RDPDR_DEVICE* csmart =
|
||||
freerdp_device_collection_find_type(settings, RDPDR_DTYP_SMARTCARD);
|
||||
const RDPDR_DEVICE* cprinter = freerdp_device_collection_find_type(settings, RDPDR_DTYP_PRINT);
|
||||
|
||||
/* Only send capabilities the server announced */
|
||||
const BOOL drives = cdrives && rdpdr->capabilities[CAP_DRIVE_TYPE];
|
||||
const BOOL serial = cserial && rdpdr->capabilities[CAP_PORT_TYPE];
|
||||
const BOOL parallel = cparallel && rdpdr->capabilities[CAP_PORT_TYPE];
|
||||
const BOOL smart = csmart && rdpdr->capabilities[CAP_SMARTCARD_TYPE];
|
||||
const BOOL printer = cprinter && rdpdr->capabilities[CAP_PRINTER_TYPE];
|
||||
UINT16 count = 1;
|
||||
if (drives)
|
||||
count++;
|
||||
if (serial || parallel)
|
||||
count++;
|
||||
if (smart)
|
||||
count++;
|
||||
if (printer)
|
||||
count++;
|
||||
|
||||
Stream_Write_UINT16(s, RDPDR_CTYP_CORE);
|
||||
Stream_Write_UINT16(s, PAKID_CORE_CLIENT_CAPABILITY);
|
||||
Stream_Write_UINT16(s, 5); /* numCapabilities */
|
||||
Stream_Write_UINT16(s, count); /* numCapabilities */
|
||||
Stream_Write_UINT16(s, 0); /* pad */
|
||||
rdpdr_write_general_capset(rdpdr, s);
|
||||
rdpdr_write_printer_capset(rdpdr, s);
|
||||
rdpdr_write_port_capset(rdpdr, s);
|
||||
rdpdr_write_drive_capset(rdpdr, s);
|
||||
rdpdr_write_smartcard_capset(rdpdr, s);
|
||||
|
||||
if (!rdpdr_write_general_capset(rdpdr, s))
|
||||
goto fail;
|
||||
|
||||
if (printer)
|
||||
{
|
||||
if (!rdpdr_write_printer_capset(rdpdr, s))
|
||||
goto fail;
|
||||
}
|
||||
if (serial || parallel)
|
||||
{
|
||||
if (!rdpdr_write_port_capset(rdpdr, s))
|
||||
goto fail;
|
||||
}
|
||||
if (drives)
|
||||
{
|
||||
if (!rdpdr_write_drive_capset(rdpdr, s))
|
||||
goto fail;
|
||||
}
|
||||
if (smart)
|
||||
{
|
||||
if (!rdpdr_write_smartcard_capset(rdpdr, s))
|
||||
goto fail;
|
||||
}
|
||||
return rdpdr_send(rdpdr, s);
|
||||
|
||||
fail:
|
||||
Stream_Release(s);
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
@ -223,7 +224,7 @@ static BOOL rdpdr_load_drive(rdpdrPlugin* rdpdr, const char* name, const char* p
|
||||
|
||||
fail:
|
||||
freerdp_device_free(drive.device);
|
||||
return rc;
|
||||
return rc == CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -514,19 +515,19 @@ typedef struct
|
||||
*/
|
||||
static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
struct dirent* pDirent;
|
||||
DIR* pDir;
|
||||
char fullpath[PATH_MAX];
|
||||
struct dirent* pDirent = NULL;
|
||||
char fullpath[PATH_MAX] = { 0 };
|
||||
char* szdir = (char*)"/Volumes";
|
||||
struct stat buf;
|
||||
hotplug_dev dev_array[MAX_USB_DEVICES];
|
||||
int count;
|
||||
DEVICE_DRIVE_EXT* device_ext;
|
||||
struct stat buf = { 0 };
|
||||
hotplug_dev dev_array[MAX_USB_DEVICES] = { 0 };
|
||||
int count = 0;
|
||||
DEVICE_DRIVE_EXT* device_ext = NULL;
|
||||
ULONG_PTR* keys = NULL;
|
||||
int size = 0;
|
||||
UINT error;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
UINT32 ids[1];
|
||||
pDir = opendir(szdir);
|
||||
|
||||
DIR* pDir = opendir(szdir);
|
||||
|
||||
if (pDir == NULL)
|
||||
{
|
||||
@ -706,7 +707,7 @@ static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg)
|
||||
out:
|
||||
if (rdpdr->stopEvent)
|
||||
{
|
||||
CloseHandle(rdpdr->stopEvent);
|
||||
(void)CloseHandle(rdpdr->stopEvent);
|
||||
rdpdr->stopEvent = NULL;
|
||||
}
|
||||
ExitThread(CHANNEL_RC_OK);
|
||||
@ -737,12 +738,15 @@ static BOOL isAutomountLocation(const char* path)
|
||||
const char* location = automountLocations[x];
|
||||
size_t length = 0;
|
||||
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_FORMAT_NONLITERAL
|
||||
if (strstr(location, "%lu"))
|
||||
snprintf(buffer, sizeof(buffer), location, (unsigned long)uid);
|
||||
(void)snprintf(buffer, sizeof(buffer), location, (unsigned long)uid);
|
||||
else if (strstr(location, "%s"))
|
||||
snprintf(buffer, sizeof(buffer), location, uname);
|
||||
(void)snprintf(buffer, sizeof(buffer), location, uname);
|
||||
else
|
||||
snprintf(buffer, sizeof(buffer), "%s", location);
|
||||
(void)snprintf(buffer, sizeof(buffer), "%s", location);
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
|
||||
length = strnlen(buffer, sizeof(buffer));
|
||||
|
||||
@ -834,9 +838,25 @@ static UINT handle_platform_mounts_bsd(wLog* log, hotplug_dev* dev_array, size_t
|
||||
|
||||
#if defined(__LINUX__) || defined(__linux__)
|
||||
#include <mntent.h>
|
||||
static struct mntent* getmntent_x(FILE* f, struct mntent* buffer, char* pathbuffer,
|
||||
size_t pathbuffersize)
|
||||
{
|
||||
#if defined(FREERDP_HAVE_GETMNTENT_R)
|
||||
WINPR_ASSERT(pathbuffersize <= INT32_MAX);
|
||||
return getmntent_r(f, buffer, pathbuffer, (int)pathbuffersize);
|
||||
#else
|
||||
(void)buffer;
|
||||
(void)pathbuffer;
|
||||
(void)pathbuffersize;
|
||||
return getmntent(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
static UINT handle_platform_mounts_linux(wLog* log, hotplug_dev* dev_array, size_t* size)
|
||||
{
|
||||
FILE* f = NULL;
|
||||
struct mntent mnt = { 0 };
|
||||
char pathbuffer[PATH_MAX] = { 0 };
|
||||
struct mntent* ent = NULL;
|
||||
f = winpr_fopen("/proc/mounts", "r");
|
||||
if (f == NULL)
|
||||
@ -844,11 +864,11 @@ static UINT handle_platform_mounts_linux(wLog* log, hotplug_dev* dev_array, size
|
||||
WLog_Print(log, WLOG_ERROR, "fopen failed!");
|
||||
return ERROR_OPEN_FAILED;
|
||||
}
|
||||
while ((ent = getmntent(f)) != NULL)
|
||||
while ((ent = getmntent_x(f, &mnt, pathbuffer, sizeof(pathbuffer))) != NULL)
|
||||
{
|
||||
handle_mountpoint(dev_array, size, ent->mnt_dir);
|
||||
}
|
||||
fclose(f);
|
||||
(void)fclose(f);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
@ -919,6 +939,7 @@ static BOOL hotplug_delete_foreach(ULONG_PTR key, void* element, void* data)
|
||||
WINPR_ASSERT(arg);
|
||||
WINPR_ASSERT(arg->rdpdr);
|
||||
WINPR_ASSERT(arg->dev_array || (arg->dev_array_size == 0));
|
||||
WINPR_ASSERT(key <= UINT32_MAX);
|
||||
|
||||
if (!device_ext || (device_ext->device.type != RDPDR_DTYP_FILESYSTEM) || !device_ext->path ||
|
||||
!device_ext->automount)
|
||||
@ -949,7 +970,7 @@ static BOOL hotplug_delete_foreach(ULONG_PTR key, void* element, void* data)
|
||||
if (!dev_found)
|
||||
{
|
||||
UINT error = 0;
|
||||
UINT32 ids[1] = { key };
|
||||
UINT32 ids[1] = { (UINT32)key };
|
||||
|
||||
WINPR_ASSERT(arg->rdpdr->devman);
|
||||
devman_unregister_device(arg->rdpdr->devman, (void*)key);
|
||||
@ -1064,7 +1085,7 @@ out:
|
||||
|
||||
if (rdpdr->stopEvent)
|
||||
{
|
||||
CloseHandle(rdpdr->stopEvent);
|
||||
(void)CloseHandle(rdpdr->stopEvent);
|
||||
rdpdr->stopEvent = NULL;
|
||||
}
|
||||
|
||||
@ -1090,7 +1111,7 @@ static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
if (rdpdr->stopEvent)
|
||||
SetEvent(rdpdr->stopEvent);
|
||||
(void)SetEvent(rdpdr->stopEvent);
|
||||
#endif
|
||||
#ifdef __MACOSX__
|
||||
CFRunLoopStop(rdpdr->runLoop);
|
||||
@ -1104,7 +1125,7 @@ static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(rdpdr->hotplugThread);
|
||||
(void)CloseHandle(rdpdr->hotplugThread);
|
||||
rdpdr->hotplugThread = NULL;
|
||||
}
|
||||
|
||||
@ -1157,7 +1178,8 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr)
|
||||
if (!drive->Path)
|
||||
continue;
|
||||
|
||||
BOOL hotplugAll = strncmp(drive->Path, "*", 2) == 0;
|
||||
const char wildcard[] = "*";
|
||||
BOOL hotplugAll = strncmp(drive->Path, wildcard, sizeof(wildcard)) == 0;
|
||||
BOOL hotplugLater = strncmp(drive->Path, DynamicDrives, sizeof(DynamicDrives)) == 0;
|
||||
|
||||
if (hotplugAll || hotplugLater)
|
||||
@ -1249,7 +1271,7 @@ static UINT rdpdr_send_client_announce_reply(rdpdrPlugin* rdpdr)
|
||||
Stream_Write_UINT16(s, PAKID_CORE_CLIENTID_CONFIRM); /* PacketId (2 bytes) */
|
||||
Stream_Write_UINT16(s, rdpdr->clientVersionMajor);
|
||||
Stream_Write_UINT16(s, rdpdr->clientVersionMinor);
|
||||
Stream_Write_UINT32(s, (UINT32)rdpdr->clientID);
|
||||
Stream_Write_UINT32(s, rdpdr->clientID);
|
||||
return rdpdr_send(rdpdr, s);
|
||||
}
|
||||
|
||||
@ -1296,7 +1318,7 @@ static UINT rdpdr_send_client_name_request(rdpdrPlugin* rdpdr)
|
||||
Stream_Write_UINT32(s, 0); /* codePage, must be set to zero */
|
||||
Stream_Write_UINT32(s,
|
||||
(UINT32)computerNameLenW); /* computerNameLen, including null terminator */
|
||||
Stream_Write(s, computerNameW, (size_t)computerNameLenW);
|
||||
Stream_Write(s, computerNameW, computerNameLenW);
|
||||
free(computerNameW);
|
||||
return rdpdr_send(rdpdr, s);
|
||||
}
|
||||
@ -1626,7 +1648,7 @@ static const char* state_str(size_t count, va_list ap, char* buffer, size_t size
|
||||
static BOOL rdpdr_state_check(rdpdrPlugin* rdpdr, UINT16 packetid, enum RDPDR_CHANNEL_STATE next,
|
||||
size_t count, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_list ap = { 0 };
|
||||
WINPR_ASSERT(rdpdr);
|
||||
|
||||
va_start(ap, count);
|
||||
@ -1858,8 +1880,7 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
|
||||
*/
|
||||
UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT status = 0;
|
||||
rdpdrPlugin* plugin = (rdpdrPlugin*)rdpdr;
|
||||
rdpdrPlugin* plugin = rdpdr;
|
||||
|
||||
if (!s)
|
||||
{
|
||||
@ -1874,9 +1895,13 @@ UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s)
|
||||
}
|
||||
|
||||
const size_t pos = Stream_GetPosition(s);
|
||||
rdpdr_dump_send_packet(rdpdr->log, WLOG_TRACE, s, "[rdpdr-channel] send");
|
||||
status = plugin->channelEntryPoints.pVirtualChannelWriteEx(
|
||||
plugin->InitHandle, plugin->OpenHandle, Stream_Buffer(s), pos, s);
|
||||
UINT status = ERROR_INTERNAL_ERROR;
|
||||
if (pos <= UINT32_MAX)
|
||||
{
|
||||
rdpdr_dump_send_packet(rdpdr->log, WLOG_TRACE, s, "[rdpdr-channel] send");
|
||||
status = plugin->channelEntryPoints.pVirtualChannelWriteEx(
|
||||
plugin->InitHandle, plugin->OpenHandle, Stream_Buffer(s), (UINT32)pos, s);
|
||||
}
|
||||
|
||||
if (status != CHANNEL_RC_OK)
|
||||
{
|
||||
@ -2009,13 +2034,13 @@ static VOID VCAPITYPE rdpdr_virtual_channel_open_event_ex(LPVOID lpUserParam, DW
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (error && rdpdr && rdpdr->rdpcontext)
|
||||
setChannelError(rdpdr->rdpcontext, error,
|
||||
"rdpdr_virtual_channel_open_event_ex reported an error");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static DWORD WINAPI rdpdr_virtual_channel_client_thread(LPVOID arg)
|
||||
@ -2176,7 +2201,7 @@ static UINT rdpdr_virtual_channel_event_disconnected(rdpdrPlugin* rdpdr)
|
||||
}
|
||||
|
||||
if (rdpdr->thread)
|
||||
CloseHandle(rdpdr->thread);
|
||||
(void)CloseHandle(rdpdr->thread);
|
||||
MessageQueue_Free(rdpdr->queue);
|
||||
rdpdr->queue = NULL;
|
||||
rdpdr->thread = NULL;
|
||||
@ -2311,7 +2336,8 @@ FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS p
|
||||
|
||||
rdpdr->channelDef.options =
|
||||
CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
|
||||
sprintf_s(rdpdr->channelDef.name, ARRAYSIZE(rdpdr->channelDef.name), RDPDR_SVC_CHANNEL_NAME);
|
||||
(void)sprintf_s(rdpdr->channelDef.name, ARRAYSIZE(rdpdr->channelDef.name),
|
||||
RDPDR_SVC_CHANNEL_NAME);
|
||||
rdpdr->sequenceId = 0;
|
||||
pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
|
||||
|
||||
|
@ -113,6 +113,7 @@ typedef struct
|
||||
wStreamPool* pool;
|
||||
wLog* log;
|
||||
BOOL async;
|
||||
BOOL capabilities[6];
|
||||
} rdpdrPlugin;
|
||||
|
||||
BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next);
|
||||
|
@ -206,7 +206,6 @@ static RDPDR_IRP* rdpdr_server_dequeue_irp(RdpdrServerContext* context, UINT32 c
|
||||
static UINT rdpdr_seal_send_free_request(RdpdrServerContext* context, wStream* s)
|
||||
{
|
||||
BOOL status = 0;
|
||||
size_t length = 0;
|
||||
ULONG written = 0;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
@ -214,8 +213,8 @@ static UINT rdpdr_seal_send_free_request(RdpdrServerContext* context, wStream* s
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
Stream_SealLength(s);
|
||||
length = Stream_Length(s);
|
||||
WINPR_ASSERT(length <= ULONG_MAX);
|
||||
const size_t length = Stream_Length(s);
|
||||
WINPR_ASSERT(length <= UINT32_MAX);
|
||||
Stream_SetPosition(s, 0);
|
||||
|
||||
if (length >= RDPDR_HEADER_LENGTH)
|
||||
@ -230,7 +229,7 @@ static UINT rdpdr_seal_send_free_request(RdpdrServerContext* context, wStream* s
|
||||
rdpdr_packetid_string(header.PacketId), header.PacketId);
|
||||
}
|
||||
winpr_HexLogDump(context->priv->log, WLOG_DEBUG, Stream_Buffer(s), Stream_Length(s));
|
||||
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
|
||||
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, Stream_BufferAs(s, char),
|
||||
(ULONG)length, &written);
|
||||
Stream_Free(s, TRUE);
|
||||
return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
@ -463,13 +462,9 @@ static UINT rdpdr_server_read_general_capability_set(RdpdrServerContext* context
|
||||
switch (VersionMinor)
|
||||
{
|
||||
case RDPDR_MINOR_RDP_VERSION_13:
|
||||
break;
|
||||
case RDPDR_MINOR_RDP_VERSION_6_X:
|
||||
break;
|
||||
case RDPDR_MINOR_RDP_VERSION_5_2:
|
||||
break;
|
||||
case RDPDR_MINOR_RDP_VERSION_5_1:
|
||||
break;
|
||||
case RDPDR_MINOR_RDP_VERSION_5_0:
|
||||
break;
|
||||
default:
|
||||
@ -1540,7 +1535,9 @@ static UINT rdpdr_server_receive_io_lock_control_request(RdpdrServerContext* con
|
||||
|
||||
WLog_Print(context->priv->log, WLOG_WARN,
|
||||
"[MS-RDPEFS] 2.2.3.3.12 Server Drive Lock Control Request (DR_DRIVE_LOCK_REQ) "
|
||||
"not implemented");
|
||||
"[Lock=0x%08" PRIx32 "]"
|
||||
"not implemented",
|
||||
Lock);
|
||||
WLog_Print(context->priv->log, WLOG_WARN, "TODO");
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
@ -2079,8 +2076,8 @@ static DWORD WINAPI rdpdr_server_thread(LPVOID arg)
|
||||
break;
|
||||
}
|
||||
|
||||
capacity = MIN(Stream_Capacity(s), ULONG_MAX);
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, (PCHAR)Stream_Buffer(s),
|
||||
capacity = MIN(Stream_Capacity(s), UINT32_MAX);
|
||||
if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, Stream_BufferAs(s, char),
|
||||
(ULONG)capacity, &BytesReturned))
|
||||
{
|
||||
WLog_Print(context->priv->log, WLOG_ERROR, "WTSVirtualChannelRead failed!");
|
||||
@ -2149,7 +2146,7 @@ static UINT rdpdr_server_start(RdpdrServerContext* context)
|
||||
CreateThread(NULL, 0, rdpdr_server_thread, (void*)context, 0, NULL)))
|
||||
{
|
||||
WLog_Print(context->priv->log, WLOG_ERROR, "CreateThread failed!");
|
||||
CloseHandle(context->priv->StopEvent);
|
||||
(void)CloseHandle(context->priv->StopEvent);
|
||||
context->priv->StopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -2170,7 +2167,7 @@ static UINT rdpdr_server_stop(RdpdrServerContext* context)
|
||||
|
||||
if (context->priv->StopEvent)
|
||||
{
|
||||
SetEvent(context->priv->StopEvent);
|
||||
(void)SetEvent(context->priv->StopEvent);
|
||||
|
||||
if (WaitForSingleObject(context->priv->Thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -2180,15 +2177,15 @@ static UINT rdpdr_server_stop(RdpdrServerContext* context)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(context->priv->Thread);
|
||||
(void)CloseHandle(context->priv->Thread);
|
||||
context->priv->Thread = NULL;
|
||||
CloseHandle(context->priv->StopEvent);
|
||||
(void)CloseHandle(context->priv->StopEvent);
|
||||
context->priv->StopEvent = NULL;
|
||||
}
|
||||
|
||||
if (context->priv->ChannelHandle)
|
||||
{
|
||||
WTSVirtualChannelClose(context->priv->ChannelHandle);
|
||||
(void)WTSVirtualChannelClose(context->priv->ChannelHandle);
|
||||
context->priv->ChannelHandle = NULL;
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
|
38
channels/rdpear/CMakeLists.txt
Normal file
38
channels/rdpear/CMakeLists.txt
Normal file
@ -0,0 +1,38 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2023 David Fort <contact@hardening-consulting.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
if (NOT IOS AND NOT WIN32 AND NOT ANDROID)
|
||||
find_package(KRB5)
|
||||
|
||||
if(KRB5_FOUND)
|
||||
define_channel("rdpear")
|
||||
|
||||
include_directories(common)
|
||||
|
||||
if(WITH_CLIENT_CHANNELS OR WITH_SERVER_CHANNELS)
|
||||
add_subdirectory(common)
|
||||
endif()
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
|
||||
#if(WITH_SERVER_CHANNELS)
|
||||
# add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
#endif()
|
||||
endif()
|
||||
endif()
|
13
channels/rdpear/ChannelOptions.cmake
Normal file
13
channels/rdpear/ChannelOptions.cmake
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
set(OPTION_DEFAULT OFF)
|
||||
set(OPTION_CLIENT_DEFAULT OFF)
|
||||
set(OPTION_SERVER_DEFAULT OFF)
|
||||
|
||||
define_channel_options(NAME "rdpear" TYPE "dynamic"
|
||||
DESCRIPTION "Authentication redirection Virtual Channel Extension"
|
||||
SPECIFICATIONS "[MS-RDPEAR]"
|
||||
DEFAULT ${OPTION_DEFAULT})
|
||||
|
||||
define_channel_client_options(${OPTION_CLIENT_DEFAULT})
|
||||
define_channel_server_options(${OPTION_SERVER_DEFAULT})
|
||||
|
31
channels/rdpear/client/CMakeLists.txt
Normal file
31
channels/rdpear/client/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2023 David Fort <contact@hardening-consulting.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
define_channel_client("rdpear")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
rdpear_main.c
|
||||
)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS
|
||||
winpr
|
||||
freerdp
|
||||
rdpear-common
|
||||
)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
|
989
channels/rdpear/client/rdpear_main.c
Normal file
989
channels/rdpear/client/rdpear_main.c
Normal file
@ -0,0 +1,989 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Authentication redirection virtual channel
|
||||
*
|
||||
* Copyright 2023 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed 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 <krb5.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/asn1.h>
|
||||
#include <winpr/sspi.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <rdpear-common/ndr.h>
|
||||
#include <rdpear-common/rdpear_common.h>
|
||||
#include <rdpear-common/rdpear_asn1.h>
|
||||
|
||||
#include <freerdp/config.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/client/channels.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/channels/rdpear.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("rdpear.client")
|
||||
|
||||
/* defined in libkrb5 */
|
||||
krb5_error_code encode_krb5_authenticator(const krb5_authenticator* rep, krb5_data** code_out);
|
||||
krb5_error_code encode_krb5_ap_rep(const krb5_ap_rep* rep, krb5_data** code_out);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GENERIC_DYNVC_PLUGIN base;
|
||||
rdpContext* rdp_context;
|
||||
krb5_context krbContext;
|
||||
} RDPEAR_PLUGIN;
|
||||
|
||||
static const BYTE payloadHeader[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
static krb5_error_code RPC_ENCRYPTION_KEY_to_keyblock(krb5_context ctx,
|
||||
const KERB_RPC_ENCRYPTION_KEY* key,
|
||||
krb5_keyblock** pkeyblock)
|
||||
{
|
||||
WINPR_ASSERT(ctx);
|
||||
WINPR_ASSERT(key);
|
||||
WINPR_ASSERT(pkeyblock);
|
||||
|
||||
if (!key->reserved3.length)
|
||||
return KRB5KDC_ERR_NULL_KEY;
|
||||
|
||||
krb5_error_code rv =
|
||||
krb5_init_keyblock(ctx, (krb5_enctype)key->reserved2, key->reserved3.length, pkeyblock);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
krb5_keyblock* keyblock = *pkeyblock;
|
||||
memcpy(keyblock->contents, key->reserved3.value, key->reserved3.length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static krb5_error_code kerb_do_checksum(krb5_context ctx, const KERB_RPC_ENCRYPTION_KEY* key,
|
||||
krb5_keyusage kusage, krb5_cksumtype cksumtype,
|
||||
const KERB_ASN1_DATA* plain, krb5_checksum* out)
|
||||
{
|
||||
WINPR_ASSERT(ctx);
|
||||
WINPR_ASSERT(key);
|
||||
WINPR_ASSERT(plain);
|
||||
WINPR_ASSERT(out);
|
||||
|
||||
krb5_keyblock* keyblock = NULL;
|
||||
krb5_data data = { 0 };
|
||||
|
||||
krb5_error_code rv = RPC_ENCRYPTION_KEY_to_keyblock(ctx, key, &keyblock);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
data.data = (char*)plain->Asn1Buffer;
|
||||
data.length = plain->Asn1BufferHints.count;
|
||||
|
||||
rv = krb5_c_make_checksum(ctx, cksumtype, keyblock, kusage, &data, out);
|
||||
|
||||
krb5_free_keyblock(ctx, keyblock);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static krb5_error_code kerb_do_encrypt(krb5_context ctx, const KERB_RPC_ENCRYPTION_KEY* key,
|
||||
krb5_keyusage kusage, const KERB_ASN1_DATA* plain,
|
||||
krb5_data* out)
|
||||
{
|
||||
WINPR_ASSERT(ctx);
|
||||
WINPR_ASSERT(key);
|
||||
WINPR_ASSERT(plain);
|
||||
WINPR_ASSERT(out);
|
||||
|
||||
krb5_keyblock* keyblock = NULL;
|
||||
krb5_data data = { 0 };
|
||||
krb5_enc_data enc = { 0 };
|
||||
size_t elen = 0;
|
||||
|
||||
krb5_error_code rv = RPC_ENCRYPTION_KEY_to_keyblock(ctx, key, &keyblock);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
data.data = (char*)plain->Asn1Buffer;
|
||||
data.length = plain->Asn1BufferHints.count;
|
||||
|
||||
rv = krb5_c_encrypt_length(ctx, keyblock->enctype, data.length, &elen);
|
||||
if (rv)
|
||||
goto out;
|
||||
if (!elen || (elen > UINT32_MAX))
|
||||
{
|
||||
rv = KRB5_PARSE_MALFORMED;
|
||||
goto out;
|
||||
}
|
||||
enc.ciphertext.length = (unsigned int)elen;
|
||||
enc.ciphertext.data = malloc(elen);
|
||||
if (!enc.ciphertext.data)
|
||||
{
|
||||
rv = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rv = krb5_c_encrypt(ctx, keyblock, kusage, NULL, &data, &enc);
|
||||
|
||||
out->data = enc.ciphertext.data;
|
||||
out->length = enc.ciphertext.length;
|
||||
out:
|
||||
krb5_free_keyblock(ctx, keyblock);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static krb5_error_code kerb_do_decrypt(krb5_context ctx, const KERB_RPC_ENCRYPTION_KEY* key,
|
||||
krb5_keyusage kusage, const krb5_data* cipher,
|
||||
KERB_ASN1_DATA* plain)
|
||||
{
|
||||
WINPR_ASSERT(ctx);
|
||||
WINPR_ASSERT(key);
|
||||
WINPR_ASSERT(cipher);
|
||||
WINPR_ASSERT(cipher->length);
|
||||
WINPR_ASSERT(plain);
|
||||
|
||||
krb5_keyblock* keyblock = NULL;
|
||||
krb5_data data = { 0 };
|
||||
krb5_enc_data enc = { 0 };
|
||||
|
||||
krb5_error_code rv = RPC_ENCRYPTION_KEY_to_keyblock(ctx, key, &keyblock);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
enc.kvno = KRB5_PVNO;
|
||||
enc.enctype = (krb5_enctype)key->reserved2;
|
||||
enc.ciphertext.length = cipher->length;
|
||||
enc.ciphertext.data = cipher->data;
|
||||
|
||||
data.length = cipher->length;
|
||||
data.data = (char*)malloc(cipher->length);
|
||||
if (!data.data)
|
||||
{
|
||||
rv = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rv = krb5_c_decrypt(ctx, keyblock, kusage, NULL, &enc, &data);
|
||||
|
||||
plain->Asn1Buffer = (BYTE*)data.data;
|
||||
plain->Asn1BufferHints.count = data.length;
|
||||
out:
|
||||
krb5_free_keyblock(ctx, keyblock);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static BOOL rdpear_send_payload(RDPEAR_PLUGIN* rdpear, IWTSVirtualChannelCallback* pChannelCallback,
|
||||
RdpEarPackageType packageType, wStream* payload)
|
||||
{
|
||||
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
BOOL ret = FALSE;
|
||||
wStream* finalStream = NULL;
|
||||
SecBuffer cryptedBuffer = { 0 };
|
||||
wStream* unencodedContent = rdpear_encodePayload(packageType, payload);
|
||||
if (!unencodedContent)
|
||||
goto out;
|
||||
|
||||
const size_t unencodedLen = Stream_GetPosition(unencodedContent);
|
||||
|
||||
#if UINT32_MAX < SIZE_MAX
|
||||
if (unencodedLen > UINT32_MAX)
|
||||
goto out;
|
||||
#endif
|
||||
|
||||
SecBuffer inBuffer = { (ULONG)unencodedLen, SECBUFFER_DATA, Stream_Buffer(unencodedContent) };
|
||||
|
||||
if (!freerdp_nla_encrypt(rdpear->rdp_context, &inBuffer, &cryptedBuffer))
|
||||
goto out;
|
||||
|
||||
finalStream = Stream_New(NULL, 200);
|
||||
if (!finalStream)
|
||||
goto out;
|
||||
Stream_Write_UINT32(finalStream, 0x4EACC3C8); /* ProtocolMagic (4 bytes) */
|
||||
Stream_Write_UINT32(finalStream, cryptedBuffer.cbBuffer); /* Length (4 bytes) */
|
||||
Stream_Write_UINT32(finalStream, 0x00000000); /* Version (4 bytes) */
|
||||
Stream_Write_UINT32(finalStream, 0x00000000); /* Reserved (4 bytes) */
|
||||
Stream_Write_UINT64(finalStream, 0); /* TsPkgContext (8 bytes) */
|
||||
|
||||
/* payload */
|
||||
if (!Stream_EnsureRemainingCapacity(finalStream, cryptedBuffer.cbBuffer))
|
||||
goto out;
|
||||
|
||||
Stream_Write(finalStream, cryptedBuffer.pvBuffer, cryptedBuffer.cbBuffer);
|
||||
|
||||
const size_t pos = Stream_GetPosition(finalStream);
|
||||
#if UINT32_MAX < SIZE_MAX
|
||||
if (pos > UINT32_MAX)
|
||||
goto out;
|
||||
#endif
|
||||
|
||||
UINT status =
|
||||
callback->channel->Write(callback->channel, (ULONG)pos, Stream_Buffer(finalStream), NULL);
|
||||
ret = (status == CHANNEL_RC_OK);
|
||||
if (!ret)
|
||||
WLog_DBG(TAG, "rdpear_send_payload=0x%x", status);
|
||||
out:
|
||||
sspi_SecBufferFree(&cryptedBuffer);
|
||||
Stream_Free(unencodedContent, TRUE);
|
||||
Stream_Free(finalStream, TRUE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL rdpear_prepare_response(NdrContext* rcontext, UINT16 callId, UINT32 status,
|
||||
NdrContext** pwcontext, wStream* retStream)
|
||||
{
|
||||
WINPR_ASSERT(rcontext);
|
||||
WINPR_ASSERT(pwcontext);
|
||||
|
||||
BOOL ret = FALSE;
|
||||
*pwcontext = NULL;
|
||||
NdrContext* wcontext = ndr_context_copy(rcontext);
|
||||
if (!wcontext)
|
||||
return FALSE;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(retStream, sizeof(payloadHeader)))
|
||||
goto out;
|
||||
|
||||
Stream_Write(retStream, payloadHeader, sizeof(payloadHeader));
|
||||
|
||||
if (!ndr_write_header(wcontext, retStream) || !ndr_start_constructed(wcontext, retStream) ||
|
||||
!ndr_write_pickle(wcontext, retStream) || /* pickle header */
|
||||
!ndr_write_uint16(wcontext, retStream, callId) || /* callId */
|
||||
!ndr_write_uint16(wcontext, retStream, 0x0000) || /* align padding */
|
||||
!ndr_write_uint32(wcontext, retStream, status) || /* status */
|
||||
!ndr_write_uint16(wcontext, retStream, callId) || /* callId */
|
||||
!ndr_write_uint16(wcontext, retStream, 0x0000)) /* align padding */
|
||||
goto out;
|
||||
|
||||
*pwcontext = wcontext;
|
||||
ret = TRUE;
|
||||
out:
|
||||
if (!ret)
|
||||
ndr_context_destroy(&wcontext);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_version(NdrContext* rcontext, wStream* s, UINT32* pstatus, UINT32* pversion)
|
||||
{
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
|
||||
if (!ndr_read_uint32(rcontext, s, pversion))
|
||||
return TRUE;
|
||||
|
||||
WLog_DBG(TAG, "-> KerbNegotiateVersion(v=0x%x)", *pversion);
|
||||
*pstatus = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_ComputeTgsChecksum(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext, wStream* s,
|
||||
UINT32* pstatus, KERB_ASN1_DATA* resp)
|
||||
{
|
||||
ComputeTgsChecksumReq req = { 0 };
|
||||
krb5_checksum checksum = { 0 };
|
||||
wStream* asn1Payload = NULL;
|
||||
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
WLog_DBG(TAG, "-> ComputeTgsChecksum");
|
||||
|
||||
if (!ndr_read_ComputeTgsChecksumReq(rcontext, s, NULL, &req) ||
|
||||
!ndr_treat_deferred_read(rcontext, s))
|
||||
goto out;
|
||||
// ComputeTgsChecksumReq_dump(WLog_Get(""), WLOG_DEBUG, &req);
|
||||
|
||||
krb5_error_code rv =
|
||||
kerb_do_checksum(rdpear->krbContext, req.Key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
|
||||
(krb5_cksumtype)req.ChecksumType, req.requestBody, &checksum);
|
||||
if (rv)
|
||||
goto out;
|
||||
|
||||
asn1Payload = rdpear_enc_Checksum(req.ChecksumType, &checksum);
|
||||
if (!asn1Payload)
|
||||
goto out;
|
||||
|
||||
resp->Pdu = 8;
|
||||
resp->Asn1Buffer = Stream_Buffer(asn1Payload);
|
||||
const size_t pos = Stream_GetPosition(asn1Payload);
|
||||
if (pos > UINT32_MAX)
|
||||
goto out;
|
||||
resp->Asn1BufferHints.count = (UINT32)pos;
|
||||
*pstatus = 0;
|
||||
|
||||
out:
|
||||
ndr_destroy_ComputeTgsChecksumReq(rcontext, NULL, &req);
|
||||
krb5_free_checksum_contents(rdpear->krbContext, &checksum);
|
||||
Stream_Free(asn1Payload, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_BuildEncryptedAuthData(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext,
|
||||
wStream* s, UINT32* pstatus, KERB_ASN1_DATA* asn1)
|
||||
{
|
||||
BuildEncryptedAuthDataReq req = { 0 };
|
||||
krb5_data encrypted = { 0 };
|
||||
wStream* asn1Payload = NULL;
|
||||
krb5_error_code rv = 0;
|
||||
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
WLog_DBG(TAG, "-> BuildEncryptedAuthData");
|
||||
|
||||
if (!ndr_read_BuildEncryptedAuthDataReq(rcontext, s, NULL, &req) ||
|
||||
!ndr_treat_deferred_read(rcontext, s))
|
||||
goto out;
|
||||
|
||||
rv = kerb_do_encrypt(rdpear->krbContext, req.Key, (krb5_keyusage)req.KeyUsage,
|
||||
req.PlainAuthData, &encrypted);
|
||||
if (rv)
|
||||
goto out;
|
||||
|
||||
/* do the encoding */
|
||||
asn1Payload = rdpear_enc_EncryptedData(req.Key->reserved2, &encrypted);
|
||||
if (!asn1Payload)
|
||||
goto out;
|
||||
|
||||
// WLog_DBG(TAG, "rdpear_kerb_BuildEncryptedAuthData resp=");
|
||||
// winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(asn1Payload), Stream_GetPosition(asn1Payload));
|
||||
asn1->Pdu = 6;
|
||||
asn1->Asn1Buffer = Stream_Buffer(asn1Payload);
|
||||
const size_t pos = Stream_GetPosition(asn1Payload);
|
||||
if (pos > UINT32_MAX)
|
||||
goto out;
|
||||
asn1->Asn1BufferHints.count = (UINT32)pos;
|
||||
*pstatus = 0;
|
||||
|
||||
out:
|
||||
krb5_free_data_contents(rdpear->krbContext, &encrypted);
|
||||
ndr_destroy_BuildEncryptedAuthDataReq(rcontext, NULL, &req);
|
||||
Stream_Free(asn1Payload, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static char* KERB_RPC_UNICODESTR_to_charptr(const RPC_UNICODE_STRING* src)
|
||||
{
|
||||
WINPR_ASSERT(src);
|
||||
return ConvertWCharNToUtf8Alloc(src->Buffer, src->strLength, NULL);
|
||||
}
|
||||
|
||||
static BOOL extractAuthData(const KERB_ASN1_DATA* src, krb5_authdata* authData, BOOL* haveData)
|
||||
{
|
||||
WinPrAsn1Decoder dec = { 0 };
|
||||
WinPrAsn1Decoder dec2 = { 0 };
|
||||
WinPrAsn1Decoder dec3 = { 0 };
|
||||
WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, src->Asn1Buffer, src->Asn1BufferHints.count);
|
||||
BOOL error = FALSE;
|
||||
WinPrAsn1_INTEGER adType = 0;
|
||||
WinPrAsn1_OctetString os = { 0 };
|
||||
|
||||
*haveData = FALSE;
|
||||
if (!WinPrAsn1DecReadSequence(&dec, &dec2))
|
||||
return FALSE;
|
||||
|
||||
wStream subStream = WinPrAsn1DecGetStream(&dec2);
|
||||
if (!Stream_GetRemainingLength(&subStream))
|
||||
return TRUE;
|
||||
|
||||
if (!WinPrAsn1DecReadSequence(&dec2, &dec3))
|
||||
return FALSE;
|
||||
|
||||
if (!WinPrAsn1DecReadContextualInteger(&dec3, 0, &error, &adType) ||
|
||||
!WinPrAsn1DecReadContextualOctetString(&dec3, 1, &error, &os, FALSE))
|
||||
return FALSE;
|
||||
|
||||
if (os.len > UINT32_MAX)
|
||||
return FALSE;
|
||||
|
||||
authData->ad_type = adType;
|
||||
authData->length = (unsigned int)os.len;
|
||||
authData->contents = os.data;
|
||||
*haveData = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL extractChecksum(const KERB_ASN1_DATA* src, krb5_checksum* dst)
|
||||
{
|
||||
WinPrAsn1Decoder dec = { 0 };
|
||||
WinPrAsn1Decoder dec2 = { 0 };
|
||||
WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, src->Asn1Buffer, src->Asn1BufferHints.count);
|
||||
BOOL error = FALSE;
|
||||
WinPrAsn1_OctetString os;
|
||||
|
||||
if (!WinPrAsn1DecReadSequence(&dec, &dec2))
|
||||
return FALSE;
|
||||
|
||||
WinPrAsn1_INTEGER cksumtype = 0;
|
||||
if (!WinPrAsn1DecReadContextualInteger(&dec2, 0, &error, &cksumtype) ||
|
||||
!WinPrAsn1DecReadContextualOctetString(&dec2, 1, &error, &os, FALSE))
|
||||
return FALSE;
|
||||
|
||||
if (os.len > UINT32_MAX)
|
||||
return FALSE;
|
||||
dst->checksum_type = cksumtype;
|
||||
dst->length = (unsigned int)os.len;
|
||||
dst->contents = os.data;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define FILETIME_TO_UNIX_OFFSET_S 11644473600LL
|
||||
|
||||
static LONGLONG krb5_time_to_FILETIME(const krb5_timestamp* ts, krb5_int32 usec)
|
||||
{
|
||||
WINPR_ASSERT(ts);
|
||||
return (((*ts + FILETIME_TO_UNIX_OFFSET_S) * (1000LL * 1000LL) + usec) * 10LL);
|
||||
}
|
||||
|
||||
static void krb5_free_principal_contents(krb5_context ctx, krb5_principal principal)
|
||||
{
|
||||
WINPR_ASSERT(principal);
|
||||
krb5_free_data_contents(ctx, &principal->realm);
|
||||
krb5_free_data(ctx, principal->data);
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_CreateApReqAuthenticator(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext,
|
||||
wStream* s, UINT32* pstatus,
|
||||
CreateApReqAuthenticatorResp* resp)
|
||||
{
|
||||
krb5_error_code rv = 0;
|
||||
wStream* asn1EncodedAuth = NULL;
|
||||
CreateApReqAuthenticatorReq req = { 0 };
|
||||
krb5_data authenticator = { 0 };
|
||||
krb5_data* der = NULL;
|
||||
krb5_keyblock* subkey = NULL;
|
||||
krb5_principal_data client = { 0 };
|
||||
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
WLog_DBG(TAG, "-> CreateApReqAuthenticator");
|
||||
|
||||
if (!ndr_read_CreateApReqAuthenticatorReq(rcontext, s, NULL, &req) ||
|
||||
!ndr_treat_deferred_read(rcontext, s))
|
||||
goto out;
|
||||
|
||||
krb5_authdata authdata = { 0 };
|
||||
krb5_authdata* authDataPtr[2] = { &authdata, NULL };
|
||||
BOOL haveData = 0;
|
||||
|
||||
if (!extractAuthData(req.AuthData, &authdata, &haveData))
|
||||
{
|
||||
WLog_ERR(TAG, "error retrieving auth data");
|
||||
winpr_HexDump(TAG, WLOG_DEBUG, req.AuthData->Asn1Buffer,
|
||||
req.AuthData->Asn1BufferHints.count);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (req.SkewTime->QuadPart)
|
||||
{
|
||||
WLog_ERR(TAG, "!!!!! should handle SkewTime !!!!!");
|
||||
}
|
||||
|
||||
if (req.SubKey)
|
||||
{
|
||||
rv = RPC_ENCRYPTION_KEY_to_keyblock(rdpear->krbContext, req.SubKey, &subkey);
|
||||
if (rv)
|
||||
{
|
||||
WLog_ERR(TAG, "error importing subkey");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
krb5_authenticator authent = { .checksum = NULL,
|
||||
.subkey = NULL,
|
||||
.seq_number = req.SequenceNumber,
|
||||
.authorization_data = haveData ? authDataPtr : NULL };
|
||||
|
||||
client.type = req.ClientName->NameType;
|
||||
if (req.ClientName->nameHints.count > INT32_MAX)
|
||||
goto out;
|
||||
|
||||
client.length = (krb5_int32)req.ClientName->nameHints.count;
|
||||
client.data = calloc(req.ClientName->nameHints.count, sizeof(krb5_data));
|
||||
if (!client.data)
|
||||
goto out;
|
||||
|
||||
for (int i = 0; i < client.length; i++)
|
||||
{
|
||||
client.data[i].data = KERB_RPC_UNICODESTR_to_charptr(&req.ClientName->Names[i]);
|
||||
if (!client.data[i].data)
|
||||
goto out;
|
||||
client.data[i].length = (unsigned int)strnlen(client.data[i].data, UINT32_MAX);
|
||||
}
|
||||
client.realm.data = KERB_RPC_UNICODESTR_to_charptr(req.ClientRealm);
|
||||
if (!client.realm.data)
|
||||
goto out;
|
||||
client.realm.length = (unsigned int)strnlen(client.realm.data, UINT32_MAX);
|
||||
authent.client = &client;
|
||||
|
||||
krb5_checksum checksum;
|
||||
krb5_checksum* pchecksum = NULL;
|
||||
if (req.GssChecksum)
|
||||
{
|
||||
if (!extractChecksum(req.GssChecksum, &checksum))
|
||||
{
|
||||
WLog_ERR(TAG, "Error getting the checksum");
|
||||
goto out;
|
||||
}
|
||||
pchecksum = &checksum;
|
||||
}
|
||||
authent.checksum = pchecksum;
|
||||
|
||||
krb5_us_timeofday(rdpear->krbContext, &authent.ctime, &authent.cusec);
|
||||
|
||||
rv = encode_krb5_authenticator(&authent, &der);
|
||||
if (rv)
|
||||
{
|
||||
WLog_ERR(TAG, "error encoding authenticator");
|
||||
goto out;
|
||||
}
|
||||
|
||||
KERB_ASN1_DATA plain_authent = { .Pdu = 0,
|
||||
.Asn1Buffer = (BYTE*)der->data,
|
||||
.Asn1BufferHints = { .count = der->length } };
|
||||
|
||||
rv = kerb_do_encrypt(rdpear->krbContext, req.EncryptionKey, (krb5_keyusage)req.KeyUsage,
|
||||
&plain_authent, &authenticator);
|
||||
if (rv)
|
||||
{
|
||||
WLog_ERR(TAG, "error encrypting authenticator");
|
||||
goto out;
|
||||
}
|
||||
|
||||
asn1EncodedAuth = rdpear_enc_EncryptedData(req.EncryptionKey->reserved2, &authenticator);
|
||||
if (!asn1EncodedAuth)
|
||||
{
|
||||
WLog_ERR(TAG, "error encoding to ASN1");
|
||||
rv = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// WLog_DBG(TAG, "authenticator=");
|
||||
// winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(asn1EncodedAuth),
|
||||
// Stream_GetPosition(asn1EncodedAuth));
|
||||
|
||||
const size_t size = Stream_GetPosition(asn1EncodedAuth);
|
||||
if (size > UINT32_MAX)
|
||||
goto out;
|
||||
resp->Authenticator.Asn1BufferHints.count = (UINT32)size;
|
||||
resp->Authenticator.Asn1Buffer = Stream_Buffer(asn1EncodedAuth);
|
||||
resp->AuthenticatorTime.QuadPart = krb5_time_to_FILETIME(&authent.ctime, authent.cusec);
|
||||
*pstatus = 0;
|
||||
|
||||
out:
|
||||
resp->Authenticator.Pdu = 6;
|
||||
resp->KerbProtocolError = rv;
|
||||
krb5_free_principal_contents(rdpear->krbContext, &client);
|
||||
krb5_free_data(rdpear->krbContext, der);
|
||||
krb5_free_data_contents(rdpear->krbContext, &authenticator);
|
||||
if (subkey)
|
||||
krb5_free_keyblock(rdpear->krbContext, subkey);
|
||||
ndr_destroy_CreateApReqAuthenticatorReq(rcontext, NULL, &req);
|
||||
Stream_Free(asn1EncodedAuth, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpear_findEncryptedData(const KERB_ASN1_DATA* src, int* penctype, krb5_data* data)
|
||||
{
|
||||
WinPrAsn1Decoder dec = { 0 };
|
||||
WinPrAsn1Decoder dec2 = { 0 };
|
||||
WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, src->Asn1Buffer, src->Asn1BufferHints.count);
|
||||
BOOL error = FALSE;
|
||||
WinPrAsn1_INTEGER encType = 0;
|
||||
WinPrAsn1_OctetString os = { 0 };
|
||||
|
||||
if (!WinPrAsn1DecReadSequence(&dec, &dec2) ||
|
||||
!WinPrAsn1DecReadContextualInteger(&dec2, 0, &error, &encType) ||
|
||||
!WinPrAsn1DecReadContextualOctetString(&dec2, 2, &error, &os, FALSE))
|
||||
return FALSE;
|
||||
|
||||
if (os.len > UINT32_MAX)
|
||||
return FALSE;
|
||||
data->data = (char*)os.data;
|
||||
data->length = (unsigned int)os.len;
|
||||
*penctype = encType;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_UnpackKdcReplyBody(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext, wStream* s,
|
||||
UINT32* pstatus, UnpackKdcReplyBodyResp* resp)
|
||||
{
|
||||
UnpackKdcReplyBodyReq req = { 0 };
|
||||
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
|
||||
if (!ndr_read_UnpackKdcReplyBodyReq(rcontext, s, NULL, &req) ||
|
||||
!ndr_treat_deferred_read(rcontext, s))
|
||||
goto out;
|
||||
|
||||
if (req.StrengthenKey)
|
||||
{
|
||||
WLog_ERR(TAG, "StrengthenKey not supported yet");
|
||||
goto out;
|
||||
}
|
||||
|
||||
WLog_DBG(TAG, "-> UnpackKdcReplyBody: KeyUsage=0x%x PDU=0x%x", req.KeyUsage, req.Pdu);
|
||||
// WLog_DBG(TAG, "encryptedPayload=");
|
||||
// winpr_HexDump(TAG, WLOG_DEBUG, req.EncryptedData->Asn1Buffer,
|
||||
// req.EncryptedData->Asn1BufferHints.count);
|
||||
|
||||
krb5_data asn1Data = { 0 };
|
||||
int encType = 0;
|
||||
if (!rdpear_findEncryptedData(req.EncryptedData, &encType, &asn1Data) || !asn1Data.length)
|
||||
goto out;
|
||||
|
||||
resp->KerbProtocolError = kerb_do_decrypt(
|
||||
rdpear->krbContext, req.Key, (krb5_keyusage)req.KeyUsage, &asn1Data, &resp->ReplyBody);
|
||||
resp->ReplyBody.Pdu = req.Pdu;
|
||||
|
||||
*pstatus = 0;
|
||||
|
||||
out:
|
||||
ndr_destroy_UnpackKdcReplyBodyReq(rcontext, NULL, &req);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_DecryptApReply(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext, wStream* s,
|
||||
UINT32* pstatus, KERB_ASN1_DATA* resp)
|
||||
{
|
||||
DecryptApReplyReq req = { 0 };
|
||||
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
if (!ndr_read_DecryptApReplyReq(rcontext, s, NULL, &req) ||
|
||||
!ndr_treat_deferred_read(rcontext, s))
|
||||
goto out;
|
||||
|
||||
WLog_DBG(TAG, "-> DecryptApReply");
|
||||
// winpr_HexDump(TAG, WLOG_DEBUG, req.EncryptedReply->Asn1Buffer,
|
||||
// req.EncryptedReply->Asn1BufferHints.count);
|
||||
|
||||
krb5_data asn1Data = { 0 };
|
||||
int encType = 0;
|
||||
if (!rdpear_findEncryptedData(req.EncryptedReply, &encType, &asn1Data) || !asn1Data.length)
|
||||
goto out;
|
||||
|
||||
resp->Pdu = 0x31;
|
||||
krb5_error_code rv =
|
||||
kerb_do_decrypt(rdpear->krbContext, req.Key, KRB5_KEYUSAGE_AP_REP_ENCPART, &asn1Data, resp);
|
||||
if (rv != 0)
|
||||
{
|
||||
WLog_ERR(TAG, "error decrypting");
|
||||
goto out;
|
||||
}
|
||||
|
||||
// WLog_DBG(TAG, "response=");
|
||||
// winpr_HexDump(TAG, WLOG_DEBUG, resp->Asn1Buffer, resp->Asn1BufferHints.count);
|
||||
*pstatus = 0;
|
||||
out:
|
||||
ndr_destroy_DecryptApReplyReq(rcontext, NULL, &req);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpear_kerb_PackApReply(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext, wStream* s,
|
||||
UINT32* pstatus, PackApReplyResp* resp)
|
||||
{
|
||||
PackApReplyReq req = { 0 };
|
||||
krb5_data asn1Data = { 0 };
|
||||
krb5_data* out = NULL;
|
||||
|
||||
WLog_DBG(TAG, "-> PackApReply");
|
||||
*pstatus = ERROR_INVALID_DATA;
|
||||
if (!ndr_read_PackApReplyReq(rcontext, s, NULL, &req) || !ndr_treat_deferred_read(rcontext, s))
|
||||
goto out;
|
||||
|
||||
krb5_error_code rv = kerb_do_encrypt(rdpear->krbContext, req.SessionKey,
|
||||
KRB5_KEYUSAGE_AP_REP_ENCPART, req.ReplyBody, &asn1Data);
|
||||
if (rv)
|
||||
goto out;
|
||||
|
||||
krb5_ap_rep reply;
|
||||
reply.enc_part.kvno = KRB5_PVNO;
|
||||
reply.enc_part.enctype = (krb5_enctype)req.SessionKey->reserved2;
|
||||
reply.enc_part.ciphertext.length = asn1Data.length;
|
||||
reply.enc_part.ciphertext.data = asn1Data.data;
|
||||
|
||||
rv = encode_krb5_ap_rep(&reply, &out);
|
||||
if (rv)
|
||||
goto out;
|
||||
|
||||
resp->PackedReply = (BYTE*)out->data;
|
||||
resp->PackedReplyHints.count = out->length;
|
||||
*pstatus = 0;
|
||||
out:
|
||||
free(out);
|
||||
krb5_free_data_contents(rdpear->krbContext, &asn1Data);
|
||||
ndr_destroy_PackApReplyReq(rcontext, NULL, &req);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT rdpear_decode_payload(RDPEAR_PLUGIN* rdpear,
|
||||
IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
|
||||
{
|
||||
UINT ret = ERROR_INVALID_DATA;
|
||||
NdrContext* context = NULL;
|
||||
NdrContext* wcontext = NULL;
|
||||
UINT32 status = 0;
|
||||
|
||||
UINT32 uint32Resp = 0;
|
||||
KERB_ASN1_DATA asn1Data = { 0 };
|
||||
CreateApReqAuthenticatorResp createApReqAuthenticatorResp = { 0 };
|
||||
UnpackKdcReplyBodyResp unpackKdcReplyBodyResp = { 0 };
|
||||
PackApReplyResp packApReplyResp = { 0 };
|
||||
|
||||
void* resp = NULL;
|
||||
NdrMessageType respDescr = NULL;
|
||||
|
||||
wStream* respStream = Stream_New(NULL, 500);
|
||||
if (!respStream)
|
||||
goto out;
|
||||
|
||||
Stream_Seek(s, 16); /* skip first 16 bytes */
|
||||
|
||||
wStream commandStream = { 0 };
|
||||
UINT16 callId = 0;
|
||||
UINT16 callId2 = 0;
|
||||
|
||||
context = ndr_read_header(s);
|
||||
if (!context || !ndr_read_constructed(context, s, &commandStream) ||
|
||||
!ndr_read_pickle(context, &commandStream) ||
|
||||
!ndr_read_uint16(context, &commandStream, &callId) ||
|
||||
!ndr_read_uint16(context, &commandStream, &callId2) || (callId != callId2))
|
||||
goto out;
|
||||
|
||||
ret = CHANNEL_RC_NOT_OPEN;
|
||||
switch (callId)
|
||||
{
|
||||
case RemoteCallKerbNegotiateVersion:
|
||||
resp = &uint32Resp;
|
||||
respDescr = ndr_uint32_descr();
|
||||
|
||||
if (rdpear_kerb_version(context, &commandStream, &status, &uint32Resp))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
case RemoteCallKerbCreateApReqAuthenticator:
|
||||
resp = &createApReqAuthenticatorResp;
|
||||
respDescr = ndr_CreateApReqAuthenticatorResp_descr();
|
||||
|
||||
if (rdpear_kerb_CreateApReqAuthenticator(rdpear, context, &commandStream, &status,
|
||||
&createApReqAuthenticatorResp))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
case RemoteCallKerbDecryptApReply:
|
||||
resp = &asn1Data;
|
||||
respDescr = ndr_KERB_ASN1_DATA_descr();
|
||||
|
||||
if (rdpear_kerb_DecryptApReply(rdpear, context, &commandStream, &status, &asn1Data))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
case RemoteCallKerbComputeTgsChecksum:
|
||||
resp = &asn1Data;
|
||||
respDescr = ndr_KERB_ASN1_DATA_descr();
|
||||
|
||||
if (rdpear_kerb_ComputeTgsChecksum(rdpear, context, &commandStream, &status, &asn1Data))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
case RemoteCallKerbBuildEncryptedAuthData:
|
||||
resp = &asn1Data;
|
||||
respDescr = ndr_KERB_ASN1_DATA_descr();
|
||||
|
||||
if (rdpear_kerb_BuildEncryptedAuthData(rdpear, context, &commandStream, &status,
|
||||
&asn1Data))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
case RemoteCallKerbUnpackKdcReplyBody:
|
||||
resp = &unpackKdcReplyBodyResp;
|
||||
respDescr = ndr_UnpackKdcReplyBodyResp_descr();
|
||||
|
||||
if (rdpear_kerb_UnpackKdcReplyBody(rdpear, context, &commandStream, &status,
|
||||
&unpackKdcReplyBodyResp))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
case RemoteCallKerbPackApReply:
|
||||
resp = &packApReplyResp;
|
||||
respDescr = ndr_PackApReplyResp_descr();
|
||||
|
||||
if (rdpear_kerb_PackApReply(rdpear, context, &commandStream, &status, &packApReplyResp))
|
||||
ret = CHANNEL_RC_OK;
|
||||
break;
|
||||
|
||||
case RemoteCallNtlmNegotiateVersion:
|
||||
WLog_ERR(TAG, "don't wanna support NTLM");
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_DBG(TAG, "Unhandled callId=0x%x", callId);
|
||||
winpr_HexDump(TAG, WLOG_DEBUG, Stream_PointerAs(&commandStream, BYTE),
|
||||
Stream_GetRemainingLength(&commandStream));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rdpear_prepare_response(context, callId, status, &wcontext, respStream))
|
||||
goto out;
|
||||
|
||||
if (resp && respDescr)
|
||||
{
|
||||
WINPR_ASSERT(respDescr->writeFn);
|
||||
|
||||
BOOL r = respDescr->writeFn(wcontext, respStream, NULL, resp) &&
|
||||
ndr_treat_deferred_write(wcontext, respStream);
|
||||
|
||||
if (respDescr->destroyFn)
|
||||
respDescr->destroyFn(wcontext, NULL, resp);
|
||||
|
||||
if (!r)
|
||||
{
|
||||
WLog_DBG(TAG, "!writeFn || !ndr_treat_deferred_write");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ndr_end_constructed(wcontext, respStream) ||
|
||||
!rdpear_send_payload(rdpear, pChannelCallback, RDPEAR_PACKAGE_KERBEROS, respStream))
|
||||
{
|
||||
WLog_DBG(TAG, "rdpear_send_payload !!!!!!!!");
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
if (context)
|
||||
ndr_context_destroy(&context);
|
||||
|
||||
if (wcontext)
|
||||
ndr_context_destroy(&wcontext);
|
||||
|
||||
if (respStream)
|
||||
Stream_Free(respStream, TRUE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static UINT rdpear_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
|
||||
{
|
||||
GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
|
||||
WINPR_ASSERT(callback);
|
||||
UINT ret = ERROR_INVALID_DATA;
|
||||
|
||||
// winpr_HexDump(TAG, WLOG_DEBUG, Stream_PointerAs(s, BYTE), Stream_GetRemainingLength(s));
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
UINT32 protocolMagic = 0;
|
||||
UINT32 Length = 0;
|
||||
UINT32 Version = 0;
|
||||
Stream_Read_UINT32(s, protocolMagic);
|
||||
if (protocolMagic != 0x4EACC3C8)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, Length);
|
||||
|
||||
Stream_Read_UINT32(s, Version);
|
||||
if (Version != 0x00000000)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, 4); /* Reserved (4 bytes) */
|
||||
Stream_Seek(s, 8); /* TsPkgContext (8 bytes) */
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, Length))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
SecBuffer inBuffer = { Length, SECBUFFER_TOKEN, Stream_PointerAs(s, void) };
|
||||
SecBuffer decrypted = { 0 };
|
||||
|
||||
RDPEAR_PLUGIN* rdpear = (RDPEAR_PLUGIN*)callback->plugin;
|
||||
WINPR_ASSERT(rdpear);
|
||||
if (!freerdp_nla_decrypt(rdpear->rdp_context, &inBuffer, &decrypted))
|
||||
goto out;
|
||||
|
||||
WinPrAsn1Decoder dec = { 0 };
|
||||
WinPrAsn1Decoder dec2 = { 0 };
|
||||
wStream decodedStream = { 0 };
|
||||
Stream_StaticInit(&decodedStream, decrypted.pvBuffer, decrypted.cbBuffer);
|
||||
WinPrAsn1Decoder_Init(&dec, WINPR_ASN1_DER, &decodedStream);
|
||||
|
||||
if (!WinPrAsn1DecReadSequence(&dec, &dec2))
|
||||
goto out;
|
||||
|
||||
WinPrAsn1_OctetString packageName = { 0 };
|
||||
WinPrAsn1_OctetString payload = { 0 };
|
||||
BOOL error = 0;
|
||||
if (!WinPrAsn1DecReadContextualOctetString(&dec2, 1, &error, &packageName, FALSE))
|
||||
goto out;
|
||||
|
||||
if (!WinPrAsn1DecReadContextualOctetString(&dec2, 2, &error, &payload, FALSE))
|
||||
goto out;
|
||||
|
||||
wStream payloadStream = { 0 };
|
||||
Stream_StaticInit(&payloadStream, payload.data, payload.len);
|
||||
|
||||
ret = rdpear_decode_payload(rdpear, pChannelCallback, &payloadStream);
|
||||
out:
|
||||
sspi_SecBufferFree(&decrypted);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpear_on_open(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
WINPR_UNUSED(pChannelCallback);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpear_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
WINPR_UNUSED(pChannelCallback);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
|
||||
{
|
||||
WINPR_ASSERT(base);
|
||||
|
||||
RDPEAR_PLUGIN* rdpear = (RDPEAR_PLUGIN*)base;
|
||||
krb5_free_context(rdpear->krbContext);
|
||||
}
|
||||
|
||||
static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext, rdpSettings* settings)
|
||||
{
|
||||
WINPR_ASSERT(base);
|
||||
WINPR_UNUSED(settings);
|
||||
|
||||
RDPEAR_PLUGIN* rdpear = (RDPEAR_PLUGIN*)base;
|
||||
rdpear->rdp_context = rcontext;
|
||||
if (krb5_init_context(&rdpear->krbContext))
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static const IWTSVirtualChannelCallback rdpear_callbacks = { rdpear_on_data_received,
|
||||
rdpear_on_open, rdpear_on_close,
|
||||
NULL };
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT rdpear_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, RDPEAR_DVC_CHANNEL_NAME,
|
||||
sizeof(RDPEAR_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
|
||||
&rdpear_callbacks, init_plugin_cb, terminate_plugin_cb);
|
||||
}
|
58
channels/rdpear/common/CMakeLists.txt
Normal file
58
channels/rdpear/common/CMakeLists.txt
Normal file
@ -0,0 +1,58 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2024 David Fort <contact@hardening-consulting.com>
|
||||
#
|
||||
# Licensed 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.
|
||||
|
||||
add_library(rdpear-common INTERFACE)
|
||||
add_library(rdpear-common-obj OBJECT
|
||||
ndr.c
|
||||
rdpear_asn1.c
|
||||
rdpear_common.c
|
||||
rdpear-common/ndr.h
|
||||
rdpear-common/rdpear_asn1.h
|
||||
rdpear-common/rdpear_common.h
|
||||
)
|
||||
|
||||
target_include_directories(rdpear-common
|
||||
SYSTEM
|
||||
INTERFACE ${KRB5_INCLUDE_DIRS}
|
||||
)
|
||||
target_compile_options(rdpear-common
|
||||
INTERFACE
|
||||
${KRB5_CFLAGS}
|
||||
)
|
||||
target_link_options(rdpear-common
|
||||
INTERFACE
|
||||
${KRB5_LDFLAGS}
|
||||
)
|
||||
|
||||
target_include_directories(rdpear-common-obj
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
target_include_directories(rdpear-common-obj
|
||||
SYSTEM
|
||||
PRIVATE ${KRB5_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_directories(rdpear-common INTERFACE ${KRB5_LIBRARY_DIRS})
|
||||
target_link_libraries(rdpear-common INTERFACE
|
||||
${KRB5_LIBRARIES}
|
||||
$<TARGET_OBJECTS:rdpear-common-obj>
|
||||
)
|
||||
|
||||
channel_install(rdpear-common ${FREERDP_ADDIN_PATH} "FreeRDPTargets")
|
||||
|
||||
if (BUILD_TESTING_INTERNAL OR BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
994
channels/rdpear/common/ndr.c
Normal file
994
channels/rdpear/common/ndr.c
Normal file
@ -0,0 +1,994 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Authentication redirection virtual channel
|
||||
*
|
||||
* Copyright 2024 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed 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 <winpr/assert.h>
|
||||
#include <winpr/collections.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
|
||||
#include <rdpear-common/ndr.h>
|
||||
|
||||
#define TAG FREERDP_TAG("ndr")
|
||||
|
||||
#define NDR_MAX_CONSTRUCTS 16
|
||||
#define NDR_MAX_DEFERRED 50
|
||||
|
||||
struct NdrContext_s
|
||||
{
|
||||
BYTE version;
|
||||
BOOL bigEndianDrep;
|
||||
size_t alignBytes;
|
||||
|
||||
int currentLevel;
|
||||
size_t indentLevels[16];
|
||||
|
||||
int constructLevel;
|
||||
size_t constructs[NDR_MAX_CONSTRUCTS];
|
||||
|
||||
wHashTable* refPointers;
|
||||
size_t ndeferred;
|
||||
NdrDeferredEntry deferred[NDR_MAX_DEFERRED];
|
||||
|
||||
UINT32 refIdCounter;
|
||||
};
|
||||
|
||||
NdrContext* ndr_context_new(BOOL bigEndianDrep, BYTE version)
|
||||
{
|
||||
NdrContext* ret = calloc(1, sizeof(*ret));
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ret->version = version;
|
||||
ret->bigEndianDrep = bigEndianDrep;
|
||||
ret->alignBytes = 4;
|
||||
ret->refPointers = HashTable_New(FALSE);
|
||||
if (!ret->refPointers)
|
||||
{
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ndr_context_reset(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ndr_context_reset(NdrContext* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
context->currentLevel = 0;
|
||||
context->constructLevel = -1;
|
||||
memset(context->indentLevels, 0, sizeof(context->indentLevels));
|
||||
|
||||
if (context->refPointers)
|
||||
HashTable_Clear(context->refPointers);
|
||||
context->ndeferred = 0;
|
||||
context->refIdCounter = 0x20000;
|
||||
}
|
||||
|
||||
NdrContext* ndr_context_copy(const NdrContext* src)
|
||||
{
|
||||
WINPR_ASSERT(src);
|
||||
|
||||
NdrContext* ret = calloc(1, sizeof(*ret));
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
*ret = *src;
|
||||
|
||||
ret->refPointers = HashTable_New(FALSE);
|
||||
if (!ret->refPointers)
|
||||
{
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ndr_context_reset(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ndr_context_free(NdrContext* context)
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
HashTable_Free(context->refPointers);
|
||||
free(context);
|
||||
}
|
||||
}
|
||||
|
||||
static void ndr_context_bytes_read(NdrContext* context, size_t len)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
context->indentLevels[context->currentLevel] += len;
|
||||
}
|
||||
|
||||
static void ndr_context_bytes_written(NdrContext* context, size_t len)
|
||||
{
|
||||
ndr_context_bytes_read(context, len);
|
||||
}
|
||||
|
||||
NdrContext* ndr_read_header(wStream* s)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
|
||||
return NULL;
|
||||
|
||||
BYTE version = Stream_Get_UINT8(s);
|
||||
BYTE drep = Stream_Get_UINT8(s);
|
||||
UINT16 headerLen = Stream_Get_UINT16(s);
|
||||
|
||||
if (headerLen < 4 || !Stream_CheckAndLogRequiredLength(TAG, s, headerLen - 4))
|
||||
return NULL;
|
||||
|
||||
/* skip filler */
|
||||
Stream_Seek(s, headerLen - 4);
|
||||
|
||||
return ndr_context_new((drep != 0x10), version);
|
||||
}
|
||||
|
||||
BOOL ndr_write_header(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 8))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT8(s, context->version);
|
||||
Stream_Write_UINT8(s, context->bigEndianDrep ? 0x00 : 0x10);
|
||||
Stream_Write_UINT16(s, 0x8); /* header len */
|
||||
|
||||
BYTE filler[] = { 0xcc, 0xcc, 0xcc, 0xcc };
|
||||
Stream_Write(s, filler, sizeof(filler));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_skip_bytes(NdrContext* context, wStream* s, size_t nbytes)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, nbytes))
|
||||
return FALSE;
|
||||
|
||||
context->indentLevels[context->currentLevel] += nbytes;
|
||||
Stream_Seek(s, nbytes);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_read_align(NdrContext* context, wStream* s, size_t sz)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
size_t rest = context->indentLevels[context->currentLevel] % sz;
|
||||
if (rest)
|
||||
{
|
||||
size_t padding = (sz - rest);
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, padding))
|
||||
return FALSE;
|
||||
|
||||
Stream_Seek(s, padding);
|
||||
context->indentLevels[context->currentLevel] += padding;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_write_align(NdrContext* context, wStream* s, size_t sz)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
size_t rest = context->indentLevels[context->currentLevel] % sz;
|
||||
if (rest)
|
||||
{
|
||||
size_t padding = (sz - rest);
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, padding))
|
||||
return FALSE;
|
||||
|
||||
Stream_Zero(s, padding);
|
||||
context->indentLevels[context->currentLevel] += padding;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_read_pickle(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
UINT32 v = 0;
|
||||
|
||||
/* NDR format label */
|
||||
if (!ndr_read_uint32(context, s, &v) || v != 0x20000)
|
||||
return FALSE;
|
||||
|
||||
return ndr_read_uint32(context, s, &v); // padding
|
||||
}
|
||||
|
||||
BOOL ndr_write_pickle(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
/* NDR format label */
|
||||
if (!ndr_write_uint32(context, s, 0x20000))
|
||||
return FALSE;
|
||||
|
||||
ndr_write_uint32(context, s, 0); /* padding */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_read_constructed(NdrContext* context, wStream* s, wStream* target)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
UINT32 len = 0;
|
||||
|
||||
/* len */
|
||||
if (!ndr_read_uint32(context, s, &len))
|
||||
return FALSE;
|
||||
|
||||
/* padding */
|
||||
if (!ndr_skip_bytes(context, s, 4))
|
||||
return FALSE;
|
||||
|
||||
/* payload */
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, len))
|
||||
return FALSE;
|
||||
|
||||
Stream_StaticInit(target, Stream_PointerAs(s, BYTE), len);
|
||||
Stream_Seek(s, len);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_start_constructed(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!Stream_EnsureCapacity(s, 8))
|
||||
return FALSE;
|
||||
|
||||
if (context->constructLevel == NDR_MAX_CONSTRUCTS)
|
||||
return FALSE;
|
||||
|
||||
context->constructLevel++;
|
||||
context->constructs[context->constructLevel] = Stream_GetPosition(s);
|
||||
|
||||
Stream_Zero(s, 8);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_end_constructed(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(context->constructs);
|
||||
WINPR_ASSERT(context->constructLevel >= 0);
|
||||
|
||||
size_t offset = context->constructs[context->constructLevel];
|
||||
|
||||
wStream staticS = { 0 };
|
||||
Stream_StaticInit(&staticS, Stream_Buffer(s) + offset, 4);
|
||||
|
||||
/* len */
|
||||
const size_t len = Stream_GetPosition(s) - (offset + 8);
|
||||
if (len > UINT32_MAX)
|
||||
return FALSE;
|
||||
if (!ndr_write_uint32(context, &staticS, (UINT32)len))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static size_t ndr_hintsCount(NdrMessageType msgType, const void* hints)
|
||||
{
|
||||
WINPR_ASSERT(msgType);
|
||||
|
||||
switch (msgType->arity)
|
||||
{
|
||||
case NDR_ARITY_SIMPLE:
|
||||
return 1;
|
||||
case NDR_ARITY_ARRAYOF:
|
||||
WINPR_ASSERT(hints);
|
||||
return ((const NdrArrayHints*)hints)->count;
|
||||
case NDR_ARITY_VARYING_ARRAYOF:
|
||||
WINPR_ASSERT(hints);
|
||||
return ((const NdrVaryingArrayHints*)hints)->maxLength;
|
||||
default:
|
||||
WINPR_ASSERT(0 && "unknown arity");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL ndr_read_uint8(NdrContext* context, wStream* s, BYTE* v)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
|
||||
return FALSE;
|
||||
|
||||
Stream_Read_UINT8(s, *v);
|
||||
|
||||
ndr_context_bytes_read(context, 1);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_read_uint8_(NdrContext* context, wStream* s, const void* hints, void* v)
|
||||
{
|
||||
WINPR_UNUSED(hints);
|
||||
return ndr_read_uint8(context, s, (BYTE*)v);
|
||||
}
|
||||
|
||||
BOOL ndr_write_uint8(NdrContext* context, wStream* s, BYTE v)
|
||||
{
|
||||
if (!Stream_EnsureRemainingCapacity(s, 1))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write_UINT8(s, v);
|
||||
ndr_context_bytes_written(context, 1);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_write_uint8_(NdrContext* context, wStream* s, const void* hints, const void* v)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(v);
|
||||
WINPR_UNUSED(hints);
|
||||
|
||||
return ndr_write_uint8(context, s, *(const BYTE*)v);
|
||||
}
|
||||
|
||||
const static NdrMessageDescr uint8_descr = { NDR_ARITY_SIMPLE, 1, ndr_read_uint8_,
|
||||
ndr_write_uint8_, NULL, NULL };
|
||||
|
||||
NdrMessageType ndr_uint8_descr(void)
|
||||
{
|
||||
return &uint8_descr;
|
||||
}
|
||||
|
||||
#define SIMPLE_TYPE_IMPL(UPPERTYPE, LOWERTYPE) \
|
||||
BOOL ndr_read_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE* v) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
\
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UPPERTYPE))) \
|
||||
return FALSE; \
|
||||
\
|
||||
if (!ndr_read_align(context, s, sizeof(UPPERTYPE))) \
|
||||
return FALSE; \
|
||||
\
|
||||
if (context->bigEndianDrep) \
|
||||
Stream_Read_##UPPERTYPE##_BE(s, *v); \
|
||||
else \
|
||||
Stream_Read_##UPPERTYPE(s, *v); \
|
||||
\
|
||||
ndr_context_bytes_read(context, sizeof(UPPERTYPE)); \
|
||||
return TRUE; \
|
||||
} \
|
||||
\
|
||||
BOOL ndr_read_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, void* v) \
|
||||
{ \
|
||||
WINPR_UNUSED(hints); \
|
||||
return ndr_read_##LOWERTYPE(context, s, (UPPERTYPE*)v); \
|
||||
} \
|
||||
\
|
||||
BOOL ndr_write_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE v) \
|
||||
{ \
|
||||
if (!ndr_write_align(context, s, sizeof(UPPERTYPE)) || \
|
||||
!Stream_EnsureRemainingCapacity(s, sizeof(UPPERTYPE))) \
|
||||
return FALSE; \
|
||||
\
|
||||
if (context->bigEndianDrep) \
|
||||
Stream_Write_##UPPERTYPE##_BE(s, v); \
|
||||
else \
|
||||
Stream_Write_##UPPERTYPE(s, v); \
|
||||
\
|
||||
ndr_context_bytes_written(context, sizeof(UPPERTYPE)); \
|
||||
return TRUE; \
|
||||
} \
|
||||
\
|
||||
BOOL ndr_write_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, \
|
||||
const void* v) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
WINPR_ASSERT(s); \
|
||||
WINPR_ASSERT(v); \
|
||||
WINPR_UNUSED(hints); \
|
||||
\
|
||||
return ndr_write_##LOWERTYPE(context, s, *(const UPPERTYPE*)v); \
|
||||
} \
|
||||
\
|
||||
const NdrMessageDescr ndr_##LOWERTYPE##_descr_s = { NDR_ARITY_SIMPLE, \
|
||||
sizeof(UPPERTYPE), \
|
||||
ndr_read_##LOWERTYPE##_, \
|
||||
ndr_write_##LOWERTYPE##_, \
|
||||
NULL, \
|
||||
NULL }; \
|
||||
\
|
||||
NdrMessageType ndr_##LOWERTYPE##_descr(void) \
|
||||
{ \
|
||||
return &ndr_##LOWERTYPE##_descr_s; \
|
||||
}
|
||||
|
||||
SIMPLE_TYPE_IMPL(UINT32, uint32)
|
||||
SIMPLE_TYPE_IMPL(UINT16, uint16)
|
||||
SIMPLE_TYPE_IMPL(UINT64, uint64)
|
||||
|
||||
#define ARRAY_OF_TYPE_IMPL(TYPE, UPPERTYPE) \
|
||||
BOOL ndr_read_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, void* v) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
WINPR_ASSERT(s); \
|
||||
WINPR_ASSERT(hints); \
|
||||
return ndr_read_uconformant_array(context, s, hints, ndr_##TYPE##_descr(), v); \
|
||||
} \
|
||||
\
|
||||
BOOL ndr_write_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, \
|
||||
const void* v) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
WINPR_ASSERT(s); \
|
||||
WINPR_ASSERT(hints); \
|
||||
const NdrArrayHints* ahints = (const NdrArrayHints*)hints; \
|
||||
return ndr_write_uconformant_array(context, s, ahints->count, ndr_##TYPE##_descr(), v); \
|
||||
} \
|
||||
void ndr_destroy_##TYPE##Array(NdrContext* context, const void* hints, void* obj) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
WINPR_ASSERT(obj); \
|
||||
WINPR_ASSERT(hints); \
|
||||
const NdrArrayHints* ahints = (const NdrArrayHints*)hints; \
|
||||
NdrMessageType descr = ndr_##TYPE##_descr(); \
|
||||
if (descr->destroyFn) \
|
||||
{ \
|
||||
UPPERTYPE* ptr = (UPPERTYPE*)obj; \
|
||||
for (UINT32 i = 0; i < ahints->count; i++, ptr++) \
|
||||
descr->destroyFn(context, NULL, ptr); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
const NdrMessageDescr ndr_##TYPE##Array_descr_s = { \
|
||||
NDR_ARITY_ARRAYOF, sizeof(UPPERTYPE), ndr_read_##TYPE##Array, \
|
||||
ndr_write_##TYPE##Array, ndr_destroy_##TYPE##Array, NULL \
|
||||
}; \
|
||||
\
|
||||
NdrMessageType ndr_##TYPE##Array_descr(void) \
|
||||
{ \
|
||||
return &ndr_##TYPE##Array_descr_s; \
|
||||
} \
|
||||
\
|
||||
BOOL ndr_read_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
|
||||
void* v) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
WINPR_ASSERT(s); \
|
||||
WINPR_ASSERT(hints); \
|
||||
return ndr_read_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
|
||||
ndr_##TYPE##_descr(), v); \
|
||||
} \
|
||||
BOOL ndr_write_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
|
||||
const void* v) \
|
||||
{ \
|
||||
WINPR_ASSERT(context); \
|
||||
WINPR_ASSERT(s); \
|
||||
WINPR_ASSERT(hints); \
|
||||
return ndr_write_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
|
||||
ndr_##TYPE##_descr(), v); \
|
||||
} \
|
||||
\
|
||||
const NdrMessageDescr ndr_##TYPE##VaryingArray_descr_s = { NDR_ARITY_VARYING_ARRAYOF, \
|
||||
sizeof(UPPERTYPE), \
|
||||
ndr_read_##TYPE##VaryingArray, \
|
||||
ndr_write_##TYPE##VaryingArray, \
|
||||
NULL, \
|
||||
NULL }; \
|
||||
\
|
||||
NdrMessageType ndr_##TYPE##VaryingArray_descr(void) \
|
||||
{ \
|
||||
return &ndr_##TYPE##VaryingArray_descr_s; \
|
||||
}
|
||||
|
||||
ARRAY_OF_TYPE_IMPL(uint8, BYTE)
|
||||
ARRAY_OF_TYPE_IMPL(uint16, UINT16)
|
||||
|
||||
BOOL ndr_read_wchar(NdrContext* context, wStream* s, WCHAR* ptr)
|
||||
{
|
||||
return ndr_read_uint16(context, s, (UINT16*)ptr);
|
||||
}
|
||||
|
||||
BOOL ndr_read_uconformant_varying_array(NdrContext* context, wStream* s,
|
||||
const NdrVaryingArrayHints* hints, NdrMessageType itemType,
|
||||
void* ptarget)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(hints);
|
||||
WINPR_ASSERT(itemType);
|
||||
WINPR_ASSERT(ptarget);
|
||||
|
||||
UINT32 maxCount = 0;
|
||||
UINT32 offset = 0;
|
||||
UINT32 length = 0;
|
||||
|
||||
if (!ndr_read_uint32(context, s, &maxCount) || !ndr_read_uint32(context, s, &offset) ||
|
||||
!ndr_read_uint32(context, s, &length))
|
||||
return FALSE;
|
||||
|
||||
if ((length * itemType->itemSize) < hints->length)
|
||||
return FALSE;
|
||||
|
||||
if ((maxCount * itemType->itemSize) < hints->maxLength)
|
||||
return FALSE;
|
||||
|
||||
BYTE* target = (BYTE*)ptarget;
|
||||
for (UINT32 i = 0; i < length; i++, target += itemType->itemSize)
|
||||
{
|
||||
if (!itemType->readFn(context, s, NULL, target))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return ndr_read_align(context, s, 4);
|
||||
}
|
||||
|
||||
BOOL ndr_write_uconformant_varying_array(NdrContext* context, wStream* s,
|
||||
const NdrVaryingArrayHints* hints, NdrMessageType itemType,
|
||||
const void* psrc)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(hints);
|
||||
WINPR_ASSERT(itemType);
|
||||
WINPR_ASSERT(psrc);
|
||||
|
||||
if (!ndr_write_uint32(context, s, hints->maxLength) || !ndr_write_uint32(context, s, 0) ||
|
||||
!ndr_write_uint32(context, s, hints->length))
|
||||
return FALSE;
|
||||
|
||||
const BYTE* src = (const BYTE*)psrc;
|
||||
for (UINT32 i = 0; i < hints->length; i++, src += itemType->itemSize)
|
||||
{
|
||||
if (!itemType->writeFn(context, s, NULL, src))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_read_uconformant_array(NdrContext* context, wStream* s, const NdrArrayHints* hints,
|
||||
NdrMessageType itemType, void* vtarget)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(itemType);
|
||||
WINPR_ASSERT(vtarget);
|
||||
|
||||
UINT32 count = 0;
|
||||
|
||||
if (!ndr_read_uint32(context, s, &count))
|
||||
return FALSE;
|
||||
|
||||
if ((count * itemType->itemSize < hints->count))
|
||||
return FALSE;
|
||||
|
||||
BYTE* target = (BYTE*)vtarget;
|
||||
for (UINT32 i = 0; i < count; i++, target += itemType->itemSize)
|
||||
{
|
||||
if (!itemType->readFn(context, s, NULL, target))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return ndr_read_align(context, s, /*context->alignBytes*/ 4);
|
||||
}
|
||||
|
||||
BOOL ndr_write_uconformant_array(NdrContext* context, wStream* s, UINT32 len,
|
||||
NdrMessageType itemType, const BYTE* ptr)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(itemType);
|
||||
WINPR_ASSERT(ptr);
|
||||
|
||||
size_t toWrite = len * itemType->itemSize;
|
||||
size_t padding = (4 - (toWrite % 4)) % 4;
|
||||
if (!ndr_write_uint32(context, s, len) || !Stream_EnsureRemainingCapacity(s, toWrite + padding))
|
||||
return FALSE;
|
||||
|
||||
for (UINT32 i = 0; i < len; i++, ptr += itemType->itemSize)
|
||||
{
|
||||
if (!itemType->writeFn(context, s, NULL, ptr))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (padding)
|
||||
{
|
||||
Stream_Zero(s, padding);
|
||||
ndr_context_bytes_written(context, padding);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_struct_read_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
|
||||
void* target)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(descr);
|
||||
WINPR_ASSERT(target);
|
||||
|
||||
#define NDR_MAX_STRUCT_DEFERRED 16
|
||||
NdrDeferredEntry deferreds[NDR_MAX_STRUCT_DEFERRED] = { 0 };
|
||||
size_t ndeferred = 0;
|
||||
|
||||
for (size_t i = 0; i < descr->nfields; i++)
|
||||
{
|
||||
const NdrFieldStruct* field = &descr->fields[i];
|
||||
BYTE* ptr = target;
|
||||
ptr += field->structOffset;
|
||||
void* hints = NULL;
|
||||
|
||||
if (field->hintsField >= 0)
|
||||
{
|
||||
/* computes the address of the hints field if any */
|
||||
WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
|
||||
const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
|
||||
|
||||
hints = (BYTE*)target + hintsField->structOffset;
|
||||
}
|
||||
|
||||
switch (field->pointerType)
|
||||
{
|
||||
case NDR_NOT_POINTER:
|
||||
if (!field->typeDescr->readFn(context, s, hints, ptr))
|
||||
{
|
||||
WLog_ERR(TAG, "error when reading %s.%s", descr->name, field->name);
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case NDR_POINTER:
|
||||
case NDR_POINTER_NON_NULL:
|
||||
{
|
||||
NdrDeferredEntry* deferred = &deferreds[ndeferred];
|
||||
if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
|
||||
{
|
||||
WLog_ERR(TAG, "too many deferred when calling ndr_read_struct_fromDescr for %s",
|
||||
descr->name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
deferred->name = field->name;
|
||||
deferred->hints = hints;
|
||||
deferred->target = ptr;
|
||||
deferred->msg = field->typeDescr;
|
||||
if (!ndr_read_refpointer(context, s, &deferred->ptrId))
|
||||
{
|
||||
WLog_ERR(TAG, "error when reading %s.%s", descr->name, field->name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!deferred->ptrId && field->pointerType == NDR_POINTER_NON_NULL)
|
||||
{
|
||||
WLog_ERR(TAG, "%s.%s can't be null", descr->name, field->name);
|
||||
return FALSE;
|
||||
}
|
||||
ndeferred++;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
WLog_ERR(TAG, "%s.%s unknown pointer type 0x%x", descr->name, field->name,
|
||||
field->pointerType);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return ndr_push_deferreds(context, deferreds, ndeferred);
|
||||
}
|
||||
|
||||
BOOL ndr_struct_write_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
|
||||
const void* src)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(descr);
|
||||
WINPR_ASSERT(src);
|
||||
|
||||
NdrDeferredEntry deferreds[NDR_MAX_STRUCT_DEFERRED] = { 0 };
|
||||
size_t ndeferred = 0;
|
||||
|
||||
for (size_t i = 0; i < descr->nfields; i++)
|
||||
{
|
||||
const NdrFieldStruct* field = &descr->fields[i];
|
||||
const BYTE* ptr = (const BYTE*)src + field->structOffset;
|
||||
|
||||
const void* hints = NULL;
|
||||
|
||||
if (field->hintsField >= 0)
|
||||
{
|
||||
/* computes the address of the hints field if any */
|
||||
WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
|
||||
const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
|
||||
|
||||
hints = (const BYTE*)src + hintsField->structOffset;
|
||||
}
|
||||
|
||||
switch (field->pointerType)
|
||||
{
|
||||
case NDR_POINTER:
|
||||
case NDR_POINTER_NON_NULL:
|
||||
{
|
||||
ndr_refid ptrId = NDR_PTR_NULL;
|
||||
BOOL isNew = 0;
|
||||
ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr, const void**));
|
||||
|
||||
if (!ptr && field->pointerType == NDR_POINTER_NON_NULL)
|
||||
{
|
||||
WLog_ERR(TAG, "%s.%s can't be null", descr->name, field->name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ndr_context_allocatePtr(context, ptr, &ptrId, &isNew))
|
||||
return FALSE;
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
NdrDeferredEntry* deferred = &deferreds[ndeferred];
|
||||
if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"too many deferred when calling ndr_read_struct_fromDescr for %s",
|
||||
descr->name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
deferred->name = field->name;
|
||||
deferred->hints = WINPR_CAST_CONST_PTR_AWAY(hints, void*);
|
||||
deferred->target = WINPR_CAST_CONST_PTR_AWAY(ptr, void*);
|
||||
deferred->msg = field->typeDescr;
|
||||
ndeferred++;
|
||||
}
|
||||
|
||||
if (!ndr_write_uint32(context, s, ptrId))
|
||||
return FALSE;
|
||||
break;
|
||||
}
|
||||
case NDR_NOT_POINTER:
|
||||
if (!field->typeDescr->writeFn(context, s, hints, ptr))
|
||||
{
|
||||
WLog_ERR(TAG, "error when writing %s.%s", descr->name, field->name);
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ndr_push_deferreds(context, deferreds, ndeferred);
|
||||
}
|
||||
|
||||
void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl, size_t identLevel,
|
||||
const NdrStructDescr* descr, const void* obj)
|
||||
{
|
||||
char tabArray[30 + 1];
|
||||
size_t ntabs = (identLevel <= 30) ? identLevel : 30;
|
||||
|
||||
memset(tabArray, '\t', ntabs);
|
||||
tabArray[ntabs] = 0;
|
||||
|
||||
WLog_Print(logger, lvl, "%s%s", tabArray, descr->name);
|
||||
for (size_t i = 0; i < descr->nfields; i++)
|
||||
{
|
||||
const NdrFieldStruct* field = &descr->fields[i];
|
||||
const BYTE* ptr = (const BYTE*)obj + field->structOffset;
|
||||
|
||||
switch (field->pointerType)
|
||||
{
|
||||
case NDR_POINTER:
|
||||
case NDR_POINTER_NON_NULL:
|
||||
ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr, const void**));
|
||||
break;
|
||||
case NDR_NOT_POINTER:
|
||||
break;
|
||||
default:
|
||||
WLog_ERR(TAG, "invalid field->pointerType");
|
||||
break;
|
||||
}
|
||||
|
||||
WLog_Print(logger, lvl, "%s*%s:", tabArray, field->name);
|
||||
if (field->typeDescr->dumpFn)
|
||||
field->typeDescr->dumpFn(logger, lvl, identLevel + 1, ptr);
|
||||
else
|
||||
WLog_Print(logger, lvl, "%s\t<no dump function>", tabArray);
|
||||
}
|
||||
}
|
||||
|
||||
void ndr_struct_destroy(NdrContext* context, const NdrStructDescr* descr, void* pptr)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(descr);
|
||||
WINPR_ASSERT(pptr);
|
||||
|
||||
for (size_t i = 0; i < descr->nfields; i++)
|
||||
{
|
||||
const NdrFieldStruct* field = &descr->fields[i];
|
||||
void* ptr = (BYTE*)pptr + field->structOffset;
|
||||
void* hints = NULL;
|
||||
|
||||
if (field->hintsField >= 0)
|
||||
{
|
||||
/* computes the address of the hints field if any */
|
||||
WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
|
||||
const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
|
||||
|
||||
hints = (BYTE*)pptr + hintsField->structOffset;
|
||||
}
|
||||
|
||||
if (field->pointerType != NDR_NOT_POINTER)
|
||||
ptr = *(void**)ptr;
|
||||
|
||||
if (ptr && field->typeDescr->destroyFn)
|
||||
field->typeDescr->destroyFn(context, hints, ptr);
|
||||
|
||||
if (field->pointerType != NDR_NOT_POINTER)
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
ndr_refid ndr_pointer_refid(const void* ptr)
|
||||
{
|
||||
return (ndr_refid)((ULONG_PTR)ptr);
|
||||
}
|
||||
|
||||
BOOL ndr_read_refpointer(NdrContext* context, wStream* s, ndr_refid* refId)
|
||||
{
|
||||
return ndr_read_uint32(context, s, refId);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const void* needle;
|
||||
ndr_refid* presult;
|
||||
} FindValueArgs;
|
||||
|
||||
static BOOL findValueRefFn(const void* key, void* value, void* parg)
|
||||
{
|
||||
WINPR_ASSERT(parg);
|
||||
|
||||
FindValueArgs* args = (FindValueArgs*)parg;
|
||||
if (args->needle == value)
|
||||
{
|
||||
*args->presult = (ndr_refid)(UINT_PTR)key;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_context_allocatePtr(NdrContext* context, const void* ptr, ndr_refid* prefId, BOOL* pnewPtr)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
FindValueArgs findArgs = { ptr, prefId };
|
||||
if (!HashTable_Foreach(context->refPointers, findValueRefFn, &findArgs))
|
||||
{
|
||||
*pnewPtr = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
*pnewPtr = TRUE;
|
||||
*prefId = context->refIdCounter + 4;
|
||||
if (!HashTable_Insert(context->refPointers, (void*)(UINT_PTR)(*prefId), ptr))
|
||||
return FALSE;
|
||||
|
||||
context->refIdCounter += 4;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_read_pointedMessageEx(NdrContext* context, wStream* s, ndr_refid ptrId,
|
||||
NdrMessageType descr, void* hints, void** target)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(descr);
|
||||
WINPR_ASSERT(target);
|
||||
|
||||
*target = NULL;
|
||||
if (!ptrId)
|
||||
return TRUE;
|
||||
|
||||
void* ret = HashTable_GetItemValue(context->refPointers, (void*)(UINT_PTR)ptrId);
|
||||
if (!ret)
|
||||
{
|
||||
size_t itemCount = ndr_hintsCount(descr, hints);
|
||||
ret = calloc(itemCount, descr->itemSize);
|
||||
if (!ret)
|
||||
return FALSE;
|
||||
|
||||
if (!descr->readFn(context, s, hints, ret) ||
|
||||
!HashTable_Insert(context->refPointers, (void*)(UINT_PTR)ptrId, ret))
|
||||
{
|
||||
if (descr->destroyFn)
|
||||
descr->destroyFn(context, hints, ret);
|
||||
free(ret);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
*target = ret;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_push_deferreds(NdrContext* context, NdrDeferredEntry* deferreds, size_t ndeferred)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(deferreds);
|
||||
|
||||
if (!ndeferred)
|
||||
return TRUE;
|
||||
|
||||
if (context->ndeferred + ndeferred > NDR_MAX_DEFERRED)
|
||||
{
|
||||
WLog_ERR(TAG, "too many deferred");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (size_t i = ndeferred; i > 0; i--, context->ndeferred++)
|
||||
{
|
||||
context->deferred[context->ndeferred] = deferreds[i - 1];
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_treat_deferred_read(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
while (context->ndeferred)
|
||||
{
|
||||
NdrDeferredEntry current = context->deferred[context->ndeferred - 1];
|
||||
context->ndeferred--;
|
||||
|
||||
WLog_VRB(TAG, "treating read deferred 0x%x for %s", current.ptrId, current.name);
|
||||
if (!ndr_read_pointedMessageEx(context, s, current.ptrId, current.msg, current.hints,
|
||||
(void**)current.target))
|
||||
{
|
||||
WLog_ERR(TAG, "error parsing deferred %s", current.name);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ndr_treat_deferred_write(NdrContext* context, wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
while (context->ndeferred)
|
||||
{
|
||||
NdrDeferredEntry current = context->deferred[context->ndeferred - 1];
|
||||
context->ndeferred--;
|
||||
|
||||
WLog_VRB(TAG, "treating write deferred for %s", current.name);
|
||||
if (!current.msg->writeFn(context, s, current.hints, current.target))
|
||||
{
|
||||
WLog_ERR(TAG, "error writing deferred %s", current.name);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
217
channels/rdpear/common/rdpear-common/ndr.h
Normal file
217
channels/rdpear/common/rdpear-common/ndr.h
Normal file
@ -0,0 +1,217 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Authentication redirection virtual channel
|
||||
*
|
||||
* Copyright 2024 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed 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 CHANNELS_RDPEAR_NDR_H_
|
||||
#define CHANNELS_RDPEAR_NDR_H_
|
||||
|
||||
#include <winpr/stream.h>
|
||||
#include <freerdp/api.h>
|
||||
|
||||
#define NDR_PTR_NULL (0UL)
|
||||
|
||||
#define NDR_SIMPLE_TYPE_DECL(LOWER, UPPER) \
|
||||
BOOL ndr_read_##LOWER(NdrContext* context, wStream* s, UPPER* v); \
|
||||
BOOL ndr_read_##LOWER##_(NdrContext* context, wStream* s, const void* hints, void* v); \
|
||||
BOOL ndr_write_##LOWER(NdrContext* context, wStream* s, UPPER v); \
|
||||
BOOL ndr_write_##LOWER##_(NdrContext* context, wStream* s, const void* hints, const void* v); \
|
||||
extern const NdrMessageDescr ndr_##LOWER##_descr_s; \
|
||||
NdrMessageType ndr_##LOWER##_descr(void)
|
||||
|
||||
#define NDR_ARRAY_OF_TYPE_DECL(TYPE, UPPERTYPE) \
|
||||
BOOL ndr_read_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, void* v); \
|
||||
BOOL ndr_write_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, \
|
||||
const void* v); \
|
||||
void ndr_destroy_##TYPE##Array(NdrContext* context, const void* hints, void* obj); \
|
||||
extern const NdrMessageDescr ndr_##TYPE##Array_descr_s; \
|
||||
NdrMessageType ndr_##TYPE##Array_descr(void); \
|
||||
\
|
||||
BOOL ndr_read_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
|
||||
void* v); \
|
||||
BOOL ndr_write_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
|
||||
const void* v); \
|
||||
extern const NdrMessageDescr ndr_##TYPE##VaryingArray_descr_s; \
|
||||
NdrMessageType ndr_##TYPE##VaryingArray_descr(void)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct NdrContext_s NdrContext;
|
||||
|
||||
typedef UINT32 ndr_refid;
|
||||
|
||||
typedef BOOL (*NDR_READER_FN)(NdrContext* context, wStream* s, const void* hints, void* target);
|
||||
typedef BOOL (*NDR_WRITER_FN)(NdrContext* context, wStream* s, const void* hints,
|
||||
const void* obj);
|
||||
typedef void (*NDR_DESTROY_FN)(NdrContext* context, const void* hints, void* obj);
|
||||
typedef void (*NDR_DUMP_FN)(wLog* logger, UINT32 lvl, size_t indentLevel, const void* obj);
|
||||
|
||||
/** @brief arity of a message */
|
||||
typedef enum
|
||||
{
|
||||
NDR_ARITY_SIMPLE,
|
||||
NDR_ARITY_ARRAYOF,
|
||||
NDR_ARITY_VARYING_ARRAYOF,
|
||||
} NdrTypeArity;
|
||||
|
||||
/** @brief message descriptor */
|
||||
typedef struct
|
||||
{
|
||||
NdrTypeArity arity;
|
||||
size_t itemSize;
|
||||
NDR_READER_FN readFn;
|
||||
NDR_WRITER_FN writeFn;
|
||||
NDR_DESTROY_FN destroyFn;
|
||||
NDR_DUMP_FN dumpFn;
|
||||
} NdrMessageDescr;
|
||||
|
||||
typedef const NdrMessageDescr* NdrMessageType;
|
||||
|
||||
/** @brief pointer or not and if null is accepted */
|
||||
typedef enum
|
||||
{
|
||||
NDR_NOT_POINTER,
|
||||
NDR_POINTER_NON_NULL,
|
||||
NDR_POINTER
|
||||
} NdrPointerType;
|
||||
|
||||
/** @brief descriptor of a field in a structure */
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
size_t structOffset;
|
||||
NdrPointerType pointerType;
|
||||
ssize_t hintsField;
|
||||
NdrMessageType typeDescr;
|
||||
} NdrFieldStruct;
|
||||
|
||||
/** @brief structure descriptor */
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
size_t nfields;
|
||||
const NdrFieldStruct* fields;
|
||||
} NdrStructDescr;
|
||||
|
||||
/** @brief a deferred pointer */
|
||||
typedef struct
|
||||
{
|
||||
ndr_refid ptrId;
|
||||
const char* name;
|
||||
void* hints;
|
||||
void* target;
|
||||
NdrMessageType msg;
|
||||
} NdrDeferredEntry;
|
||||
|
||||
void ndr_context_free(NdrContext* context);
|
||||
|
||||
static INLINE void ndr_context_destroy(NdrContext** pcontext)
|
||||
{
|
||||
WINPR_ASSERT(pcontext);
|
||||
ndr_context_free(*pcontext);
|
||||
*pcontext = NULL;
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(ndr_context_free, 1)
|
||||
NdrContext* ndr_context_new(BOOL bigEndianDrep, BYTE version);
|
||||
|
||||
void ndr_context_reset(NdrContext* context);
|
||||
|
||||
WINPR_ATTR_MALLOC(ndr_context_free, 1)
|
||||
NdrContext* ndr_context_copy(const NdrContext* src);
|
||||
|
||||
WINPR_ATTR_MALLOC(ndr_context_free, 1)
|
||||
NdrContext* ndr_read_header(wStream* s);
|
||||
|
||||
BOOL ndr_write_header(NdrContext* context, wStream* s);
|
||||
|
||||
NDR_SIMPLE_TYPE_DECL(uint8, UINT8);
|
||||
NDR_SIMPLE_TYPE_DECL(uint16, UINT16);
|
||||
NDR_SIMPLE_TYPE_DECL(uint32, UINT32);
|
||||
NDR_SIMPLE_TYPE_DECL(uint64, UINT64);
|
||||
|
||||
NDR_ARRAY_OF_TYPE_DECL(uint8, BYTE);
|
||||
NDR_ARRAY_OF_TYPE_DECL(uint16, UINT16);
|
||||
|
||||
BOOL ndr_skip_bytes(NdrContext* context, wStream* s, size_t nbytes);
|
||||
BOOL ndr_read_align(NdrContext* context, wStream* s, size_t sz);
|
||||
BOOL ndr_write_align(NdrContext* context, wStream* s, size_t sz);
|
||||
|
||||
BOOL ndr_read_pickle(NdrContext* context, wStream* s);
|
||||
BOOL ndr_write_pickle(NdrContext* context, wStream* s);
|
||||
|
||||
BOOL ndr_read_constructed(NdrContext* context, wStream* s, wStream* target);
|
||||
BOOL ndr_write_constructed(NdrContext* context, wStream* s, wStream* payload);
|
||||
|
||||
BOOL ndr_start_constructed(NdrContext* context, wStream* s);
|
||||
BOOL ndr_end_constructed(NdrContext* context, wStream* s);
|
||||
|
||||
BOOL ndr_read_wchar(NdrContext* context, wStream* s, WCHAR* ptr);
|
||||
|
||||
/** @brief hints for a varying conformant array */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 length;
|
||||
UINT32 maxLength;
|
||||
} NdrVaryingArrayHints;
|
||||
|
||||
BOOL ndr_read_uconformant_varying_array(NdrContext* context, wStream* s,
|
||||
const NdrVaryingArrayHints* hints,
|
||||
NdrMessageType itemType, void* ptarget);
|
||||
BOOL ndr_write_uconformant_varying_array(NdrContext* context, wStream* s,
|
||||
const NdrVaryingArrayHints* hints,
|
||||
NdrMessageType itemType, const void* src);
|
||||
|
||||
/** @brief hints for a conformant array */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 count;
|
||||
} NdrArrayHints;
|
||||
|
||||
BOOL ndr_read_uconformant_array(NdrContext* context, wStream* s, const NdrArrayHints* hints,
|
||||
NdrMessageType itemType, void* vtarget);
|
||||
BOOL ndr_write_uconformant_array(NdrContext* context, wStream* s, UINT32 len,
|
||||
NdrMessageType itemType, const BYTE* ptr);
|
||||
|
||||
BOOL ndr_struct_read_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
|
||||
void* target);
|
||||
BOOL ndr_struct_write_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
|
||||
const void* src);
|
||||
void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl, size_t identLevel,
|
||||
const NdrStructDescr* descr, const void* obj);
|
||||
void ndr_struct_destroy(NdrContext* context, const NdrStructDescr* descr, void* pptr);
|
||||
|
||||
ndr_refid ndr_pointer_refid(const void* ptr);
|
||||
BOOL ndr_read_refpointer(NdrContext* context, wStream* s, UINT32* refId);
|
||||
BOOL ndr_context_allocatePtr(NdrContext* context, const void* ptr, ndr_refid* prefId,
|
||||
BOOL* pnewPtr);
|
||||
|
||||
BOOL ndr_read_pointedMessageEx(NdrContext* context, wStream* s, ndr_refid ptrId,
|
||||
NdrMessageType descr, void* hints, void** target);
|
||||
|
||||
BOOL ndr_push_deferreds(NdrContext* context, NdrDeferredEntry* deferreds, size_t ndeferred);
|
||||
BOOL ndr_treat_deferred_read(NdrContext* context, wStream* s);
|
||||
BOOL ndr_treat_deferred_write(NdrContext* context, wStream* s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CHANNELS_RDPEAR_NDR_H_ */
|
31
channels/rdpear/common/rdpear-common/rdpear_asn1.h
Normal file
31
channels/rdpear/common/rdpear-common/rdpear_asn1.h
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* ASN1 routines for RDPEAR
|
||||
*
|
||||
* Copyright 2024 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed 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 RPDEAR_RDPEAR_ASN1_H__
|
||||
#define RPDEAR_RDPEAR_ASN1_H__
|
||||
|
||||
#include <krb5.h>
|
||||
|
||||
#include <winpr/stream.h>
|
||||
|
||||
wStream* rdpear_enc_Checksum(UINT32 cksumtype, krb5_checksum* csum);
|
||||
|
||||
wStream* rdpear_enc_EncryptedData(UINT32 encType, krb5_data* payload);
|
||||
|
||||
#endif /* RPDEAR_RDPEAR_ASN1_H__ */
|
236
channels/rdpear/common/rdpear-common/rdpear_common.h
Normal file
236
channels/rdpear/common/rdpear-common/rdpear_common.h
Normal file
@ -0,0 +1,236 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
*
|
||||
* Copyright 2023 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed 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 FREERDP_CHANNEL_RDPEAR_COMMON_H
|
||||
#define FREERDP_CHANNEL_RDPEAR_COMMON_H
|
||||
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/asn1.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/sspi.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
|
||||
#include <rdpear-common/ndr.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RDPEAR_PACKAGE_KERBEROS,
|
||||
RDPEAR_PACKAGE_NTLM,
|
||||
RDPEAR_PACKAGE_UNKNOWN
|
||||
} RdpEarPackageType;
|
||||
|
||||
/* RDPEAR 2.2.1.1 */
|
||||
typedef enum
|
||||
{
|
||||
// Start Kerberos remote calls
|
||||
RemoteCallKerbMinimum = 0x100,
|
||||
RemoteCallKerbNegotiateVersion = 0x100,
|
||||
RemoteCallKerbBuildAsReqAuthenticator,
|
||||
RemoteCallKerbVerifyServiceTicket,
|
||||
RemoteCallKerbCreateApReqAuthenticator,
|
||||
RemoteCallKerbDecryptApReply,
|
||||
RemoteCallKerbUnpackKdcReplyBody,
|
||||
RemoteCallKerbComputeTgsChecksum,
|
||||
RemoteCallKerbBuildEncryptedAuthData,
|
||||
RemoteCallKerbPackApReply,
|
||||
RemoteCallKerbHashS4UPreauth,
|
||||
RemoteCallKerbSignS4UPreauthData,
|
||||
RemoteCallKerbVerifyChecksum,
|
||||
RemoteCallKerbReserved1,
|
||||
RemoteCallKerbReserved2,
|
||||
RemoteCallKerbReserved3,
|
||||
RemoteCallKerbReserved4,
|
||||
RemoteCallKerbReserved5,
|
||||
RemoteCallKerbReserved6,
|
||||
RemoteCallKerbReserved7,
|
||||
RemoteCallKerbDecryptPacCredentials,
|
||||
RemoteCallKerbCreateECDHKeyAgreement,
|
||||
RemoteCallKerbCreateDHKeyAgreement,
|
||||
RemoteCallKerbDestroyKeyAgreement,
|
||||
RemoteCallKerbKeyAgreementGenerateNonce,
|
||||
RemoteCallKerbFinalizeKeyAgreement,
|
||||
RemoteCallKerbMaximum = 0x1ff,
|
||||
// End Kerberos remote calls
|
||||
|
||||
// Start NTLM remote calls
|
||||
RemoteCallNtlmMinimum = 0x200,
|
||||
RemoteCallNtlmNegotiateVersion = 0x200,
|
||||
RemoteCallNtlmLm20GetNtlm3ChallengeResponse,
|
||||
RemoteCallNtlmCalculateNtResponse,
|
||||
RemoteCallNtlmCalculateUserSessionKeyNt,
|
||||
RemoteCallNtlmCompareCredentials,
|
||||
RemoteCallNtlmMaximum = 0x2ff,
|
||||
// End NTLM remote calls
|
||||
} RemoteGuardCallId;
|
||||
|
||||
FREERDP_LOCAL RdpEarPackageType rdpear_packageType_from_name(WinPrAsn1_OctetString* package);
|
||||
FREERDP_LOCAL wStream* rdpear_encodePayload(RdpEarPackageType packageType, wStream* payload);
|
||||
|
||||
#define RDPEAR_COMMON_MESSAGE_DECL(V) \
|
||||
FREERDP_LOCAL BOOL ndr_read_##V(NdrContext* context, wStream* s, const void* hints, V* obj); \
|
||||
FREERDP_LOCAL BOOL ndr_write_##V(NdrContext* context, wStream* s, const void* hints, \
|
||||
const V* obj); \
|
||||
FREERDP_LOCAL void ndr_destroy_##V(NdrContext* context, const void* hints, V* obj); \
|
||||
FREERDP_LOCAL void ndr_dump_##V(wLog* logger, UINT32 lvl, size_t indentLevel, const V* obj); \
|
||||
FREERDP_LOCAL NdrMessageType ndr_##V##_descr(void)
|
||||
|
||||
/** @brief 2.2.1.2.2 KERB_RPC_OCTET_STRING */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 length;
|
||||
BYTE* value;
|
||||
} KERB_RPC_OCTET_STRING;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(KERB_RPC_OCTET_STRING);
|
||||
|
||||
/** @brief 2.2.1.2.1 KERB_ASN1_DATA */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 Pdu;
|
||||
NdrArrayHints Asn1BufferHints;
|
||||
BYTE* Asn1Buffer;
|
||||
} KERB_ASN1_DATA;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(KERB_ASN1_DATA);
|
||||
|
||||
/** @brief 2.3.10 RPC_UNICODE_STRING (MS-DTYP) */
|
||||
typedef struct
|
||||
{
|
||||
NdrVaryingArrayHints lenHints;
|
||||
UINT32 strLength;
|
||||
WCHAR* Buffer;
|
||||
} RPC_UNICODE_STRING;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(RPC_UNICODE_STRING);
|
||||
|
||||
/** @brief 2.2.1.2.3 KERB_RPC_INTERNAL_NAME */
|
||||
typedef struct
|
||||
{
|
||||
UINT16 NameType;
|
||||
NdrArrayHints nameHints;
|
||||
RPC_UNICODE_STRING* Names;
|
||||
} KERB_RPC_INTERNAL_NAME;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(KERB_RPC_INTERNAL_NAME);
|
||||
|
||||
/** @brief 2.2.1.2.8 KERB_RPC_ENCRYPTION_KEY */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 reserved1;
|
||||
UINT32 reserved2;
|
||||
KERB_RPC_OCTET_STRING reserved3;
|
||||
} KERB_RPC_ENCRYPTION_KEY;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(KERB_RPC_ENCRYPTION_KEY);
|
||||
|
||||
/** @brief 2.2.2.1.8 BuildEncryptedAuthData */
|
||||
typedef struct
|
||||
{
|
||||
UINT32 KeyUsage;
|
||||
KERB_RPC_ENCRYPTION_KEY* Key;
|
||||
KERB_ASN1_DATA* PlainAuthData;
|
||||
} BuildEncryptedAuthDataReq;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(BuildEncryptedAuthDataReq);
|
||||
|
||||
/** @brief 2.2.2.1.7 ComputeTgsChecksum */
|
||||
typedef struct
|
||||
{
|
||||
KERB_ASN1_DATA* requestBody;
|
||||
KERB_RPC_ENCRYPTION_KEY* Key;
|
||||
UINT32 ChecksumType;
|
||||
} ComputeTgsChecksumReq;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(ComputeTgsChecksumReq);
|
||||
|
||||
/** @brief 2.2.2.1.4 CreateApReqAuthenticator */
|
||||
typedef struct
|
||||
{
|
||||
KERB_RPC_ENCRYPTION_KEY* EncryptionKey;
|
||||
ULONG SequenceNumber;
|
||||
KERB_RPC_INTERNAL_NAME* ClientName;
|
||||
RPC_UNICODE_STRING* ClientRealm;
|
||||
PLARGE_INTEGER SkewTime;
|
||||
KERB_RPC_ENCRYPTION_KEY* SubKey; // optional
|
||||
KERB_ASN1_DATA* AuthData; // optional
|
||||
KERB_ASN1_DATA* GssChecksum; // optional
|
||||
ULONG KeyUsage;
|
||||
} CreateApReqAuthenticatorReq;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(CreateApReqAuthenticatorReq);
|
||||
|
||||
/** @brief 2.2.2.1.4 CreateApReqAuthenticator */
|
||||
typedef struct
|
||||
{
|
||||
LARGE_INTEGER AuthenticatorTime;
|
||||
KERB_ASN1_DATA Authenticator;
|
||||
LONG KerbProtocolError;
|
||||
} CreateApReqAuthenticatorResp;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(CreateApReqAuthenticatorResp);
|
||||
|
||||
/** @brief 2.2.2.1.6 UnpackKdcReplyBody */
|
||||
typedef struct
|
||||
{
|
||||
KERB_ASN1_DATA* EncryptedData;
|
||||
KERB_RPC_ENCRYPTION_KEY* Key;
|
||||
KERB_RPC_ENCRYPTION_KEY* StrengthenKey;
|
||||
ULONG Pdu;
|
||||
ULONG KeyUsage;
|
||||
} UnpackKdcReplyBodyReq;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(UnpackKdcReplyBodyReq);
|
||||
|
||||
/** @brief 2.2.2.1.6 UnpackKdcReplyBody */
|
||||
typedef struct
|
||||
{
|
||||
LONG KerbProtocolError;
|
||||
KERB_ASN1_DATA ReplyBody;
|
||||
} UnpackKdcReplyBodyResp;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(UnpackKdcReplyBodyResp);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
KERB_ASN1_DATA* EncryptedReply;
|
||||
KERB_RPC_ENCRYPTION_KEY* Key;
|
||||
} DecryptApReplyReq;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(DecryptApReplyReq);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
KERB_ASN1_DATA* Reply;
|
||||
KERB_ASN1_DATA* ReplyBody;
|
||||
KERB_RPC_ENCRYPTION_KEY* SessionKey;
|
||||
} PackApReplyReq;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(PackApReplyReq);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NdrArrayHints PackedReplyHints;
|
||||
BYTE* PackedReply;
|
||||
} PackApReplyResp;
|
||||
|
||||
RDPEAR_COMMON_MESSAGE_DECL(PackApReplyResp);
|
||||
|
||||
#undef RDPEAR_COMMON_MESSAGE_DECL
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPEAR_COMMON_H */
|
104
channels/rdpear/common/rdpear_asn1.c
Normal file
104
channels/rdpear/common/rdpear_asn1.c
Normal file
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* ASN1 routines for RDPEAR
|
||||
*
|
||||
* Copyright 2024 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed 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 <rdpear-common/rdpear_asn1.h>
|
||||
#include <winpr/asn1.h>
|
||||
|
||||
wStream* rdpear_enc_Checksum(UINT32 cksumtype, krb5_checksum* csum)
|
||||
{
|
||||
/**
|
||||
* Checksum ::= SEQUENCE {
|
||||
* cksumtype [0] Int32,
|
||||
* checksum [1] OCTET STRING
|
||||
* }
|
||||
*/
|
||||
wStream* ret = NULL;
|
||||
WinPrAsn1Encoder* enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
|
||||
if (!enc)
|
||||
return NULL;
|
||||
|
||||
if (!WinPrAsn1EncSeqContainer(enc))
|
||||
goto out;
|
||||
|
||||
if (!WinPrAsn1EncContextualInteger(enc, 0, (WinPrAsn1_INTEGER)cksumtype))
|
||||
goto out;
|
||||
|
||||
WinPrAsn1_OctetString octets;
|
||||
octets.data = (BYTE*)csum->contents;
|
||||
octets.len = csum->length;
|
||||
if (!WinPrAsn1EncContextualOctetString(enc, 1, &octets) || !WinPrAsn1EncEndContainer(enc))
|
||||
goto out;
|
||||
|
||||
ret = Stream_New(NULL, 1024);
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
if (!WinPrAsn1EncToStream(enc, ret))
|
||||
{
|
||||
Stream_Free(ret, TRUE);
|
||||
ret = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
WinPrAsn1Encoder_Free(&enc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
wStream* rdpear_enc_EncryptedData(UINT32 encType, krb5_data* payload)
|
||||
{
|
||||
/**
|
||||
* EncryptedData ::= SEQUENCE {
|
||||
* etype [0] Int32 -- EncryptionType --,
|
||||
* kvno [1] UInt32 OPTIONAL,
|
||||
* cipher [2] OCTET STRING -- ciphertext
|
||||
* }
|
||||
*/
|
||||
wStream* ret = NULL;
|
||||
WinPrAsn1Encoder* enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
|
||||
if (!enc)
|
||||
return NULL;
|
||||
|
||||
if (!WinPrAsn1EncSeqContainer(enc))
|
||||
goto out;
|
||||
|
||||
if (!WinPrAsn1EncContextualInteger(enc, 0, (WinPrAsn1_INTEGER)encType))
|
||||
goto out;
|
||||
|
||||
WinPrAsn1_OctetString octets;
|
||||
octets.data = (BYTE*)payload->data;
|
||||
octets.len = payload->length;
|
||||
if (!WinPrAsn1EncContextualOctetString(enc, 2, &octets) || !WinPrAsn1EncEndContainer(enc))
|
||||
goto out;
|
||||
|
||||
ret = Stream_New(NULL, 1024);
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
if (!WinPrAsn1EncToStream(enc, ret))
|
||||
{
|
||||
Stream_Free(ret, TRUE);
|
||||
ret = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
WinPrAsn1Encoder_Free(&enc);
|
||||
return ret;
|
||||
}
|
565
channels/rdpear/common/rdpear_common.c
Normal file
565
channels/rdpear/common/rdpear_common.c
Normal file
@ -0,0 +1,565 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
*
|
||||
* Copyright 2023 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed 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 <rdpear-common/rdpear_common.h>
|
||||
#include <rdpear-common/rdpear_asn1.h>
|
||||
#include <rdpear-common/ndr.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <winpr/print.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("rdpear")
|
||||
|
||||
static char kerberosPackageName[] = {
|
||||
'K', 0, 'e', 0, 'r', 0, 'b', 0, 'e', 0, 'r', 0, 'o', 0, 's', 0
|
||||
};
|
||||
static char ntlmPackageName[] = { 'N', 0, 'T', 0, 'L', 0, 'M', 0 };
|
||||
|
||||
RdpEarPackageType rdpear_packageType_from_name(WinPrAsn1_OctetString* package)
|
||||
{
|
||||
if (package->len == sizeof(kerberosPackageName) &&
|
||||
memcmp(package->data, kerberosPackageName, package->len) == 0)
|
||||
return RDPEAR_PACKAGE_KERBEROS;
|
||||
|
||||
if (package->len == sizeof(ntlmPackageName) &&
|
||||
memcmp(package->data, ntlmPackageName, package->len) == 0)
|
||||
return RDPEAR_PACKAGE_NTLM;
|
||||
|
||||
return RDPEAR_PACKAGE_UNKNOWN;
|
||||
}
|
||||
|
||||
wStream* rdpear_encodePayload(RdpEarPackageType packageType, wStream* payload)
|
||||
{
|
||||
wStream* ret = NULL;
|
||||
WinPrAsn1Encoder* enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
|
||||
if (!enc)
|
||||
return NULL;
|
||||
|
||||
/* TSRemoteGuardInnerPacket ::= SEQUENCE { */
|
||||
if (!WinPrAsn1EncSeqContainer(enc))
|
||||
goto out;
|
||||
|
||||
/* packageName [1] OCTET STRING */
|
||||
WinPrAsn1_OctetString packageOctetString;
|
||||
switch (packageType)
|
||||
{
|
||||
case RDPEAR_PACKAGE_KERBEROS:
|
||||
packageOctetString.data = (BYTE*)kerberosPackageName;
|
||||
packageOctetString.len = sizeof(kerberosPackageName);
|
||||
break;
|
||||
case RDPEAR_PACKAGE_NTLM:
|
||||
packageOctetString.data = (BYTE*)ntlmPackageName;
|
||||
packageOctetString.len = sizeof(ntlmPackageName);
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!WinPrAsn1EncContextualOctetString(enc, 1, &packageOctetString))
|
||||
goto out;
|
||||
|
||||
/* buffer [2] OCTET STRING*/
|
||||
WinPrAsn1_OctetString payloadOctetString = { Stream_GetPosition(payload),
|
||||
Stream_Buffer(payload) };
|
||||
if (!WinPrAsn1EncContextualOctetString(enc, 2, &payloadOctetString))
|
||||
goto out;
|
||||
|
||||
/* } */
|
||||
if (!WinPrAsn1EncEndContainer(enc))
|
||||
goto out;
|
||||
|
||||
ret = Stream_New(NULL, 100);
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
if (!WinPrAsn1EncToStream(enc, ret))
|
||||
{
|
||||
Stream_Free(ret, TRUE);
|
||||
ret = NULL;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
WinPrAsn1Encoder_Free(&enc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define RDPEAR_SIMPLE_MESSAGE_TYPE(V) \
|
||||
BOOL ndr_read_##V(NdrContext* context, wStream* s, const void* hints, V* obj) \
|
||||
{ \
|
||||
WINPR_UNUSED(hints); \
|
||||
return ndr_struct_read_fromDescr(context, s, &V##_struct, obj); \
|
||||
} \
|
||||
BOOL ndr_write_##V(NdrContext* context, wStream* s, const void* hints, const V* obj) \
|
||||
{ \
|
||||
WINPR_UNUSED(hints); \
|
||||
return ndr_struct_write_fromDescr(context, s, &V##_struct, obj); \
|
||||
} \
|
||||
void ndr_destroy_##V(NdrContext* context, const void* hints, V* obj) \
|
||||
{ \
|
||||
WINPR_UNUSED(hints); \
|
||||
ndr_struct_destroy(context, &V##_struct, obj); \
|
||||
} \
|
||||
void ndr_dump_##V(wLog* logger, UINT32 lvl, size_t indentLevel, const V* obj) \
|
||||
{ \
|
||||
ndr_struct_dump_fromDescr(logger, lvl, indentLevel, &V##_struct, obj); \
|
||||
} \
|
||||
\
|
||||
static BOOL ndr_descr_read_##V(NdrContext* context, wStream* s, const void* hints, void* obj) \
|
||||
{ \
|
||||
WINPR_UNUSED(hints); \
|
||||
return ndr_struct_read_fromDescr(context, s, &V##_struct, obj); \
|
||||
} \
|
||||
static BOOL ndr_descr_write_##V(NdrContext* context, wStream* s, const void* hints, \
|
||||
const void* obj) \
|
||||
{ \
|
||||
WINPR_UNUSED(hints); \
|
||||
return ndr_struct_write_fromDescr(context, s, &V##_struct, obj); \
|
||||
} \
|
||||
static void ndr_descr_destroy_##V(NdrContext* context, const void* hints, void* obj) \
|
||||
{ \
|
||||
WINPR_UNUSED(hints); \
|
||||
ndr_struct_destroy(context, &V##_struct, obj); \
|
||||
} \
|
||||
static void ndr_descr_dump_##V(wLog* logger, UINT32 lvl, size_t indentLevel, const void* obj) \
|
||||
{ \
|
||||
ndr_struct_dump_fromDescr(logger, lvl, indentLevel, &V##_struct, obj); \
|
||||
} \
|
||||
\
|
||||
static NdrMessageDescr ndr_##V##_descr_s = { \
|
||||
NDR_ARITY_SIMPLE, sizeof(V), ndr_descr_read_##V, ndr_descr_write_##V, \
|
||||
ndr_descr_destroy_##V, ndr_descr_dump_##V, \
|
||||
}; \
|
||||
\
|
||||
NdrMessageType ndr_##V##_descr(void) \
|
||||
{ \
|
||||
return &ndr_##V##_descr_s; \
|
||||
}
|
||||
|
||||
static const NdrFieldStruct KERB_RPC_OCTET_STRING_fields[] = {
|
||||
{ "Length", offsetof(KERB_RPC_OCTET_STRING, length), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s },
|
||||
{ "value", offsetof(KERB_RPC_OCTET_STRING, value), NDR_POINTER_NON_NULL, 0,
|
||||
&ndr_uint8Array_descr_s }
|
||||
};
|
||||
static const NdrStructDescr KERB_RPC_OCTET_STRING_struct = { "KERB_RPC_OCTET_STRING", 2,
|
||||
KERB_RPC_OCTET_STRING_fields };
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(KERB_RPC_OCTET_STRING)
|
||||
|
||||
/* ============================= KERB_ASN1_DATA ============================== */
|
||||
|
||||
static const NdrFieldStruct KERB_ASN1_DATA_fields[] = {
|
||||
{ "Pdu", offsetof(KERB_ASN1_DATA, Pdu), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s },
|
||||
{ "Count", offsetof(KERB_ASN1_DATA, Asn1BufferHints.count), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "Asn1Buffer", offsetof(KERB_ASN1_DATA, Asn1Buffer), NDR_POINTER_NON_NULL, 1,
|
||||
&ndr_uint8Array_descr_s }
|
||||
};
|
||||
static const NdrStructDescr KERB_ASN1_DATA_struct = { "KERB_ASN1_DATA",
|
||||
ARRAYSIZE(KERB_ASN1_DATA_fields),
|
||||
KERB_ASN1_DATA_fields };
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(KERB_ASN1_DATA)
|
||||
|
||||
/* ============================ RPC_UNICODE_STRING ========================== */
|
||||
|
||||
BOOL ndr_read_RPC_UNICODE_STRING(NdrContext* context, wStream* s, const void* hints,
|
||||
RPC_UNICODE_STRING* res)
|
||||
{
|
||||
NdrDeferredEntry bufferDesc = { NDR_PTR_NULL, "RPC_UNICODE_STRING.Buffer", &res->lenHints,
|
||||
&res->Buffer, ndr_uint16VaryingArray_descr() };
|
||||
UINT16 Length = 0;
|
||||
UINT16 MaximumLength = 0;
|
||||
|
||||
WINPR_UNUSED(hints);
|
||||
if (!ndr_read_uint16(context, s, &Length) || !ndr_read_uint16(context, s, &MaximumLength) ||
|
||||
!ndr_read_refpointer(context, s, &bufferDesc.ptrId) || Length > MaximumLength)
|
||||
return FALSE;
|
||||
|
||||
res->lenHints.length = Length;
|
||||
res->lenHints.maxLength = MaximumLength;
|
||||
res->strLength = Length / 2;
|
||||
|
||||
return ndr_push_deferreds(context, &bufferDesc, 1);
|
||||
}
|
||||
|
||||
static BOOL ndr_descr_read_RPC_UNICODE_STRING(NdrContext* context, wStream* s, const void* hints,
|
||||
void* res)
|
||||
{
|
||||
return ndr_read_RPC_UNICODE_STRING(context, s, hints, res);
|
||||
}
|
||||
|
||||
#if 0
|
||||
BOOL ndr_write_RPC_UNICODE_STRING(NdrContext* context, wStream* s, const void* hints,
|
||||
const RPC_UNICODE_STRING* res)
|
||||
{
|
||||
return ndr_write_uint32(context, s, res->lenHints.length) &&
|
||||
ndr_write_uint32(context, s, res->lenHints.maxLength) /*&&
|
||||
ndr_write_BYTE_ptr(context, s, (BYTE*)res->Buffer, res->Length)*/
|
||||
;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ndr_dump_RPC_UNICODE_STRING(wLog* logger, UINT32 lvl, size_t indentLevel,
|
||||
const RPC_UNICODE_STRING* obj)
|
||||
{
|
||||
WINPR_UNUSED(indentLevel);
|
||||
WLog_Print(logger, lvl, "\tLength=%d MaximumLength=%d", obj->lenHints.length,
|
||||
obj->lenHints.maxLength);
|
||||
winpr_HexLogDump(logger, lvl, obj->Buffer, obj->lenHints.length);
|
||||
}
|
||||
|
||||
static void ndr_descr_dump_RPC_UNICODE_STRING(wLog* logger, UINT32 lvl, size_t indentLevel,
|
||||
const void* obj)
|
||||
{
|
||||
ndr_dump_RPC_UNICODE_STRING(logger, lvl, indentLevel, obj);
|
||||
}
|
||||
|
||||
void ndr_destroy_RPC_UNICODE_STRING(NdrContext* context, const void* hints, RPC_UNICODE_STRING* obj)
|
||||
{
|
||||
WINPR_UNUSED(context);
|
||||
WINPR_UNUSED(hints);
|
||||
if (!obj)
|
||||
return;
|
||||
free(obj->Buffer);
|
||||
obj->Buffer = NULL;
|
||||
}
|
||||
|
||||
static void ndr_descr_destroy_RPC_UNICODE_STRING(NdrContext* context, const void* hints, void* obj)
|
||||
{
|
||||
ndr_destroy_RPC_UNICODE_STRING(context, hints, obj);
|
||||
}
|
||||
|
||||
static const NdrMessageDescr RPC_UNICODE_STRING_descr_s = { NDR_ARITY_SIMPLE,
|
||||
sizeof(RPC_UNICODE_STRING),
|
||||
ndr_descr_read_RPC_UNICODE_STRING,
|
||||
/*ndr_write_RPC_UNICODE_STRING*/ NULL,
|
||||
ndr_descr_destroy_RPC_UNICODE_STRING,
|
||||
ndr_descr_dump_RPC_UNICODE_STRING };
|
||||
|
||||
NdrMessageType ndr_RPC_UNICODE_STRING_descr(void)
|
||||
{
|
||||
return &RPC_UNICODE_STRING_descr_s;
|
||||
}
|
||||
|
||||
/* ========================= RPC_UNICODE_STRING_Array ======================== */
|
||||
|
||||
static BOOL ndr_read_RPC_UNICODE_STRING_Array(NdrContext* context, wStream* s, const void* hints,
|
||||
void* v)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(hints);
|
||||
return ndr_read_uconformant_array(context, s, hints, ndr_RPC_UNICODE_STRING_descr(), v);
|
||||
}
|
||||
|
||||
static BOOL ndr_write_RPC_UNICODE_STRING_Array(NdrContext* context, wStream* s, const void* ghints,
|
||||
const void* v)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(ghints);
|
||||
|
||||
const NdrArrayHints* hints = (const NdrArrayHints*)ghints;
|
||||
|
||||
return ndr_write_uconformant_array(context, s, hints->count, ndr_RPC_UNICODE_STRING_descr(), v);
|
||||
}
|
||||
|
||||
static const NdrMessageDescr RPC_UNICODE_STRING_Array_descr_s = {
|
||||
NDR_ARITY_ARRAYOF,
|
||||
sizeof(RPC_UNICODE_STRING),
|
||||
ndr_read_RPC_UNICODE_STRING_Array,
|
||||
ndr_write_RPC_UNICODE_STRING_Array,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
static NdrMessageType ndr_RPC_UNICODE_STRING_Array_descr(void)
|
||||
{
|
||||
return &RPC_UNICODE_STRING_Array_descr_s;
|
||||
}
|
||||
|
||||
/* ========================== KERB_RPC_INTERNAL_NAME ======================== */
|
||||
|
||||
BOOL ndr_read_KERB_RPC_INTERNAL_NAME(NdrContext* context, wStream* s, const void* hints,
|
||||
KERB_RPC_INTERNAL_NAME* res)
|
||||
{
|
||||
WINPR_ASSERT(res);
|
||||
|
||||
union
|
||||
{
|
||||
RPC_UNICODE_STRING** ppstr;
|
||||
void* pv;
|
||||
} cnv;
|
||||
cnv.ppstr = &res->Names;
|
||||
NdrDeferredEntry names = { NDR_PTR_NULL, "KERB_RPC_INTERNAL_NAME.Names", &res->nameHints,
|
||||
cnv.pv, ndr_RPC_UNICODE_STRING_Array_descr() };
|
||||
|
||||
UINT16 nameCount = 0;
|
||||
WINPR_UNUSED(hints);
|
||||
|
||||
if (!ndr_read_uint16(context, s, &res->NameType) || !ndr_read_uint16(context, s, &nameCount))
|
||||
return FALSE;
|
||||
|
||||
res->nameHints.count = nameCount;
|
||||
|
||||
return ndr_read_refpointer(context, s, &names.ptrId) && ndr_push_deferreds(context, &names, 1);
|
||||
}
|
||||
|
||||
static BOOL ndr_descr_read_KERB_RPC_INTERNAL_NAME(NdrContext* context, wStream* s,
|
||||
const void* hints, void* res)
|
||||
{
|
||||
return ndr_read_KERB_RPC_INTERNAL_NAME(context, s, hints, res);
|
||||
}
|
||||
|
||||
BOOL ndr_write_KERB_RPC_INTERNAL_NAME(NdrContext* context, wStream* s, const void* hints,
|
||||
const KERB_RPC_INTERNAL_NAME* res)
|
||||
{
|
||||
WINPR_UNUSED(context);
|
||||
WINPR_UNUSED(s);
|
||||
WINPR_UNUSED(hints);
|
||||
WINPR_UNUSED(res);
|
||||
WLog_ERR(TAG, "TODO: implement this");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void ndr_dump_KERB_RPC_INTERNAL_NAME(wLog* logger, UINT32 lvl, size_t indentLevel,
|
||||
const KERB_RPC_INTERNAL_NAME* obj)
|
||||
{
|
||||
WINPR_UNUSED(indentLevel);
|
||||
WINPR_UNUSED(obj);
|
||||
WLog_Print(logger, lvl, "TODO: implement this");
|
||||
}
|
||||
|
||||
static void ndr_descr_dump_KERB_RPC_INTERNAL_NAME(wLog* logger, UINT32 lvl, size_t indentLevel,
|
||||
const void* obj)
|
||||
{
|
||||
ndr_dump_KERB_RPC_INTERNAL_NAME(logger, lvl, indentLevel, obj);
|
||||
}
|
||||
|
||||
void ndr_destroy_KERB_RPC_INTERNAL_NAME(NdrContext* context, const void* hints,
|
||||
KERB_RPC_INTERNAL_NAME* obj)
|
||||
{
|
||||
WINPR_UNUSED(hints);
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
for (UINT32 i = 0; i < obj->nameHints.count; i++)
|
||||
ndr_destroy_RPC_UNICODE_STRING(context, NULL, &obj->Names[i]);
|
||||
|
||||
free(obj->Names);
|
||||
obj->Names = NULL;
|
||||
}
|
||||
|
||||
static void ndr_descr_destroy_KERB_RPC_INTERNAL_NAME(NdrContext* context, const void* hints,
|
||||
void* obj)
|
||||
{
|
||||
ndr_destroy_KERB_RPC_INTERNAL_NAME(context, hints, obj);
|
||||
}
|
||||
|
||||
static NdrMessageDescr KERB_RPC_INTERNAL_NAME_descr_s = { NDR_ARITY_SIMPLE,
|
||||
sizeof(KERB_RPC_INTERNAL_NAME),
|
||||
ndr_descr_read_KERB_RPC_INTERNAL_NAME,
|
||||
NULL,
|
||||
ndr_descr_destroy_KERB_RPC_INTERNAL_NAME,
|
||||
ndr_descr_dump_KERB_RPC_INTERNAL_NAME };
|
||||
|
||||
NdrMessageType ndr_KERB_RPC_INTERNAL_NAME_descr(void)
|
||||
{
|
||||
return &KERB_RPC_INTERNAL_NAME_descr_s;
|
||||
}
|
||||
|
||||
/* ========================== KERB_RPC_ENCRYPTION_KEY ======================== */
|
||||
|
||||
static const NdrFieldStruct KERB_RPC_ENCRYPTION_KEY_fields[] = {
|
||||
{ "reserved1", offsetof(KERB_RPC_ENCRYPTION_KEY, reserved1), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "reserved2", offsetof(KERB_RPC_ENCRYPTION_KEY, reserved2), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "reserved3", offsetof(KERB_RPC_ENCRYPTION_KEY, reserved3), NDR_NOT_POINTER, -1,
|
||||
&ndr_KERB_RPC_OCTET_STRING_descr_s }
|
||||
};
|
||||
static const NdrStructDescr KERB_RPC_ENCRYPTION_KEY_struct = {
|
||||
"KERB_RPC_ENCRYPTION_KEY", ARRAYSIZE(KERB_RPC_ENCRYPTION_KEY_fields),
|
||||
KERB_RPC_ENCRYPTION_KEY_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(KERB_RPC_ENCRYPTION_KEY)
|
||||
|
||||
/* ========================== BuildEncryptedAuthDataReq ======================== */
|
||||
|
||||
static const NdrFieldStruct BuildEncryptedAuthDataReq_fields[] = {
|
||||
{ "KeyUsage", offsetof(BuildEncryptedAuthDataReq, KeyUsage), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "key", offsetof(BuildEncryptedAuthDataReq, Key), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s },
|
||||
{ "plainAuthData", offsetof(BuildEncryptedAuthDataReq, PlainAuthData), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s }
|
||||
};
|
||||
static const NdrStructDescr BuildEncryptedAuthDataReq_struct = {
|
||||
"BuildEncryptedAuthDataReq", ARRAYSIZE(BuildEncryptedAuthDataReq_fields),
|
||||
BuildEncryptedAuthDataReq_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(BuildEncryptedAuthDataReq)
|
||||
|
||||
/* ========================== ComputeTgsChecksumReq ======================== */
|
||||
|
||||
static const NdrFieldStruct ComputeTgsChecksumReq_fields[] = {
|
||||
{ "requestBody", offsetof(ComputeTgsChecksumReq, requestBody), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "key", offsetof(ComputeTgsChecksumReq, Key), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s },
|
||||
{ "ChecksumType", offsetof(ComputeTgsChecksumReq, ChecksumType), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s }
|
||||
};
|
||||
static const NdrStructDescr ComputeTgsChecksumReq_struct = {
|
||||
"ComputeTgsChecksumReq", ARRAYSIZE(ComputeTgsChecksumReq_fields), ComputeTgsChecksumReq_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(ComputeTgsChecksumReq)
|
||||
|
||||
/* ========================== CreateApReqAuthenticatorReq ======================== */
|
||||
|
||||
static const NdrFieldStruct CreateApReqAuthenticatorReq_fields[] = {
|
||||
{ "EncryptionKey", offsetof(CreateApReqAuthenticatorReq, EncryptionKey), NDR_POINTER_NON_NULL,
|
||||
-1, &ndr_KERB_RPC_ENCRYPTION_KEY_descr_s },
|
||||
{ "SequenceNumber", offsetof(CreateApReqAuthenticatorReq, SequenceNumber), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "ClientName", offsetof(CreateApReqAuthenticatorReq, ClientName), NDR_POINTER_NON_NULL, -1,
|
||||
&KERB_RPC_INTERNAL_NAME_descr_s },
|
||||
{ "ClientRealm", offsetof(CreateApReqAuthenticatorReq, ClientRealm), NDR_POINTER_NON_NULL, -1,
|
||||
&RPC_UNICODE_STRING_descr_s },
|
||||
{ "SkewTime", offsetof(CreateApReqAuthenticatorReq, SkewTime), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_uint64_descr_s },
|
||||
{ "SubKey", offsetof(CreateApReqAuthenticatorReq, SubKey), NDR_POINTER, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s },
|
||||
{ "AuthData", offsetof(CreateApReqAuthenticatorReq, AuthData), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "GssChecksum", offsetof(CreateApReqAuthenticatorReq, GssChecksum), NDR_POINTER, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "KeyUsage", offsetof(CreateApReqAuthenticatorReq, KeyUsage), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
};
|
||||
static const NdrStructDescr CreateApReqAuthenticatorReq_struct = {
|
||||
"CreateApReqAuthenticatorReq", ARRAYSIZE(CreateApReqAuthenticatorReq_fields),
|
||||
CreateApReqAuthenticatorReq_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(CreateApReqAuthenticatorReq)
|
||||
|
||||
/* ========================== CreateApReqAuthenticatorResp ======================== */
|
||||
|
||||
static const NdrFieldStruct CreateApReqAuthenticatorResp_fields[] = {
|
||||
{ "AuthenticatorTime", offsetof(CreateApReqAuthenticatorResp, AuthenticatorTime),
|
||||
NDR_NOT_POINTER, -1, &ndr_uint64_descr_s },
|
||||
{ "Authenticator", offsetof(CreateApReqAuthenticatorResp, Authenticator), NDR_NOT_POINTER, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "KerbProtocolError", offsetof(CreateApReqAuthenticatorResp, KerbProtocolError),
|
||||
NDR_NOT_POINTER, -1, &ndr_uint32_descr_s },
|
||||
};
|
||||
|
||||
static const NdrStructDescr CreateApReqAuthenticatorResp_struct = {
|
||||
"CreateApReqAuthenticatorResp", ARRAYSIZE(CreateApReqAuthenticatorResp_fields),
|
||||
CreateApReqAuthenticatorResp_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(CreateApReqAuthenticatorResp)
|
||||
|
||||
/* ========================== UnpackKdcReplyBodyReq ======================== */
|
||||
|
||||
static const NdrFieldStruct UnpackKdcReplyBodyReq_fields[] = {
|
||||
{ "EncryptedData", offsetof(UnpackKdcReplyBodyReq, EncryptedData), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "Key", offsetof(UnpackKdcReplyBodyReq, Key), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s },
|
||||
{ "StrenghtenKey", offsetof(UnpackKdcReplyBodyReq, StrengthenKey), NDR_POINTER, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s },
|
||||
{ "Pdu", offsetof(UnpackKdcReplyBodyReq, Pdu), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s },
|
||||
{ "KeyUsage", offsetof(UnpackKdcReplyBodyReq, KeyUsage), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
};
|
||||
|
||||
static const NdrStructDescr UnpackKdcReplyBodyReq_struct = {
|
||||
"UnpackKdcReplyBodyReq", ARRAYSIZE(UnpackKdcReplyBodyReq_fields), UnpackKdcReplyBodyReq_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(UnpackKdcReplyBodyReq)
|
||||
|
||||
/* ========================== UnpackKdcReplyBodyResp ======================== */
|
||||
|
||||
static const NdrFieldStruct UnpackKdcReplyBodyResp_fields[] = {
|
||||
{ "KerbProtocolError", offsetof(UnpackKdcReplyBodyResp, KerbProtocolError), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "ReplyBody", offsetof(UnpackKdcReplyBodyResp, ReplyBody), NDR_NOT_POINTER, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s }
|
||||
};
|
||||
|
||||
static const NdrStructDescr UnpackKdcReplyBodyResp_struct = {
|
||||
"UnpackKdcReplyBodyResp", ARRAYSIZE(UnpackKdcReplyBodyResp_fields),
|
||||
UnpackKdcReplyBodyResp_fields
|
||||
};
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(UnpackKdcReplyBodyResp)
|
||||
|
||||
/* ========================== DecryptApReplyReq ======================== */
|
||||
|
||||
static const NdrFieldStruct DecryptApReplyReq_fields[] = {
|
||||
{ "EncryptedReply", offsetof(DecryptApReplyReq, EncryptedReply), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "Key", offsetof(DecryptApReplyReq, Key), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s }
|
||||
};
|
||||
|
||||
static const NdrStructDescr DecryptApReplyReq_struct = { "DecryptApReplyReq",
|
||||
ARRAYSIZE(DecryptApReplyReq_fields),
|
||||
DecryptApReplyReq_fields };
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(DecryptApReplyReq)
|
||||
|
||||
/* ========================== PackApReplyReq ======================== */
|
||||
|
||||
static const NdrFieldStruct PackApReplyReq_fields[] = {
|
||||
{ "Reply", offsetof(PackApReplyReq, Reply), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "ReplyBody", offsetof(PackApReplyReq, ReplyBody), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_ASN1_DATA_descr_s },
|
||||
{ "SessionKey", offsetof(PackApReplyReq, SessionKey), NDR_POINTER_NON_NULL, -1,
|
||||
&ndr_KERB_RPC_ENCRYPTION_KEY_descr_s }
|
||||
};
|
||||
|
||||
static const NdrStructDescr PackApReplyReq_struct = { "PackApReplyReq",
|
||||
ARRAYSIZE(PackApReplyReq_fields),
|
||||
PackApReplyReq_fields };
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(PackApReplyReq)
|
||||
|
||||
/* ========================== PackApReplyResp ======================== */
|
||||
|
||||
static const NdrFieldStruct PackApReplyResp_fields[] = {
|
||||
{ "PackedReplySize", offsetof(PackApReplyResp, PackedReplyHints), NDR_NOT_POINTER, -1,
|
||||
&ndr_uint32_descr_s },
|
||||
{ "PackedReply", offsetof(PackApReplyResp, PackedReply), NDR_POINTER_NON_NULL, 0,
|
||||
&ndr_uint8Array_descr_s },
|
||||
};
|
||||
|
||||
static const NdrStructDescr PackApReplyResp_struct = { "PackApReplyResp",
|
||||
ARRAYSIZE(PackApReplyResp_fields),
|
||||
PackApReplyResp_fields };
|
||||
|
||||
RDPEAR_SIMPLE_MESSAGE_TYPE(PackApReplyResp)
|
40
channels/rdpear/common/test/CMakeLists.txt
Normal file
40
channels/rdpear/common/test/CMakeLists.txt
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
set(MODULE_NAME "TestRdpear")
|
||||
set(MODULE_PREFIX "TEST_RDPEAR")
|
||||
|
||||
set(TEST_RDPEAR_DRIVER TestRdpear.c)
|
||||
|
||||
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(TEST_RDPEAR_TESTS
|
||||
TestNdr.c
|
||||
)
|
||||
|
||||
if (BUILD_TESTING_INTERNAL)
|
||||
list(APPEND TEST_RDPEAR_TESTS
|
||||
TestNdrEar.c
|
||||
)
|
||||
endif()
|
||||
|
||||
create_test_sourcelist(TEST_RDPEAR_SRCS
|
||||
TestRdpear.c
|
||||
${TEST_RDPEAR_TESTS}
|
||||
)
|
||||
|
||||
add_executable(${MODULE_NAME} ${TEST_RDPEAR_SRCS})
|
||||
|
||||
add_definitions(-DTESTING_OUTPUT_DIRECTORY="${PROJECT_BINARY_DIR}")
|
||||
add_definitions(-DTESTING_SRC_DIRECTORY="${PROJECT_SOURCE_DIR}")
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp winpr freerdp-client)
|
||||
|
||||
|
||||
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
|
||||
|
||||
foreach(test ${${MODULE_PREFIX}_TESTS})
|
||||
get_filename_component(TestName ${test} NAME_WE)
|
||||
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
|
||||
endforeach()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Rdpear/Test")
|
||||
|
39
channels/rdpear/common/test/TestNdr.c
Normal file
39
channels/rdpear/common/test/TestNdr.c
Normal file
@ -0,0 +1,39 @@
|
||||
#include <rdpear-common/ndr.h>
|
||||
|
||||
int TestNdr(int argc, char* argv[])
|
||||
{
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
int retCode = -2;
|
||||
NdrContext* context = ndr_context_new(FALSE, 1);
|
||||
if (!context)
|
||||
return -1;
|
||||
|
||||
BYTE payload[] = {
|
||||
// == conformant array ==
|
||||
0x02, 0x00, 0x00, 0x00, // (nitems)
|
||||
0x30, 0x00, // content
|
||||
0x00, 0x00 // (padding)
|
||||
};
|
||||
wStream staticS;
|
||||
wStream* s = Stream_StaticInit(&staticS, payload, sizeof(payload));
|
||||
|
||||
BYTE* target = NULL;
|
||||
NdrArrayHints hints = { 2 };
|
||||
NdrDeferredEntry e = { 0x020028, "arrayContent", &hints, &target, ndr_uint8Array_descr() };
|
||||
|
||||
if (!ndr_push_deferreds(context, &e, 1))
|
||||
goto out;
|
||||
|
||||
if (!ndr_treat_deferred_read(context, s))
|
||||
goto out;
|
||||
|
||||
NdrMessageType descr = ndr_uint8Array_descr();
|
||||
descr->destroyFn(context, &hints, target);
|
||||
free(target);
|
||||
retCode = 0;
|
||||
out:
|
||||
ndr_context_destroy(&context);
|
||||
return retCode;
|
||||
}
|
381
channels/rdpear/common/test/TestNdrEar.c
Normal file
381
channels/rdpear/common/test/TestNdrEar.c
Normal file
@ -0,0 +1,381 @@
|
||||
#include <winpr/print.h>
|
||||
|
||||
#include <rdpear-common/ndr.h>
|
||||
#include <rdpear-common/rdpear_common.h>
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a) > (b)) ? (a) : (b)
|
||||
#endif
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a) < (b)) ? (a) : (b)
|
||||
#endif
|
||||
|
||||
static BYTE nextValue(BYTE old, INT32 offset, char symbol, char startSymbol)
|
||||
{
|
||||
const INT32 uold = 16 * old;
|
||||
const INT32 diff = symbol - startSymbol;
|
||||
const INT32 res = uold + diff + offset;
|
||||
return (BYTE)MIN(MAX(0, res), UINT8_MAX);
|
||||
}
|
||||
|
||||
static BYTE* parseHexBlock(const char* str, size_t* plen)
|
||||
{
|
||||
WINPR_ASSERT(str);
|
||||
WINPR_ASSERT(plen);
|
||||
|
||||
BYTE* ret = malloc(strlen(str) / 2);
|
||||
BYTE* dest = ret;
|
||||
const char* ptr = str;
|
||||
BYTE tmp = 0;
|
||||
size_t nchars = 0;
|
||||
size_t len = 0;
|
||||
|
||||
for (; *ptr; ptr++)
|
||||
{
|
||||
switch (*ptr)
|
||||
{
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\t':
|
||||
if (nchars)
|
||||
{
|
||||
WLog_ERR("", "error parsing hex block, unpaired char");
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
tmp = nextValue(tmp, 0, *ptr, '0');
|
||||
nchars++;
|
||||
if (nchars == 2)
|
||||
{
|
||||
*dest = tmp;
|
||||
dest++;
|
||||
len++;
|
||||
tmp = 0;
|
||||
nchars = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
tmp = nextValue(tmp, 10, *ptr, 'a');
|
||||
nchars++;
|
||||
if (nchars == 2)
|
||||
{
|
||||
*dest = tmp;
|
||||
dest++;
|
||||
len++;
|
||||
tmp = 0;
|
||||
nchars = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
tmp = nextValue(tmp, 10, *ptr, 'A');
|
||||
nchars++;
|
||||
if (nchars == 2)
|
||||
{
|
||||
*dest = tmp;
|
||||
dest++;
|
||||
len++;
|
||||
tmp = 0;
|
||||
nchars = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WLog_ERR("", "invalid char in hex block");
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
*plen = len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int TestNdrEarWrite(int argc, char* argv[])
|
||||
{
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
int rc = -1;
|
||||
BYTE buffer[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
KERB_ASN1_DATA asn1 = { 7, { 16 }, buffer };
|
||||
|
||||
wStream* s = Stream_New(NULL, 100);
|
||||
if (!s)
|
||||
return -1;
|
||||
|
||||
NdrContext* context = ndr_context_new(FALSE, 1);
|
||||
if (!context)
|
||||
goto fail;
|
||||
|
||||
if (!ndr_write_KERB_ASN1_DATA(context, s, NULL, &asn1))
|
||||
goto fail;
|
||||
if (!ndr_treat_deferred_write(context, s))
|
||||
goto fail;
|
||||
|
||||
// winpr_HexDump("", WLOG_DEBUG, Stream_Buffer(s), Stream_GetPosition(s));
|
||||
|
||||
rc = 0;
|
||||
fail:
|
||||
ndr_context_destroy(&context);
|
||||
Stream_Free(s, TRUE);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int TestNdrEarRead(int argc, char* argv[])
|
||||
{
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
int retCode = -2;
|
||||
|
||||
/* ====================================================================== */
|
||||
NdrContext* context = ndr_context_new(FALSE, 1);
|
||||
if (!context)
|
||||
return -1;
|
||||
|
||||
wStream staticS = { 0 };
|
||||
wStream* s = NULL;
|
||||
|
||||
#if 0
|
||||
BYTE payload[] = {
|
||||
0x00, 0x00, 0x00, 0x00, // (PduType)
|
||||
0x02, 0x00, 0x00, 0x00, // (Length)
|
||||
0x28, 0x00, 0x02, 0x00, // (Asn1Buffer)
|
||||
|
||||
// == conformant array ==
|
||||
0x02, 0x00, 0x00, 0x00, // (nitems)
|
||||
0x30, 0x00, // content
|
||||
0x00, 0x00 // (padding)
|
||||
};
|
||||
s = Stream_StaticInit(&staticS, payload, sizeof(payload));
|
||||
|
||||
KERB_ASN1_DATA asn1 = { 0 };
|
||||
if (!ndr_read_KERB_ASN1_DATA(context, s, NULL, &asn1) || !ndr_treat_deferred_read(context, s) ||
|
||||
asn1.Asn1BufferHints.count != 2 || *asn1.Asn1Buffer != 0x30)
|
||||
goto out;
|
||||
KERB_ASN1_DATA_destroy(context, &asn1);
|
||||
ndr_context_reset(context);
|
||||
|
||||
/* ====================================================================== */
|
||||
BYTE payload2[] = {
|
||||
// ------------ a RPC_UNICODE_STRING: Administrateur -------------------------
|
||||
0x1c, 0x00, // (Length)
|
||||
0x1e, 0x00, // (MaximumLength)
|
||||
0x1c, 0x00, 0x02, 0x00, // (Buffer ptr)
|
||||
// == conformant array ==
|
||||
0x0f, 0x00, 0x00, 0x00, // (maximum count)
|
||||
0x00, 0x00, 0x00, 0x00, // (offset)
|
||||
0x0e, 0x00, 0x00, 0x00, // (length)
|
||||
|
||||
0x48, 0x00, 0x41, 0x00, 0x52, 0x00, 0x44, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x49, 0x00, 0x4e,
|
||||
0x00, 0x47, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x00, 0x00,
|
||||
|
||||
0x00, 0x00
|
||||
};
|
||||
retCode = -3;
|
||||
|
||||
s = Stream_StaticInit(&staticS, payload2, sizeof(payload2));
|
||||
RPC_UNICODE_STRING unicode = { 0 };
|
||||
if (!ndr_read_RPC_UNICODE_STRING(context, s, NULL, &unicode) || !ndr_treat_deferred_read(context, s))
|
||||
goto out;
|
||||
RPC_UNICODE_STRING_destroy(context, &unicode);
|
||||
ndr_context_reset(context);
|
||||
|
||||
/* ====================================================================== */
|
||||
BYTE payload3[] = {
|
||||
// ------------ an KERB_RPC_INTERNAL_NAME: HARDENING3.COM -------------------------
|
||||
0x01, 0x00, // (NameType)
|
||||
0x01, 0x00, // (NameCount)
|
||||
0x10, 0x00, 0x02, 0x00, // (Names)
|
||||
// == conformant array ==
|
||||
0x01, 0x00, 0x00, 0x00, // (nitems)
|
||||
|
||||
// = RPC_UNICODE_STRING =
|
||||
0x1c, 0x00, // (Length)
|
||||
0x1e, 0x00, // (MaximumLength)
|
||||
0x14, 0x00, 0x02, 0x00, /// (Buffer ptr)
|
||||
// == Uni-dimensional Conformant-varying Array ==
|
||||
0x0f, 0x00, 0x00, 0x00, // (maximum count)
|
||||
0x00, 0x00, 0x00, 0x00, // (offset)
|
||||
0x0e, 0x00, 0x00, 0x00, // (length)
|
||||
0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74,
|
||||
0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x75, 0x00, 0x72, 0x00, 0x00, 0x00,
|
||||
|
||||
0x00, 0x00
|
||||
};
|
||||
KERB_RPC_INTERNAL_NAME intName = { 0 };
|
||||
retCode = -4;
|
||||
s = Stream_StaticInit(&staticS, payload3, sizeof(payload3));
|
||||
if (!ndr_read_KERB_RPC_INTERNAL_NAME(context, s, NULL, &intName) || !ndr_treat_deferred_read(context, s))
|
||||
goto out;
|
||||
KERB_RPC_INTERNAL_NAME_destroy(context, &intName);
|
||||
ndr_context_reset(context);
|
||||
#endif
|
||||
|
||||
/* ====================================================================== */
|
||||
#if 0
|
||||
BYTE payload4[] = {
|
||||
0x03, 0x01, 0x03, 0x01, // unionId / unionId
|
||||
0x04, 0x00, 0x02, 0x00, // (EncryptionKey ptr)
|
||||
0xf8, 0xca, 0x95, 0x11, // (SequenceNumber)
|
||||
0x0c, 0x00, 0x02, 0x00, // (ClientName ptr)
|
||||
0x18, 0x00, 0x02, 0x00, // (ClientRealm ptr)
|
||||
0x20, 0x00, 0x02, 0x00, // (SkewTime ptr)
|
||||
0x00, 0x00, 0x00, 0x00, // (SubKey ptr)
|
||||
0x24, 0x00, 0x02, 0x00, // (AuthData ptr)
|
||||
0x2c, 0x00, 0x02, 0x00, // (GssChecksum ptr)
|
||||
0x07, 0x00, 0x00, 0x00, // (KeyUsage)
|
||||
|
||||
// === EncryptionKey ===
|
||||
0x40, 0xe9, 0x12, 0xdf, // reserved1
|
||||
0x12, 0x00, 0x00, 0x00, // reserved2
|
||||
// KERB_RPC_OCTET_STRING
|
||||
0x4c, 0x00, 0x00, 0x00, // (length)
|
||||
0x08, 0x00, 0x02, 0x00, // (value ptr)
|
||||
// == conformant array ==
|
||||
0x4c, 0x00, 0x00, 0x00, // (length)
|
||||
0xc4, 0x41, 0xee, 0x34,
|
||||
0x82, 0x2b, 0x29, 0x61, 0xe2, 0x96, 0xb5, 0x75, 0x61, 0x2d, 0xbf, 0x86, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x08, 0x60, 0x2e,
|
||||
0x30, 0x3e, 0xfe, 0x56, 0x11, 0xf0, 0x31, 0xf2, 0xd6, 0x2e, 0x3d, 0x33, 0xfe, 0xce, 0x56, 0x12,
|
||||
0xbf, 0xb2, 0xe5, 0x86, 0x29, 0x8d, 0x29, 0x74, 0x1f, 0x8a, 0xf9, 0xb9, 0x8c, 0xd4, 0x86, 0x3a,
|
||||
0x21, 0x92, 0xb2, 0x07, 0x95, 0x4b, 0xea, 0xee,
|
||||
|
||||
//=== ClientName - KERB_RPC_INTERNAL_NAME ===
|
||||
0x01, 0x00, // (NameType)
|
||||
0x01, 0x00, // (NameCount)
|
||||
0x10, 0x00, 0x02, 0x00, // (Names)
|
||||
|
||||
0x01, 0x00, 0x00, 0x00, // (nitems)
|
||||
|
||||
// = RPC_UNICODE_STRING =
|
||||
0x1c, 0x00, // (Length)
|
||||
0x1e, 0x00, // (MaximumLength)
|
||||
0x14, 0x00, 0x02, 0x00, //(Buffer ptr)
|
||||
// == Uni-dimensional Conformant-varying Array ==
|
||||
0x0f, 0x00, 0x00, 0x00, // (maximum count)
|
||||
0x00, 0x00, 0x00, 0x00, // (offset)
|
||||
0x0e, 0x00, 0x00, 0x00, // (length)
|
||||
0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00,
|
||||
0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x75, 0x00, 0x72, 0x00,
|
||||
|
||||
// === ClientRealm - RPC_UNICODE_STRING ===
|
||||
0x1c, 0x00, // (Length)
|
||||
0x1e, 0x00, // (MaximumLength)
|
||||
0x1c, 0x00, 0x02, 0x00, // (Buffer ptr)
|
||||
// == Uni-dimensional conformant varying array ==
|
||||
0x0f, 0x00, 0x00, 0x00, // (maximum count)
|
||||
0x00, 0x00, 0x00, 0x00, // (offset)
|
||||
0x0e, 0x00, 0x00, 0x00, // (length)
|
||||
0x48, 0x00, 0x41, 0x00, 0x52, 0x00, 0x44, 0x00, 0x45, 0x00, 0x4e, 0x00, 0x49, 0x00, 0x4e, 0x00,
|
||||
0x47, 0x00, 0x33, 0x00, 0x2e, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x4d, 0x00,
|
||||
|
||||
0x00, 0x00, 0x00, 0x00, // padding
|
||||
|
||||
// == SkewTime ==
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// === AuthData - KERB_ASN1_DATA ==
|
||||
0x00, 0x00, 0x00, 0x00, // (PduType)
|
||||
0x02, 0x00, 0x00, 0x00, // (Length)
|
||||
0x28, 0x00, 0x02, 0x00, // (Asn1Buffer)
|
||||
// == conformant array ==
|
||||
0x02, 0x00, 0x00, 0x00, // (nitems)
|
||||
0x30, 0x00,
|
||||
0x00, 0x00, // (padding)
|
||||
|
||||
// === GssChecksum - KERB_ASN1_DATA ===
|
||||
0x08, 0x00, 0x00, 0x00, // (PduType)
|
||||
0x1b, 0x00, 0x00, 0x00, // (Length)
|
||||
0x30, 0x00, 0x02, 0x00, // (Asn1Buffer)
|
||||
// == conformant array ==
|
||||
0x1b, 0x00, 0x00, 0x00, // (length)
|
||||
0x30, 0x19,
|
||||
0xa0, 0x03,
|
||||
0x02, 0x01, 0x07,
|
||||
0xa1, 0x12,
|
||||
0x04, 0x10, 0xb9, 0x4f, 0xcd, 0xae, 0xd9, 0xa8, 0xff, 0x49, 0x69, 0x5a, 0xd1,
|
||||
0x1d, 0x38, 0x49, 0xb6, 0x92, 0x00
|
||||
};
|
||||
size_t sizeofPayload4 = sizeof(payload4);
|
||||
#endif
|
||||
#if 1
|
||||
size_t sizeofPayload4 = 0;
|
||||
BYTE* payload4 = parseHexBlock("03 01 03 01 \
|
||||
04 00 02 00 38 9e ef 6b 0c 00 02 00 18 00 02 00 \
|
||||
20 00 02 00 00 00 00 00 24 00 02 00 2c 00 02 00 \
|
||||
07 00 00 00 13 8a a5 a8 12 00 00 00 20 00 00 00 \
|
||||
08 00 02 00 20 00 00 00 c9 03 42 a8 17 8f d9 c4 \
|
||||
9b d2 c4 6e 73 64 98 7b 90 f5 9a 28 77 8e ca de \
|
||||
29 2e a3 8d 8a 56 36 d5 01 00 01 00 10 00 02 00 \
|
||||
01 00 00 00 1c 00 1e 00 14 00 02 00 0f 00 00 00 \
|
||||
00 00 00 00 0e 00 00 00 41 00 64 00 6d 00 69 00 \
|
||||
6e 00 69 00 73 00 74 00 72 00 61 00 74 00 65 00 \
|
||||
75 00 72 00 1c 00 1e 00 1c 00 02 00 0f 00 00 00 \
|
||||
00 00 00 00 0e 00 00 00 48 00 41 00 52 00 44 00 \
|
||||
45 00 4e 00 49 00 4e 00 47 00 33 00 2e 00 43 00 \
|
||||
4f 00 4d 00 00 00 00 00 00 00 00 00 00 00 00 00 \
|
||||
02 00 00 00 28 00 02 00 02 00 00 00 30 00 00 00 \
|
||||
08 00 00 00 1b 00 00 00 30 00 02 00 1b 00 00 00 \
|
||||
30 19 a0 03 02 01 07 a1 12 04 10 e4 aa ff 2b 93 \
|
||||
97 4c f2 5c 0b 49 85 72 92 94 54 00",
|
||||
&sizeofPayload4);
|
||||
|
||||
if (!payload4)
|
||||
goto out;
|
||||
#endif
|
||||
|
||||
CreateApReqAuthenticatorReq createApReqAuthenticatorReq = { 0 };
|
||||
s = Stream_StaticInit(&staticS, payload4, sizeofPayload4);
|
||||
if (!ndr_skip_bytes(context, s, 4) || /* skip union id */
|
||||
!ndr_read_CreateApReqAuthenticatorReq(context, s, NULL, &createApReqAuthenticatorReq) ||
|
||||
!ndr_treat_deferred_read(context, s) || createApReqAuthenticatorReq.KeyUsage != 7 ||
|
||||
!createApReqAuthenticatorReq.EncryptionKey || createApReqAuthenticatorReq.SubKey ||
|
||||
!createApReqAuthenticatorReq.ClientName ||
|
||||
createApReqAuthenticatorReq.ClientName->nameHints.count != 1 ||
|
||||
!createApReqAuthenticatorReq.ClientRealm || !createApReqAuthenticatorReq.AuthData ||
|
||||
createApReqAuthenticatorReq.AuthData->Asn1BufferHints.count != 2 ||
|
||||
!createApReqAuthenticatorReq.SkewTime ||
|
||||
createApReqAuthenticatorReq.SkewTime->QuadPart != 0)
|
||||
goto out;
|
||||
ndr_destroy_CreateApReqAuthenticatorReq(context, NULL, &createApReqAuthenticatorReq);
|
||||
ndr_context_reset(context);
|
||||
|
||||
/* ============ successful end of test =============== */
|
||||
retCode = 0;
|
||||
out:
|
||||
free(payload4);
|
||||
ndr_context_destroy(&context);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
int TestNdrEar(int argc, char* argv[])
|
||||
{
|
||||
const int rc = TestNdrEarWrite(argc, argv);
|
||||
if (rc)
|
||||
return rc;
|
||||
return TestNdrEarRead(argc, argv);
|
||||
}
|
@ -22,7 +22,7 @@ if(NOT WITH_SWSCALE OR NOT WITH_FFMPEG)
|
||||
endif()
|
||||
|
||||
# currently camera redirect client supported for platforms with Video4Linux only
|
||||
find_package(SWScale REQUIRED)
|
||||
find_package(FFmpeg REQUIRED COMPONENTS SWSCALE)
|
||||
find_package(V4L)
|
||||
if(V4L_FOUND)
|
||||
set(WITH_V4L ON)
|
||||
@ -31,7 +31,17 @@ else()
|
||||
message(FATAL_ERROR "libv4l-dev required for CHANNEL_RDPECAM_CLIENT")
|
||||
endif()
|
||||
|
||||
include_directories(${SWScale_INCLUDE_DIR})
|
||||
option(RDPECAM_INPUT_FORMAT_H264 "[MS-RDPECAM] Enable H264 camera format (passthrough)" ON)
|
||||
if(RDPECAM_INPUT_FORMAT_H264)
|
||||
add_definitions("-DWITH_INPUT_FORMAT_H264")
|
||||
endif()
|
||||
|
||||
option(RDPECAM_INPUT_FORMAT_MJPG "[MS-RDPECAM] Enable MJPG camera format" ON)
|
||||
if(RDPECAM_INPUT_FORMAT_MJPG)
|
||||
add_definitions("-DWITH_INPUT_FORMAT_MJPG")
|
||||
endif()
|
||||
|
||||
include_directories(SYSTEM ${SWSCALE_INCLUDE_DIRS})
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
camera_device_enum_main.c
|
||||
@ -42,7 +52,7 @@ set(${MODULE_PREFIX}_SRCS
|
||||
set(${MODULE_PREFIX}_LIBS
|
||||
freerdp
|
||||
winpr
|
||||
${SWScale_LIBRARY}
|
||||
${SWSCALE_LIBRARIES}
|
||||
${FFMPEG_LIBRARIES}
|
||||
)
|
||||
|
||||
|
@ -25,6 +25,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(WITH_INPUT_FORMAT_MJPG)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#endif
|
||||
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
|
||||
@ -54,7 +58,7 @@
|
||||
* for sample responses. Excessive size is to make sure any sample
|
||||
* will fit in, even with highest resolution.
|
||||
*/
|
||||
#define ECAM_SAMPLE_RESPONSE_BUFFER_SIZE (1024 * 4050)
|
||||
#define ECAM_SAMPLE_RESPONSE_BUFFER_SIZE (1024ULL * 4050ULL)
|
||||
|
||||
typedef struct s_ICamHal ICamHal;
|
||||
|
||||
@ -94,8 +98,15 @@ typedef struct
|
||||
wStream* sampleRespBuffer;
|
||||
|
||||
H264_CONTEXT* h264;
|
||||
|
||||
#if defined(WITH_INPUT_FORMAT_MJPG)
|
||||
AVCodecContext* avContext;
|
||||
AVPacket* avInputPkt;
|
||||
AVFrame* avOutFrame;
|
||||
#endif
|
||||
|
||||
/* sws_scale */
|
||||
struct SwsContext* sws;
|
||||
int srcLineSizes[4];
|
||||
|
||||
} CameraDeviceStream;
|
||||
|
||||
@ -160,7 +171,7 @@ typedef struct
|
||||
typedef FREERDP_CAMERA_HAL_ENTRY_POINTS* PFREERDP_CAMERA_HAL_ENTRY_POINTS;
|
||||
|
||||
/* entry point called by addin manager */
|
||||
typedef UINT (*PFREERDP_CAMERA_HAL_ENTRY)(PFREERDP_CAMERA_HAL_ENTRY_POINTS pEntryPoints);
|
||||
typedef UINT(VCAPITYPE* PFREERDP_CAMERA_HAL_ENTRY)(PFREERDP_CAMERA_HAL_ENTRY_POINTS pEntryPoints);
|
||||
|
||||
/* common functions */
|
||||
UINT ecam_channel_send_generic_msg(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
|
||||
@ -171,8 +182,10 @@ UINT ecam_channel_write(CameraPlugin* ecam, GENERIC_CHANNEL_CALLBACK* hchannel,
|
||||
wStream* out, BOOL freeStream);
|
||||
|
||||
/* ecam device interface */
|
||||
void ecam_dev_destroy(CameraDevice* dev);
|
||||
|
||||
WINPR_ATTR_MALLOC(ecam_dev_destroy, 1)
|
||||
CameraDevice* ecam_dev_create(CameraPlugin* ecam, const char* deviceId, const char* deviceName);
|
||||
void ecam_dev_destroy(void* dev);
|
||||
|
||||
/* video encoding interface */
|
||||
BOOL ecam_encoder_context_init(CameraDeviceStream* stream);
|
||||
|
@ -316,6 +316,12 @@ static UINT ecam_on_new_channel_connection(IWTSListenerCallback* pListenerCallba
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void ecam_dev_destroy_pv(void* obj)
|
||||
{
|
||||
CameraDevice* dev = obj;
|
||||
ecam_dev_destroy(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -349,7 +355,7 @@ static UINT ecam_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
|
||||
|
||||
wObject* obj = HashTable_ValueObject(ecam->devices);
|
||||
WINPR_ASSERT(obj);
|
||||
obj->fnObjectFree = ecam_dev_destroy;
|
||||
obj->fnObjectFree = ecam_dev_destroy_pv;
|
||||
|
||||
ecam->hlistener = (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
|
||||
|
||||
@ -468,11 +474,14 @@ static UINT ecam_load_hal_plugin(CameraPlugin* ecam, const char* name, const ADD
|
||||
|
||||
FREERDP_CAMERA_HAL_ENTRY_POINTS entryPoints = { 0 };
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
const PFREERDP_CAMERA_HAL_ENTRY entry =
|
||||
(const PFREERDP_CAMERA_HAL_ENTRY)freerdp_load_channel_addin_entry(RDPECAM_CHANNEL_NAME,
|
||||
name, NULL, 0);
|
||||
union
|
||||
{
|
||||
PVIRTUALCHANNELENTRY pvce;
|
||||
const PFREERDP_CAMERA_HAL_ENTRY entry;
|
||||
} cnv;
|
||||
cnv.pvce = freerdp_load_channel_addin_entry(RDPECAM_CHANNEL_NAME, name, NULL, 0);
|
||||
|
||||
if (entry == NULL)
|
||||
if (cnv.entry == NULL)
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"freerdp_load_channel_addin_entry did not return any function pointers for %s ",
|
||||
@ -485,7 +494,8 @@ static UINT ecam_load_hal_plugin(CameraPlugin* ecam, const char* name, const ADD
|
||||
entryPoints.args = args;
|
||||
entryPoints.ecam = ecam;
|
||||
|
||||
if ((error = entry(&entryPoints)))
|
||||
error = cnv.entry(&entryPoints);
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "%s entry returned error %" PRIu32 ".", name, error);
|
||||
return error;
|
||||
@ -500,7 +510,7 @@ static UINT ecam_load_hal_plugin(CameraPlugin* ecam, const char* name, const ADD
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT rdpecam_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpecam_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
|
||||
|
@ -24,11 +24,16 @@
|
||||
#define TAG CHANNELS_TAG("rdpecam-device.client")
|
||||
|
||||
/* supported formats in preference order:
|
||||
* passthrough, I420 (used as input for H264 encoder), other YUV based, RGB based
|
||||
* H264, MJPG, I420 (used as input for H264 encoder), other YUV based, RGB based
|
||||
*/
|
||||
static const CAM_MEDIA_FORMAT_INFO supportedFormats[] = {
|
||||
/* inputFormat, outputFormat */
|
||||
{ CAM_MEDIA_FORMAT_H264, CAM_MEDIA_FORMAT_H264 }, /* passthrough: comment out to disable */
|
||||
/* inputFormat, outputFormat */
|
||||
#if defined(WITH_INPUT_FORMAT_H264)
|
||||
{ CAM_MEDIA_FORMAT_H264, CAM_MEDIA_FORMAT_H264 }, /* passthrough */
|
||||
#endif
|
||||
#if defined(WITH_INPUT_FORMAT_MJPG)
|
||||
{ CAM_MEDIA_FORMAT_MJPG, CAM_MEDIA_FORMAT_H264 },
|
||||
#endif
|
||||
{ CAM_MEDIA_FORMAT_I420, CAM_MEDIA_FORMAT_H264 },
|
||||
{ CAM_MEDIA_FORMAT_YUY2, CAM_MEDIA_FORMAT_H264 },
|
||||
{ CAM_MEDIA_FORMAT_NV12, CAM_MEDIA_FORMAT_H264 },
|
||||
@ -37,11 +42,6 @@ static const CAM_MEDIA_FORMAT_INFO supportedFormats[] = {
|
||||
};
|
||||
static const size_t nSupportedFormats = ARRAYSIZE(supportedFormats);
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
static void ecam_dev_write_media_type(wStream* s, CAM_MEDIA_TYPE_DESCRIPTION* mediaType)
|
||||
{
|
||||
WINPR_ASSERT(mediaType);
|
||||
@ -56,11 +56,6 @@ static void ecam_dev_write_media_type(wStream* s, CAM_MEDIA_TYPE_DESCRIPTION* me
|
||||
Stream_Write_UINT8(s, mediaType->Flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return TRUE if success
|
||||
*/
|
||||
static BOOL ecam_dev_read_media_type(wStream* s, CAM_MEDIA_TYPE_DESCRIPTION* mediaType)
|
||||
{
|
||||
WINPR_ASSERT(mediaType);
|
||||
@ -76,11 +71,6 @@ static BOOL ecam_dev_read_media_type(wStream* s, CAM_MEDIA_TYPE_DESCRIPTION* med
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
static void ecam_dev_print_media_type(CAM_MEDIA_TYPE_DESCRIPTION* mediaType)
|
||||
{
|
||||
WINPR_ASSERT(mediaType);
|
||||
@ -149,7 +139,7 @@ static UINT ecam_dev_sample_captured_callback(CameraDevice* dev, int streamIndex
|
||||
}
|
||||
else /* passthrough */
|
||||
{
|
||||
encodedSample = (BYTE*)sample;
|
||||
encodedSample = WINPR_CAST_CONST_PTR_AWAY(sample, BYTE*);
|
||||
encodedSize = size;
|
||||
}
|
||||
|
||||
@ -163,11 +153,6 @@ static UINT ecam_dev_sample_captured_callback(CameraDevice* dev, int streamIndex
|
||||
return ecam_dev_send_sample_response(dev, streamIndex, encodedSample, encodedSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
static void ecam_dev_stop_stream(CameraDevice* dev, size_t streamIndex)
|
||||
{
|
||||
WINPR_ASSERT(dev);
|
||||
@ -493,7 +478,7 @@ static UINT ecam_dev_process_media_type_list_request(CameraDevice* dev,
|
||||
stream->formats = supportedFormats[formatIndex];
|
||||
|
||||
/* replacing inputFormat with outputFormat in mediaTypes before sending response */
|
||||
for (int i = 0; i < nMediaTypes; i++)
|
||||
for (size_t i = 0; i < nMediaTypes; i++)
|
||||
{
|
||||
mediaTypes[i].Format = streamOutputFormat(stream);
|
||||
mediaTypes[i].Flags = CAM_MEDIA_TYPE_DESCRIPTION_FLAG_DecodingRequired;
|
||||
@ -794,11 +779,9 @@ CameraDevice* ecam_dev_create(CameraPlugin* ecam, const char* deviceId, const ch
|
||||
*
|
||||
* OBJECT_FREE_FN for devices hash table value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void ecam_dev_destroy(void* obj)
|
||||
void ecam_dev_destroy(CameraDevice* dev)
|
||||
{
|
||||
CameraDevice* dev = (CameraDevice*)obj;
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
@ -817,5 +800,4 @@ void ecam_dev_destroy(void* obj)
|
||||
ecam_dev_stop_stream(dev, i);
|
||||
|
||||
free(dev);
|
||||
return;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/winpr.h>
|
||||
|
||||
#include "camera.h"
|
||||
|
||||
@ -86,6 +87,60 @@ static enum AVPixelFormat ecamToAVPixFormat(CAM_MEDIA_FORMAT ecamFormat)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
* initialize libswscale
|
||||
*
|
||||
* @return success/failure
|
||||
*/
|
||||
static BOOL ecam_init_sws_context(CameraDeviceStream* stream, enum AVPixelFormat pixFormat)
|
||||
{
|
||||
WINPR_ASSERT(stream);
|
||||
|
||||
if (stream->sws)
|
||||
return TRUE;
|
||||
|
||||
/* replacing deprecated JPEG formats, still produced by decoder */
|
||||
switch (pixFormat)
|
||||
{
|
||||
case AV_PIX_FMT_YUVJ411P:
|
||||
pixFormat = AV_PIX_FMT_YUV411P;
|
||||
break;
|
||||
|
||||
case AV_PIX_FMT_YUVJ420P:
|
||||
pixFormat = AV_PIX_FMT_YUV420P;
|
||||
break;
|
||||
|
||||
case AV_PIX_FMT_YUVJ422P:
|
||||
pixFormat = AV_PIX_FMT_YUV422P;
|
||||
break;
|
||||
|
||||
case AV_PIX_FMT_YUVJ440P:
|
||||
pixFormat = AV_PIX_FMT_YUV440P;
|
||||
break;
|
||||
|
||||
case AV_PIX_FMT_YUVJ444P:
|
||||
pixFormat = AV_PIX_FMT_YUV444P;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const int width = (int)stream->currMediaType.Width;
|
||||
const int height = (int)stream->currMediaType.Height;
|
||||
|
||||
stream->sws = sws_getContext(width, height, pixFormat, width, height, AV_PIX_FMT_YUV420P, 0,
|
||||
NULL, NULL, NULL);
|
||||
if (!stream->sws)
|
||||
{
|
||||
WLog_ERR(TAG, "sws_getContext failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -96,26 +151,71 @@ static BOOL ecam_encoder_compress_h264(CameraDeviceStream* stream, const BYTE* s
|
||||
{
|
||||
UINT32 dstSize = 0;
|
||||
BYTE* srcSlice[4] = { 0 };
|
||||
int srcLineSizes[4] = { 0 };
|
||||
BYTE* yuv420pData[3] = { 0 };
|
||||
UINT32 yuv420pStride[3] = { 0 };
|
||||
prim_size_t size = { 0 };
|
||||
size.width = stream->currMediaType.Width;
|
||||
size.height = stream->currMediaType.Height;
|
||||
prim_size_t size = { stream->currMediaType.Width, stream->currMediaType.Height };
|
||||
CAM_MEDIA_FORMAT inputFormat = streamInputFormat(stream);
|
||||
enum AVPixelFormat pixFormat = ecamToAVPixFormat(inputFormat);
|
||||
enum AVPixelFormat pixFormat = AV_PIX_FMT_NONE;
|
||||
|
||||
#if defined(WITH_INPUT_FORMAT_MJPG)
|
||||
if (inputFormat == CAM_MEDIA_FORMAT_MJPG)
|
||||
{
|
||||
stream->avInputPkt->data = WINPR_CAST_CONST_PTR_AWAY(srcData, uint8_t*);
|
||||
WINPR_ASSERT(srcSize <= INT32_MAX);
|
||||
stream->avInputPkt->size = (int)srcSize;
|
||||
|
||||
if (avcodec_send_packet(stream->avContext, stream->avInputPkt) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "avcodec_send_packet failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (avcodec_receive_frame(stream->avContext, stream->avOutFrame) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "avcodec_receive_frame failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
{
|
||||
srcSlice[i] = stream->avOutFrame->data[i];
|
||||
srcLineSizes[i] = stream->avOutFrame->linesize[i];
|
||||
}
|
||||
|
||||
/* get pixFormat produced by MJPEG decoder */
|
||||
pixFormat = stream->avContext->pix_fmt;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
pixFormat = ecamToAVPixFormat(inputFormat);
|
||||
|
||||
if (av_image_fill_linesizes(srcLineSizes, pixFormat, (int)size.width) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "av_image_fill_linesizes failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (av_image_fill_pointers(srcSlice, pixFormat, (int)size.height,
|
||||
WINPR_CAST_CONST_PTR_AWAY(srcData, BYTE*), srcLineSizes) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "av_image_fill_pointers failed");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* get buffers for YUV420P */
|
||||
if (h264_get_yuv_buffer(stream->h264, stream->srcLineSizes[0], size.width, size.height,
|
||||
yuv420pData, yuv420pStride) < 0)
|
||||
if (h264_get_yuv_buffer(stream->h264, srcLineSizes[0], size.width, size.height, yuv420pData,
|
||||
yuv420pStride) < 0)
|
||||
return FALSE;
|
||||
|
||||
/* convert from source format to YUV420P */
|
||||
if (av_image_fill_pointers(srcSlice, pixFormat, (int)size.height, (BYTE*)srcData,
|
||||
stream->srcLineSizes) < 0)
|
||||
if (!ecam_init_sws_context(stream, pixFormat))
|
||||
return FALSE;
|
||||
|
||||
const BYTE* cSrcSlice[4] = { srcSlice[0], srcSlice[1], srcSlice[2], srcSlice[3] };
|
||||
if (sws_scale(stream->sws, cSrcSlice, stream->srcLineSizes, 0, (int)size.height, yuv420pData,
|
||||
if (sws_scale(stream->sws, cSrcSlice, srcLineSizes, 0, (int)size.height, yuv420pData,
|
||||
(int*)yuv420pStride) <= 0)
|
||||
return FALSE;
|
||||
|
||||
@ -135,12 +235,28 @@ static BOOL ecam_encoder_compress_h264(CameraDeviceStream* stream, const BYTE* s
|
||||
static void ecam_encoder_context_free_h264(CameraDeviceStream* stream)
|
||||
{
|
||||
WINPR_ASSERT(stream);
|
||||
|
||||
if (stream->sws)
|
||||
{
|
||||
sws_freeContext(stream->sws);
|
||||
stream->sws = NULL;
|
||||
}
|
||||
|
||||
#if defined(WITH_INPUT_FORMAT_MJPG)
|
||||
if (stream->avOutFrame)
|
||||
av_frame_free(&stream->avOutFrame); /* sets to NULL */
|
||||
|
||||
if (stream->avInputPkt)
|
||||
{
|
||||
stream->avInputPkt->data = NULL;
|
||||
stream->avInputPkt->size = 0;
|
||||
av_packet_free(&stream->avInputPkt); /* sets to NULL */
|
||||
}
|
||||
|
||||
if (stream->avContext)
|
||||
avcodec_free_context(&stream->avContext); /* sets to NULL */
|
||||
#endif
|
||||
|
||||
if (stream->h264)
|
||||
{
|
||||
h264_context_free(stream->h264);
|
||||
@ -148,6 +264,61 @@ static void ecam_encoder_context_free_h264(CameraDeviceStream* stream)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(WITH_INPUT_FORMAT_MJPG)
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return success/failure
|
||||
*/
|
||||
static BOOL ecam_init_mjpeg_decoder(CameraDeviceStream* stream)
|
||||
{
|
||||
WINPR_ASSERT(stream);
|
||||
|
||||
const AVCodec* avcodec = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
|
||||
if (!avcodec)
|
||||
{
|
||||
WLog_ERR(TAG, "avcodec_find_decoder failed to find MJPEG codec");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
stream->avContext = avcodec_alloc_context3(avcodec);
|
||||
if (!stream->avContext)
|
||||
{
|
||||
WLog_ERR(TAG, "avcodec_alloc_context3 failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
stream->avContext->width = stream->currMediaType.Width;
|
||||
stream->avContext->height = stream->currMediaType.Height;
|
||||
|
||||
/* AV_EF_EXPLODE flag is to abort decoding on minor error detection,
|
||||
* return error, so we can skip corrupted frames, if any */
|
||||
stream->avContext->err_recognition |= AV_EF_EXPLODE;
|
||||
|
||||
if (avcodec_open2(stream->avContext, avcodec, NULL) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "avcodec_open2 failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
stream->avInputPkt = av_packet_alloc();
|
||||
if (!stream->avInputPkt)
|
||||
{
|
||||
WLog_ERR(TAG, "av_packet_alloc failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
stream->avOutFrame = av_frame_alloc();
|
||||
if (!stream->avOutFrame)
|
||||
{
|
||||
WLog_ERR(TAG, "av_frame_alloc failed");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -156,6 +327,7 @@ static void ecam_encoder_context_free_h264(CameraDeviceStream* stream)
|
||||
static BOOL ecam_encoder_context_init_h264(CameraDeviceStream* stream)
|
||||
{
|
||||
WINPR_ASSERT(stream);
|
||||
|
||||
if (!stream->h264)
|
||||
stream->h264 = h264_context_new(TRUE);
|
||||
|
||||
@ -189,30 +361,10 @@ static BOOL ecam_encoder_context_init_h264(CameraDeviceStream* stream)
|
||||
if (!h264_context_set_option(stream->h264, H264_CONTEXT_OPTION_QP, 0))
|
||||
goto fail;
|
||||
|
||||
/* initialize libswscale */
|
||||
{
|
||||
const int width = (int)stream->currMediaType.Width;
|
||||
const int height = (int)stream->currMediaType.Height;
|
||||
CAM_MEDIA_FORMAT inputFormat = streamInputFormat(stream);
|
||||
enum AVPixelFormat pixFormat = ecamToAVPixFormat(inputFormat);
|
||||
|
||||
if (av_image_fill_linesizes(stream->srcLineSizes, pixFormat, width) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "av_image_fill_linesizes failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!stream->sws)
|
||||
{
|
||||
stream->sws = sws_getContext(width, height, pixFormat, width, height,
|
||||
AV_PIX_FMT_YUV420P, 0, NULL, NULL, NULL);
|
||||
}
|
||||
if (!stream->sws)
|
||||
{
|
||||
WLog_ERR(TAG, "sws_getContext failed");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
#if defined(WITH_INPUT_FORMAT_MJPG)
|
||||
if (streamInputFormat(stream) == CAM_MEDIA_FORMAT_MJPG && !ecam_init_mjpeg_decoder(stream))
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "camera.h"
|
||||
#include <winpr/handle.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("rdpecam-v4l.client")
|
||||
|
||||
@ -77,15 +78,17 @@ static UINT cam_v4l_stream_stop(CamV4lStream* stream);
|
||||
*
|
||||
* @return NULL-terminated fourcc string
|
||||
*/
|
||||
static const char* cam_v4l_get_fourcc_str(unsigned int fourcc)
|
||||
static const char* cam_v4l_get_fourcc_str(unsigned int fourcc, char* buffer, size_t size)
|
||||
{
|
||||
static char buf[5] = { 0 };
|
||||
buf[0] = (fourcc & 0xFF);
|
||||
buf[1] = (fourcc >> 8) & 0xFF;
|
||||
buf[2] = (fourcc >> 16) & 0xFF;
|
||||
buf[3] = (fourcc >> 24) & 0xFF;
|
||||
buf[4] = 0;
|
||||
return buf;
|
||||
if (size < 5)
|
||||
return NULL;
|
||||
|
||||
buffer[0] = (char)(fourcc & 0xFF);
|
||||
buffer[1] = (char)((fourcc >> 8) & 0xFF);
|
||||
buffer[2] = (char)((fourcc >> 16) & 0xFF);
|
||||
buffer[3] = (char)((fourcc >> 24) & 0xFF);
|
||||
buffer[4] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,10 +145,9 @@ static BOOL cam_v4l_format_supported(int fd, UINT32 format)
|
||||
*/
|
||||
static int cam_v4l_open_device(const char* deviceId, int flags)
|
||||
{
|
||||
UINT n;
|
||||
char device[20];
|
||||
char device[20] = { 0 };
|
||||
int fd = -1;
|
||||
struct v4l2_capability cap;
|
||||
struct v4l2_capability cap = { 0 };
|
||||
|
||||
if (!deviceId)
|
||||
return -1;
|
||||
@ -153,9 +155,9 @@ static int cam_v4l_open_device(const char* deviceId, int flags)
|
||||
if (0 == strncmp(deviceId, "/dev/video", 10))
|
||||
return open(deviceId, flags);
|
||||
|
||||
for (n = 0; n < 64; n++)
|
||||
for (UINT n = 0; n < 64; n++)
|
||||
{
|
||||
snprintf(device, sizeof(device), "/dev/video%d", n);
|
||||
(void)_snprintf(device, sizeof(device), "/dev/video%" PRIu32, n);
|
||||
if ((fd = open(device, flags)) == -1)
|
||||
continue;
|
||||
|
||||
@ -188,19 +190,19 @@ static INT16 cam_v4l_get_media_type_descriptions(ICamHal* hal, const char* devic
|
||||
CAM_MEDIA_TYPE_DESCRIPTION* mediaTypes,
|
||||
size_t* nMediaTypes)
|
||||
{
|
||||
int fd;
|
||||
size_t maxMediaTypes = *nMediaTypes;
|
||||
size_t nTypes = 0;
|
||||
int formatIndex;
|
||||
BOOL formatFound = FALSE;
|
||||
|
||||
if ((fd = cam_v4l_open_device(deviceId, O_RDONLY)) == -1)
|
||||
int fd = cam_v4l_open_device(deviceId, O_RDONLY);
|
||||
if (fd == -1)
|
||||
{
|
||||
WLog_ERR(TAG, "Unable to open device %s", deviceId);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (formatIndex = 0; formatIndex < nSupportedFormats; formatIndex++)
|
||||
size_t formatIndex = 0;
|
||||
for (; formatIndex < nSupportedFormats; formatIndex++)
|
||||
{
|
||||
UINT32 pixelFormat = ecamToV4L2PixFormat(supportedFormats[formatIndex].inputFormat);
|
||||
WINPR_ASSERT(pixelFormat != 0);
|
||||
@ -243,9 +245,11 @@ static INT16 cam_v4l_get_media_type_descriptions(ICamHal* hal, const char* devic
|
||||
|
||||
mediaTypes->PixelAspectRatioNumerator = mediaTypes->PixelAspectRatioDenominator = 1;
|
||||
|
||||
char fourccstr[5] = { 0 };
|
||||
WLog_DBG(TAG, "Camera format: %s, width: %u, height: %u, fps: %u/%u",
|
||||
cam_v4l_get_fourcc_str(pixelFormat), mediaTypes->Width, mediaTypes->Height,
|
||||
mediaTypes->FrameRateNumerator, mediaTypes->FrameRateDenominator);
|
||||
cam_v4l_get_fourcc_str(pixelFormat, fourccstr, ARRAYSIZE(fourccstr)),
|
||||
mediaTypes->Width, mediaTypes->Height, mediaTypes->FrameRateNumerator,
|
||||
mediaTypes->FrameRateDenominator);
|
||||
|
||||
mediaTypes++;
|
||||
nTypes++;
|
||||
@ -268,7 +272,9 @@ error:
|
||||
|
||||
*nMediaTypes = nTypes;
|
||||
close(fd);
|
||||
return formatIndex;
|
||||
if (formatIndex > INT16_MAX)
|
||||
return -1;
|
||||
return (INT16)formatIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -279,16 +285,15 @@ error:
|
||||
static UINT cam_v4l_enumerate(ICamHal* ihal, ICamHalEnumCallback callback, CameraPlugin* ecam,
|
||||
GENERIC_CHANNEL_CALLBACK* hchannel)
|
||||
{
|
||||
UINT n, count = 0;
|
||||
char device[20];
|
||||
int fd = -1;
|
||||
struct v4l2_capability cap;
|
||||
char *deviceName, *deviceId;
|
||||
UINT count = 0;
|
||||
|
||||
for (n = 0; n < 64; n++)
|
||||
for (UINT n = 0; n < 64; n++)
|
||||
{
|
||||
snprintf(device, sizeof(device), "/dev/video%d", n);
|
||||
if ((fd = open(device, O_RDONLY)) == -1)
|
||||
char device[20] = { 0 };
|
||||
struct v4l2_capability cap = { 0 };
|
||||
(void)_snprintf(device, sizeof(device), "/dev/video%" PRIu32, n);
|
||||
int fd = open(device, O_RDONLY);
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
/* query device capabilities and make sure this is a video capture device */
|
||||
@ -299,12 +304,10 @@ static UINT cam_v4l_enumerate(ICamHal* ihal, ICamHalEnumCallback callback, Camer
|
||||
}
|
||||
count++;
|
||||
|
||||
deviceName = (char*)cap.card;
|
||||
|
||||
const char* deviceName = (char*)cap.card;
|
||||
const char* deviceId = device;
|
||||
if (cap.bus_info[0] != 0) /* may not be available in all drivers */
|
||||
deviceId = (char*)cap.bus_info;
|
||||
else
|
||||
deviceId = device;
|
||||
|
||||
IFCALL(callback, ecam, hchannel, deviceId, deviceName);
|
||||
|
||||
@ -314,18 +317,13 @@ static UINT cam_v4l_enumerate(ICamHal* ihal, ICamHalEnumCallback callback, Camer
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
static void cam_v4l_stream_free_buffers(CamV4lStream* stream)
|
||||
{
|
||||
if (!stream || !stream->buffers)
|
||||
return;
|
||||
|
||||
/* unmap buffers */
|
||||
for (int i = 0; i < stream->nBuffers; i++)
|
||||
for (size_t i = 0; i < stream->nBuffers; i++)
|
||||
{
|
||||
if (stream->buffers[i].length && stream->buffers[i].start != MAP_FAILED)
|
||||
{
|
||||
@ -336,7 +334,6 @@ static void cam_v4l_stream_free_buffers(CamV4lStream* stream)
|
||||
free(stream->buffers);
|
||||
stream->buffers = NULL;
|
||||
stream->nBuffers = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -420,7 +417,7 @@ static UINT cam_v4l_stream_capture_thread(void* param)
|
||||
|
||||
do
|
||||
{
|
||||
int retVal;
|
||||
int retVal = 0;
|
||||
struct pollfd pfd = { 0 };
|
||||
|
||||
pfd.fd = fd;
|
||||
@ -473,11 +470,6 @@ static UINT cam_v4l_stream_capture_thread(void* param)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void cam_v4l_stream_close_device(CamV4lStream* stream)
|
||||
{
|
||||
if (stream->fd != -1)
|
||||
@ -532,16 +524,15 @@ UINT cam_v4l_stream_stop(CamV4lStream* stream)
|
||||
|
||||
if (stream->captureThread)
|
||||
{
|
||||
WaitForSingleObject(stream->captureThread, INFINITE);
|
||||
CloseHandle(stream->captureThread);
|
||||
(void)WaitForSingleObject(stream->captureThread, INFINITE);
|
||||
(void)CloseHandle(stream->captureThread);
|
||||
stream->captureThread = NULL;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&stream->lock);
|
||||
|
||||
/* stop streaming */
|
||||
enum v4l2_buf_type type;
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (ioctl(stream->fd, VIDIOC_STREAMOFF, &type) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failure in VIDIOC_STREAMOFF, errno %d", errno);
|
||||
@ -616,7 +607,8 @@ static UINT cam_v4l_stream_start(ICamHal* ihal, CameraDevice* dev, int streamInd
|
||||
}
|
||||
|
||||
/* trying to set frame rate, if driver supports it */
|
||||
struct v4l2_streamparm sp1 = { 0 }, sp2 = { 0 };
|
||||
struct v4l2_streamparm sp1 = { 0 };
|
||||
struct v4l2_streamparm sp2 = { 0 };
|
||||
sp1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (ioctl(stream->fd, VIDIOC_G_PARM, &sp1) < 0 ||
|
||||
!(sp1.parm.capture.capability & V4L2_CAP_TIMEPERFRAME))
|
||||
@ -648,8 +640,7 @@ static UINT cam_v4l_stream_start(ICamHal* ihal, CameraDevice* dev, int streamInd
|
||||
stream->streaming = TRUE;
|
||||
|
||||
/* start streaming */
|
||||
enum v4l2_buf_type type;
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (ioctl(stream->fd, VIDIOC_STREAMON, &type) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "Failure in VIDIOC_STREAMON, errno %d", errno);
|
||||
@ -665,9 +656,11 @@ static UINT cam_v4l_stream_start(ICamHal* ihal, CameraDevice* dev, int streamInd
|
||||
return CAM_ERROR_CODE_OutOfMemory;
|
||||
}
|
||||
|
||||
char fourccstr[5] = { 0 };
|
||||
WLog_INFO(TAG, "Camera format: %s, width: %u, height: %u, fps: %u/%u",
|
||||
cam_v4l_get_fourcc_str(pixelFormat), mediaType->Width, mediaType->Height,
|
||||
mediaType->FrameRateNumerator, mediaType->FrameRateDenominator);
|
||||
cam_v4l_get_fourcc_str(pixelFormat, fourccstr, ARRAYSIZE(fourccstr)),
|
||||
mediaType->Width, mediaType->Height, mediaType->FrameRateNumerator,
|
||||
mediaType->FrameRateDenominator);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -694,7 +687,6 @@ static UINT cam_v4l_stream_stop_by_device_id(ICamHal* ihal, const char* deviceId
|
||||
*
|
||||
* OBJECT_FREE_FN for streams hash table value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void cam_v4l_stream_free(void* obj)
|
||||
{
|
||||
@ -706,8 +698,6 @@ void cam_v4l_stream_free(void* obj)
|
||||
|
||||
DeleteCriticalSection(&stream->lock);
|
||||
free(stream);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -734,8 +724,8 @@ static UINT cam_v4l_free(ICamHal* ihal)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(
|
||||
UINT v4l_freerdp_rdpecam_client_subsystem_entry(PFREERDP_CAMERA_HAL_ENTRY_POINTS pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE v4l_freerdp_rdpecam_client_subsystem_entry(
|
||||
PFREERDP_CAMERA_HAL_ENTRY_POINTS pEntryPoints))
|
||||
{
|
||||
UINT ret = CHANNEL_RC_OK;
|
||||
WINPR_ASSERT(pEntryPoints);
|
||||
|
@ -287,7 +287,7 @@ static UINT enumerator_process_message(enumerator_server* enumerator)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(enumerator->enumerator_channel, 0, (PCHAR)Stream_Buffer(s),
|
||||
if (WTSVirtualChannelRead(enumerator->enumerator_channel, 0, Stream_BufferAs(s, char),
|
||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
@ -348,6 +348,8 @@ static UINT enumerator_server_context_poll_int(CamDevEnumServerContext* context)
|
||||
case ENUMERATOR_OPENED:
|
||||
error = enumerator_process_message(enumerator);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
@ -415,10 +417,12 @@ static DWORD WINAPI enumerator_server_thread_func(LPVOID arg)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WTSVirtualChannelClose(enumerator->enumerator_channel);
|
||||
(void)WTSVirtualChannelClose(enumerator->enumerator_channel);
|
||||
enumerator->enumerator_channel = NULL;
|
||||
|
||||
if (error && enumerator->context.rdpcontext)
|
||||
@ -449,7 +453,7 @@ static UINT enumerator_server_open(CamDevEnumServerContext* context)
|
||||
if (!enumerator->thread)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(enumerator->stopEvent);
|
||||
(void)CloseHandle(enumerator->stopEvent);
|
||||
enumerator->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -468,7 +472,7 @@ static UINT enumerator_server_close(CamDevEnumServerContext* context)
|
||||
|
||||
if (!enumerator->externalThread && enumerator->thread)
|
||||
{
|
||||
SetEvent(enumerator->stopEvent);
|
||||
(void)SetEvent(enumerator->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(enumerator->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -477,8 +481,8 @@ static UINT enumerator_server_close(CamDevEnumServerContext* context)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(enumerator->thread);
|
||||
CloseHandle(enumerator->stopEvent);
|
||||
(void)CloseHandle(enumerator->thread);
|
||||
(void)CloseHandle(enumerator->stopEvent);
|
||||
enumerator->thread = NULL;
|
||||
enumerator->stopEvent = NULL;
|
||||
}
|
||||
@ -486,7 +490,7 @@ static UINT enumerator_server_close(CamDevEnumServerContext* context)
|
||||
{
|
||||
if (enumerator->state != ENUMERATOR_INITIAL)
|
||||
{
|
||||
WTSVirtualChannelClose(enumerator->enumerator_channel);
|
||||
(void)WTSVirtualChannelClose(enumerator->enumerator_channel);
|
||||
enumerator->enumerator_channel = NULL;
|
||||
enumerator->state = ENUMERATOR_INITIAL;
|
||||
}
|
||||
@ -531,8 +535,10 @@ static UINT enumerator_server_packet_send(CamDevEnumServerContext* context, wStr
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
ULONG written = 0;
|
||||
|
||||
if (!WTSVirtualChannelWrite(enumerator->enumerator_channel, (PCHAR)Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written))
|
||||
const size_t len = Stream_GetPosition(s);
|
||||
WINPR_ASSERT(len <= UINT32_MAX);
|
||||
if (!WTSVirtualChannelWrite(enumerator->enumerator_channel, Stream_BufferAs(s, char),
|
||||
(UINT32)len, &written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
|
@ -426,7 +426,7 @@ static UINT device_process_message(device_server* device)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WTSVirtualChannelRead(device->device_channel, 0, (PCHAR)Stream_Buffer(s),
|
||||
if (WTSVirtualChannelRead(device->device_channel, 0, Stream_BufferAs(s, char),
|
||||
(ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
|
||||
@ -501,6 +501,8 @@ static UINT device_server_context_poll_int(CameraDeviceServerContext* context)
|
||||
case CAMERA_DEVICE_OPENED:
|
||||
error = device_process_message(device);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
@ -568,10 +570,12 @@ static DWORD WINAPI device_server_thread_func(LPVOID arg)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WTSVirtualChannelClose(device->device_channel);
|
||||
(void)WTSVirtualChannelClose(device->device_channel);
|
||||
device->device_channel = NULL;
|
||||
|
||||
if (error && device->context.rdpcontext)
|
||||
@ -601,7 +605,7 @@ static UINT device_server_open(CameraDeviceServerContext* context)
|
||||
if (!device->thread)
|
||||
{
|
||||
WLog_ERR(TAG, "CreateThread failed!");
|
||||
CloseHandle(device->stopEvent);
|
||||
(void)CloseHandle(device->stopEvent);
|
||||
device->stopEvent = NULL;
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@ -620,7 +624,7 @@ static UINT device_server_close(CameraDeviceServerContext* context)
|
||||
|
||||
if (!device->externalThread && device->thread)
|
||||
{
|
||||
SetEvent(device->stopEvent);
|
||||
(void)SetEvent(device->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(device->thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
@ -629,8 +633,8 @@ static UINT device_server_close(CameraDeviceServerContext* context)
|
||||
return error;
|
||||
}
|
||||
|
||||
CloseHandle(device->thread);
|
||||
CloseHandle(device->stopEvent);
|
||||
(void)CloseHandle(device->thread);
|
||||
(void)CloseHandle(device->stopEvent);
|
||||
device->thread = NULL;
|
||||
device->stopEvent = NULL;
|
||||
}
|
||||
@ -638,7 +642,7 @@ static UINT device_server_close(CameraDeviceServerContext* context)
|
||||
{
|
||||
if (device->state != CAMERA_DEVICE_INITIAL)
|
||||
{
|
||||
WTSVirtualChannelClose(device->device_channel);
|
||||
(void)WTSVirtualChannelClose(device->device_channel);
|
||||
device->device_channel = NULL;
|
||||
device->state = CAMERA_DEVICE_INITIAL;
|
||||
}
|
||||
@ -704,8 +708,10 @@ static UINT device_server_packet_send(CameraDeviceServerContext* context, wStrea
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
if (!WTSVirtualChannelWrite(device->device_channel, (PCHAR)Stream_Buffer(s),
|
||||
Stream_GetPosition(s), &written))
|
||||
const size_t len = Stream_GetPosition(s);
|
||||
WINPR_ASSERT(len <= UINT32_MAX);
|
||||
if (!WTSVirtualChannelWrite(device->device_channel, Stream_BufferAs(s, char), (UINT32)len,
|
||||
&written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
|
@ -30,3 +30,4 @@ set(${MODULE_PREFIX}_LIBS
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
@ -41,6 +42,8 @@
|
||||
|
||||
#include "rdpei_main.h"
|
||||
|
||||
#define RDPEI_TAG CHANNELS_TAG("rdpei.client")
|
||||
|
||||
/**
|
||||
* Touch Input
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/dd562197/
|
||||
@ -84,9 +87,13 @@ typedef struct
|
||||
|
||||
CRITICAL_SECTION lock;
|
||||
rdpContext* rdpcontext;
|
||||
|
||||
HANDLE thread;
|
||||
|
||||
HANDLE event;
|
||||
UINT64 lastPollEventTime;
|
||||
BOOL running;
|
||||
BOOL async;
|
||||
} RDPEI_PLUGIN;
|
||||
|
||||
/**
|
||||
@ -197,7 +204,8 @@ static UINT rdpei_add_frame(RdpeiClientContext* context)
|
||||
UINT error = rdpei_send_frame(context, &frame);
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_send_frame failed with error %" PRIu32 "!", error);
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR,
|
||||
"rdpei_send_frame failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@ -210,23 +218,30 @@ static UINT rdpei_add_frame(RdpeiClientContext* context)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpei_send_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s, UINT16 eventId,
|
||||
UINT32 pduLength)
|
||||
size_t pduLength)
|
||||
{
|
||||
UINT status = 0;
|
||||
|
||||
if (!callback || !s || !callback->channel || !callback->channel->Write || !callback->plugin)
|
||||
if (!callback || !s || !callback->channel || !callback->channel->Write)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (pduLength > UINT32_MAX)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
|
||||
if (!rdpei)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
Stream_Write_UINT16(s, eventId); /* eventId (2 bytes) */
|
||||
Stream_Write_UINT32(s, pduLength); /* pduLength (4 bytes) */
|
||||
Stream_Write_UINT32(s, (UINT32)pduLength); /* pduLength (4 bytes) */
|
||||
Stream_SetPosition(s, Stream_Length(s));
|
||||
status = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
|
||||
NULL);
|
||||
#ifdef WITH_DEBUG_RDPEI
|
||||
WLog_DBG(TAG,
|
||||
"rdpei_send_pdu: eventId: %" PRIu16 " (%s) length: %" PRIu32 " status: %" PRIu32 "",
|
||||
eventId, rdpei_eventid_string(eventId), pduLength, status);
|
||||
WLog_Print(rdpei->base.log, WLOG_DEBUG,
|
||||
"rdpei_send_pdu: eventId: %" PRIu16 " (%s) length: %" PRIu32 " status: %" PRIu32 "",
|
||||
eventId, rdpei_eventid_string(eventId), pduLength, status);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
@ -284,12 +299,23 @@ static UINT rdpei_write_pen_frame(wStream* s, const RDPINPUT_PEN_FRAME* frame)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT rdpei_send_pen_event_pdu(GENERIC_CHANNEL_CALLBACK* callback, UINT32 frameOffset,
|
||||
const RDPINPUT_PEN_FRAME* frames, UINT16 count)
|
||||
static UINT rdpei_send_pen_event_pdu(GENERIC_CHANNEL_CALLBACK* callback, size_t frameOffset,
|
||||
const RDPINPUT_PEN_FRAME* frames, size_t count)
|
||||
{
|
||||
UINT status = 0;
|
||||
wStream* s = NULL;
|
||||
|
||||
WINPR_ASSERT(callback);
|
||||
|
||||
if (frameOffset > UINT32_MAX)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
if (count > UINT16_MAX)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
|
||||
if (!rdpei)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (!frames || (count == 0))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
@ -297,7 +323,7 @@ static UINT rdpei_send_pen_event_pdu(GENERIC_CHANNEL_CALLBACK* callback, UINT32
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
@ -306,14 +332,16 @@ static UINT rdpei_send_pen_event_pdu(GENERIC_CHANNEL_CALLBACK* callback, UINT32
|
||||
* the time that has elapsed (in milliseconds) from when the oldest touch frame
|
||||
* was generated to when it was encoded for transmission by the client.
|
||||
*/
|
||||
rdpei_write_4byte_unsigned(s, frameOffset); /* encodeTime (FOUR_BYTE_UNSIGNED_INTEGER) */
|
||||
rdpei_write_2byte_unsigned(s, count); /* (frameCount) TWO_BYTE_UNSIGNED_INTEGER */
|
||||
rdpei_write_4byte_unsigned(s,
|
||||
(UINT32)frameOffset); /* encodeTime (FOUR_BYTE_UNSIGNED_INTEGER) */
|
||||
rdpei_write_2byte_unsigned(s, (UINT16)count); /* (frameCount) TWO_BYTE_UNSIGNED_INTEGER */
|
||||
|
||||
for (UINT16 x = 0; x < count; x++)
|
||||
for (size_t x = 0; x < count; x++)
|
||||
{
|
||||
if ((status = rdpei_write_pen_frame(s, &frames[x])))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_write_pen_frame failed with error %" PRIu32 "!", status);
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR,
|
||||
"rdpei_write_pen_frame failed with error %" PRIu32 "!", status);
|
||||
Stream_Free(s, TRUE);
|
||||
return status;
|
||||
}
|
||||
@ -410,18 +438,58 @@ static UINT rdpei_add_pen_frame(RdpeiClientContext* context)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static UINT rdpei_update(RdpeiClientContext* context)
|
||||
static UINT rdpei_update(wLog* log, RdpeiClientContext* context)
|
||||
{
|
||||
UINT error = rdpei_add_frame(context);
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_add_frame failed with error %" PRIu32 "!", error);
|
||||
WLog_Print(log, WLOG_ERROR, "rdpei_add_frame failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return rdpei_add_pen_frame(context);
|
||||
}
|
||||
|
||||
static BOOL rdpei_poll_run_unlocked(rdpContext* context, void* userdata)
|
||||
{
|
||||
RDPEI_PLUGIN* rdpei = userdata;
|
||||
WINPR_ASSERT(rdpei);
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
const UINT64 now = GetTickCount64();
|
||||
|
||||
/* Send an event every ~20ms */
|
||||
if ((now < rdpei->lastPollEventTime) || (now - rdpei->lastPollEventTime < 20ULL))
|
||||
return TRUE;
|
||||
|
||||
rdpei->lastPollEventTime = now;
|
||||
|
||||
const UINT error = rdpei_update(rdpei->base.log, rdpei->context);
|
||||
|
||||
(void)ResetEvent(rdpei->event);
|
||||
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR, "rdpei_add_frame failed with error %" PRIu32 "!",
|
||||
error);
|
||||
setChannelError(context, error, "rdpei_add_frame reported an error");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL rdpei_poll_run(rdpContext* context, void* userdata)
|
||||
{
|
||||
RDPEI_PLUGIN* rdpei = userdata;
|
||||
WINPR_ASSERT(rdpei);
|
||||
|
||||
EnterCriticalSection(&rdpei->lock);
|
||||
BOOL rc = rdpei_poll_run_unlocked(context, userdata);
|
||||
LeaveCriticalSection(&rdpei->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static DWORD WINAPI rdpei_periodic_update(LPVOID arg)
|
||||
{
|
||||
DWORD status = 0;
|
||||
@ -450,23 +518,12 @@ static DWORD WINAPI rdpei_periodic_update(LPVOID arg)
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR,
|
||||
"WaitForMultipleObjects failed with error %" PRIu32 "!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&rdpei->lock);
|
||||
|
||||
error = rdpei_update(context);
|
||||
if (error != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_add_frame failed with error %" PRIu32 "!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
ResetEvent(rdpei->event);
|
||||
|
||||
LeaveCriticalSection(&rdpei->lock);
|
||||
error = rdpei_poll_run(rdpei->rdpcontext, rdpei);
|
||||
}
|
||||
|
||||
out:
|
||||
@ -509,7 +566,7 @@ static UINT rdpei_send_cs_ready_pdu(GENERIC_CHANNEL_CALLBACK* callback)
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
@ -523,25 +580,36 @@ static UINT rdpei_send_cs_ready_pdu(GENERIC_CHANNEL_CALLBACK* callback)
|
||||
return status;
|
||||
}
|
||||
|
||||
static void rdpei_print_contact_flags(UINT32 contactFlags)
|
||||
#if defined(WITH_DEBUG_RDPEI)
|
||||
static void rdpei_print_contact_flags(wLog* log, UINT32 contactFlags)
|
||||
{
|
||||
if (contactFlags & RDPINPUT_CONTACT_FLAG_DOWN)
|
||||
WLog_DBG(TAG, " RDPINPUT_CONTACT_FLAG_DOWN");
|
||||
WLog_Print(log, WLOG_DEBUG, " RDPINPUT_CONTACT_FLAG_DOWN");
|
||||
|
||||
if (contactFlags & RDPINPUT_CONTACT_FLAG_UPDATE)
|
||||
WLog_DBG(TAG, " RDPINPUT_CONTACT_FLAG_UPDATE");
|
||||
WLog_Print(log, WLOG_DEBUG, " RDPINPUT_CONTACT_FLAG_UPDATE");
|
||||
|
||||
if (contactFlags & RDPINPUT_CONTACT_FLAG_UP)
|
||||
WLog_DBG(TAG, " RDPINPUT_CONTACT_FLAG_UP");
|
||||
WLog_Print(log, WLOG_DEBUG, " RDPINPUT_CONTACT_FLAG_UP");
|
||||
|
||||
if (contactFlags & RDPINPUT_CONTACT_FLAG_INRANGE)
|
||||
WLog_DBG(TAG, " RDPINPUT_CONTACT_FLAG_INRANGE");
|
||||
WLog_Print(log, WLOG_DEBUG, " RDPINPUT_CONTACT_FLAG_INRANGE");
|
||||
|
||||
if (contactFlags & RDPINPUT_CONTACT_FLAG_INCONTACT)
|
||||
WLog_DBG(TAG, " RDPINPUT_CONTACT_FLAG_INCONTACT");
|
||||
WLog_Print(log, WLOG_DEBUG, " RDPINPUT_CONTACT_FLAG_INCONTACT");
|
||||
|
||||
if (contactFlags & RDPINPUT_CONTACT_FLAG_CANCELED)
|
||||
WLog_DBG(TAG, " RDPINPUT_CONTACT_FLAG_CANCELED");
|
||||
WLog_Print(log, WLOG_DEBUG, " RDPINPUT_CONTACT_FLAG_CANCELED");
|
||||
}
|
||||
#endif
|
||||
|
||||
static INT16 bounded(INT32 val)
|
||||
{
|
||||
if (val < INT16_MIN)
|
||||
return INT16_MIN;
|
||||
if (val > INT16_MAX)
|
||||
return INT16_MAX;
|
||||
return (INT16)val;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -549,15 +617,15 @@ static void rdpei_print_contact_flags(UINT32 contactFlags)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpei_write_touch_frame(wStream* s, RDPINPUT_TOUCH_FRAME* frame)
|
||||
static UINT rdpei_write_touch_frame(wLog* log, wStream* s, RDPINPUT_TOUCH_FRAME* frame)
|
||||
{
|
||||
int rectSize = 2;
|
||||
RDPINPUT_CONTACT_DATA* contact = NULL;
|
||||
if (!s || !frame)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
#ifdef WITH_DEBUG_RDPEI
|
||||
WLog_DBG(TAG, "contactCount: %" PRIu32 "", frame->contactCount);
|
||||
WLog_DBG(TAG, "frameOffset: 0x%016" PRIX64 "", frame->frameOffset);
|
||||
WLog_Print(log, WLOG_DEBUG, "contactCount: %" PRIu32 "", frame->contactCount);
|
||||
WLog_Print(log, WLOG_DEBUG, "frameOffset: 0x%016" PRIX64 "", frame->frameOffset);
|
||||
#endif
|
||||
rdpei_write_2byte_unsigned(s,
|
||||
frame->contactCount); /* contactCount (TWO_BYTE_UNSIGNED_INTEGER) */
|
||||
@ -570,7 +638,7 @@ static UINT rdpei_write_touch_frame(wStream* s, RDPINPUT_TOUCH_FRAME* frame)
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, (size_t)frame->contactCount * 64))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
WLog_Print(log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
@ -578,19 +646,20 @@ static UINT rdpei_write_touch_frame(wStream* s, RDPINPUT_TOUCH_FRAME* frame)
|
||||
{
|
||||
contact = &frame->contacts[index];
|
||||
contact->fieldsPresent |= CONTACT_DATA_CONTACTRECT_PRESENT;
|
||||
contact->contactRectLeft = contact->x - rectSize;
|
||||
contact->contactRectTop = contact->y - rectSize;
|
||||
contact->contactRectRight = contact->x + rectSize;
|
||||
contact->contactRectBottom = contact->y + rectSize;
|
||||
contact->contactRectLeft = bounded(contact->x - rectSize);
|
||||
contact->contactRectTop = bounded(contact->y - rectSize);
|
||||
contact->contactRectRight = bounded(contact->x + rectSize);
|
||||
contact->contactRectBottom = bounded(contact->y + rectSize);
|
||||
#ifdef WITH_DEBUG_RDPEI
|
||||
WLog_DBG(TAG, "contact[%" PRIu32 "].contactId: %" PRIu32 "", index, contact->contactId);
|
||||
WLog_DBG(TAG, "contact[%" PRIu32 "].fieldsPresent: %" PRIu32 "", index,
|
||||
contact->fieldsPresent);
|
||||
WLog_DBG(TAG, "contact[%" PRIu32 "].x: %" PRId32 "", index, contact->x);
|
||||
WLog_DBG(TAG, "contact[%" PRIu32 "].y: %" PRId32 "", index, contact->y);
|
||||
WLog_DBG(TAG, "contact[%" PRIu32 "].contactFlags: 0x%08" PRIX32 "", index,
|
||||
contact->contactFlags);
|
||||
rdpei_print_contact_flags(contact->contactFlags);
|
||||
WLog_Print(log, WLOG_DEBUG, "contact[%" PRIu32 "].contactId: %" PRIu32 "", index,
|
||||
contact->contactId);
|
||||
WLog_Print(log, WLOG_DEBUG, "contact[%" PRIu32 "].fieldsPresent: %" PRIu32 "", index,
|
||||
contact->fieldsPresent);
|
||||
WLog_Print(log, WLOG_DEBUG, "contact[%" PRIu32 "].x: %" PRId32 "", index, contact->x);
|
||||
WLog_Print(log, WLOG_DEBUG, "contact[%" PRIu32 "].y: %" PRId32 "", index, contact->y);
|
||||
WLog_Print(log, WLOG_DEBUG, "contact[%" PRIu32 "].contactFlags: 0x%08" PRIX32 "", index,
|
||||
contact->contactFlags);
|
||||
rdpei_print_contact_flags(log, contact->contactFlags);
|
||||
#endif
|
||||
Stream_Write_UINT8(s, contact->contactId); /* contactId (1 byte) */
|
||||
/* fieldsPresent (TWO_BYTE_UNSIGNED_INTEGER) */
|
||||
@ -637,13 +706,10 @@ static UINT rdpei_send_touch_event_pdu(GENERIC_CHANNEL_CALLBACK* callback,
|
||||
RDPINPUT_TOUCH_FRAME* frame)
|
||||
{
|
||||
UINT status = 0;
|
||||
wStream* s = NULL;
|
||||
UINT32 pduLength = 0;
|
||||
RDPEI_PLUGIN* rdpei = NULL;
|
||||
|
||||
WINPR_ASSERT(callback);
|
||||
|
||||
rdpei = (RDPEI_PLUGIN*)callback->plugin;
|
||||
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
|
||||
if (!rdpei || !rdpei->rdpcontext)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
if (freerdp_settings_get_bool(rdpei->rdpcontext->settings, FreeRDP_SuspendInput))
|
||||
@ -652,12 +718,12 @@ static UINT rdpei_send_touch_event_pdu(GENERIC_CHANNEL_CALLBACK* callback,
|
||||
if (!frame)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
pduLength = 64 + (frame->contactCount * 64);
|
||||
s = Stream_New(NULL, pduLength);
|
||||
size_t pduLength = 64ULL + (64ULL * frame->contactCount);
|
||||
wStream* s = Stream_New(NULL, pduLength);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR, "Stream_New failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
@ -670,16 +736,17 @@ static UINT rdpei_send_touch_event_pdu(GENERIC_CHANNEL_CALLBACK* callback,
|
||||
s, (UINT32)frame->frameOffset); /* encodeTime (FOUR_BYTE_UNSIGNED_INTEGER) */
|
||||
rdpei_write_2byte_unsigned(s, 1); /* (frameCount) TWO_BYTE_UNSIGNED_INTEGER */
|
||||
|
||||
if ((status = rdpei_write_touch_frame(s, frame)))
|
||||
status = rdpei_write_touch_frame(rdpei->base.log, s, frame);
|
||||
if (status)
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_write_touch_frame failed with error %" PRIu32 "!", status);
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR,
|
||||
"rdpei_write_touch_frame failed with error %" PRIu32 "!", status);
|
||||
Stream_Free(s, TRUE);
|
||||
return status;
|
||||
}
|
||||
|
||||
Stream_SealLength(s);
|
||||
pduLength = Stream_Length(s);
|
||||
status = rdpei_send_pdu(callback, s, EVENTID_TOUCH, pduLength);
|
||||
status = rdpei_send_pdu(callback, s, EVENTID_TOUCH, Stream_Length(s));
|
||||
Stream_Free(s, TRUE);
|
||||
return status;
|
||||
}
|
||||
@ -693,20 +760,19 @@ static UINT rdpei_recv_sc_ready_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream*
|
||||
{
|
||||
UINT32 features = 0;
|
||||
UINT32 protocolVersion = 0;
|
||||
RDPEI_PLUGIN* rdpei = NULL;
|
||||
|
||||
if (!callback || !callback->plugin)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
rdpei = (RDPEI_PLUGIN*)callback->plugin;
|
||||
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpei->base.log, s, 4))
|
||||
return ERROR_INVALID_DATA;
|
||||
Stream_Read_UINT32(s, protocolVersion); /* protocolVersion (4 bytes) */
|
||||
|
||||
if (protocolVersion >= RDPINPUT_PROTOCOL_V300)
|
||||
{
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpei->base.log, s, 4))
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
@ -720,7 +786,7 @@ static UINT rdpei_recv_sc_ready_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream*
|
||||
|
||||
if (protocolVersion != RDPINPUT_PROTOCOL_V10)
|
||||
{
|
||||
WLog_ERR(TAG, "Unknown [MS-RDPEI] protocolVersion: 0x%08"PRIX32"", protocolVersion);
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR, "Unknown [MS-RDPEI] protocolVersion: 0x%08"PRIX32"", protocolVersion);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -736,20 +802,22 @@ static UINT rdpei_recv_sc_ready_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream*
|
||||
static UINT rdpei_recv_suspend_touch_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
RdpeiClientContext* rdpei = NULL;
|
||||
|
||||
WINPR_UNUSED(s);
|
||||
|
||||
if (!callback || !callback->plugin)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
rdpei = (RdpeiClientContext*)callback->plugin->pInterface;
|
||||
|
||||
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
|
||||
RdpeiClientContext* context = rdpei->context;
|
||||
if (!rdpei)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
IFCALLRET(rdpei->SuspendTouch, error, rdpei);
|
||||
IFCALLRET(context->SuspendTouch, error, context);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "rdpei->SuspendTouch failed with error %" PRIu32 "!", error);
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR,
|
||||
"rdpei->SuspendTouch failed with error %" PRIu32 "!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -761,18 +829,23 @@ static UINT rdpei_recv_suspend_touch_pdu(GENERIC_CHANNEL_CALLBACK* callback, wSt
|
||||
*/
|
||||
static UINT rdpei_recv_resume_touch_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
{
|
||||
RdpeiClientContext* rdpei = NULL;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
if (!s || !callback || !callback->plugin)
|
||||
if (!s || !callback)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
rdpei = (RdpeiClientContext*)callback->plugin->pInterface;
|
||||
|
||||
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
|
||||
if (!rdpei)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
IFCALLRET(rdpei->ResumeTouch, error, rdpei);
|
||||
RdpeiClientContext* context = (RdpeiClientContext*)callback->plugin->pInterface;
|
||||
if (!context)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
IFCALLRET(context->ResumeTouch, error, context);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "rdpei->ResumeTouch failed with error %" PRIu32 "!", error);
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR, "rdpei->ResumeTouch failed with error %" PRIu32 "!",
|
||||
error);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -788,31 +861,41 @@ static UINT rdpei_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
UINT32 pduLength = 0;
|
||||
UINT error = 0;
|
||||
|
||||
if (!s)
|
||||
if (!callback || !s)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
|
||||
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)callback->plugin;
|
||||
if (!rdpei)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLengthWLog(rdpei->base.log, s, 6))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, eventId); /* eventId (2 bytes) */
|
||||
Stream_Read_UINT32(s, pduLength); /* pduLength (4 bytes) */
|
||||
#ifdef WITH_DEBUG_RDPEI
|
||||
WLog_DBG(TAG, "rdpei_recv_pdu: eventId: %" PRIu16 " (%s) length: %" PRIu32 "", eventId,
|
||||
rdpei_eventid_string(eventId), pduLength);
|
||||
WLog_Print(rdpei->base.log, WLOG_DEBUG,
|
||||
"rdpei_recv_pdu: eventId: %" PRIu16 " (%s) length: %" PRIu32 "", eventId,
|
||||
rdpei_eventid_string(eventId), pduLength);
|
||||
#endif
|
||||
|
||||
if ((pduLength < 6) || !Stream_CheckAndLogRequiredLengthWLog(rdpei->base.log, s, pduLength - 6))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
switch (eventId)
|
||||
{
|
||||
case EVENTID_SC_READY:
|
||||
if ((error = rdpei_recv_sc_ready_pdu(callback, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_recv_sc_ready_pdu failed with error %" PRIu32 "!", error);
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR,
|
||||
"rdpei_recv_sc_ready_pdu failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = rdpei_send_cs_ready_pdu(callback)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_send_cs_ready_pdu failed with error %" PRIu32 "!", error);
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR,
|
||||
"rdpei_send_cs_ready_pdu failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -821,7 +904,8 @@ static UINT rdpei_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
case EVENTID_SUSPEND_TOUCH:
|
||||
if ((error = rdpei_recv_suspend_touch_pdu(callback, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_recv_suspend_touch_pdu failed with error %" PRIu32 "!", error);
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR,
|
||||
"rdpei_recv_suspend_touch_pdu failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -830,7 +914,8 @@ static UINT rdpei_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
|
||||
case EVENTID_RESUME_TOUCH:
|
||||
if ((error = rdpei_recv_resume_touch_pdu(callback, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_recv_resume_touch_pdu failed with error %" PRIu32 "!", error);
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR,
|
||||
"rdpei_recv_resume_touch_pdu failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -928,7 +1013,8 @@ UINT rdpei_send_frame(RdpeiClientContext* context, RDPINPUT_TOUCH_FRAME* frame)
|
||||
|
||||
if ((error = rdpei_send_touch_event_pdu(callback, frame)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpei_send_touch_event_pdu failed with error %" PRIu32 "!", error);
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR,
|
||||
"rdpei_send_touch_event_pdu failed with error %" PRIu32 "!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -954,7 +1040,7 @@ static UINT rdpei_add_contact(RdpeiClientContext* context, const RDPINPUT_CONTAC
|
||||
contactPoint = &rdpei->contactPoints[contact->contactId];
|
||||
contactPoint->data = *contact;
|
||||
contactPoint->dirty = TRUE;
|
||||
SetEvent(rdpei->event);
|
||||
(void)SetEvent(rdpei->event);
|
||||
LeaveCriticalSection(&rdpei->lock);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
@ -965,28 +1051,29 @@ static UINT rdpei_touch_process(RdpeiClientContext* context, INT32 externalId, U
|
||||
{
|
||||
INT64 contactIdlocal = -1;
|
||||
RDPINPUT_CONTACT_POINT* contactPoint = NULL;
|
||||
RDPEI_PLUGIN* rdpei = NULL;
|
||||
BOOL begin = 0;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!context || !contactId || !context->handle)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
rdpei = (RDPEI_PLUGIN*)context->handle;
|
||||
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)context->handle;
|
||||
/* Create a new contact point in an empty slot */
|
||||
EnterCriticalSection(&rdpei->lock);
|
||||
begin = contactFlags & RDPINPUT_CONTACT_FLAG_DOWN;
|
||||
const BOOL begin = (contactFlags & RDPINPUT_CONTACT_FLAG_DOWN) != 0;
|
||||
contactPoint = rdpei_contact(rdpei, externalId, !begin);
|
||||
if (contactPoint)
|
||||
contactIdlocal = contactPoint->contactId;
|
||||
LeaveCriticalSection(&rdpei->lock);
|
||||
|
||||
if (contactIdlocal > UINT32_MAX)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (contactIdlocal >= 0)
|
||||
{
|
||||
RDPINPUT_CONTACT_DATA contact = { 0 };
|
||||
contact.x = x;
|
||||
contact.y = y;
|
||||
contact.contactId = contactIdlocal;
|
||||
contact.contactId = (UINT32)contactIdlocal;
|
||||
contact.contactFlags = contactFlags;
|
||||
contact.fieldsPresent = fieldFlags;
|
||||
|
||||
@ -1002,10 +1089,10 @@ static UINT rdpei_touch_process(RdpeiClientContext* context, INT32 externalId, U
|
||||
UINT32 p = va_arg(ap, UINT32);
|
||||
if (p >= 360)
|
||||
{
|
||||
WLog_WARN(TAG,
|
||||
"TouchContact %" PRIu32 ": Invalid orientation value %" PRIu32
|
||||
"degree, clamping to 359 degree",
|
||||
contactIdlocal, p);
|
||||
WLog_Print(rdpei->base.log, WLOG_WARN,
|
||||
"TouchContact %" PRId64 ": Invalid orientation value %" PRIu32
|
||||
"degree, clamping to 359 degree",
|
||||
contactIdlocal, p);
|
||||
p = 359;
|
||||
}
|
||||
contact.orientation = p;
|
||||
@ -1015,10 +1102,10 @@ static UINT rdpei_touch_process(RdpeiClientContext* context, INT32 externalId, U
|
||||
UINT32 p = va_arg(ap, UINT32);
|
||||
if (p > 1024)
|
||||
{
|
||||
WLog_WARN(TAG,
|
||||
"TouchContact %" PRIu32 ": Invalid pressure value %" PRIu32
|
||||
", clamping to 1024",
|
||||
contactIdlocal, p);
|
||||
WLog_Print(rdpei->base.log, WLOG_WARN,
|
||||
"TouchContact %" PRId64 ": Invalid pressure value %" PRIu32
|
||||
", clamping to 1024",
|
||||
contactIdlocal, p);
|
||||
p = 1024;
|
||||
}
|
||||
contact.pressure = p;
|
||||
@ -1028,7 +1115,7 @@ static UINT rdpei_touch_process(RdpeiClientContext* context, INT32 externalId, U
|
||||
}
|
||||
|
||||
if (contactId)
|
||||
*contactId = contactIdlocal;
|
||||
*contactId = (INT32)contactIdlocal;
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1168,7 +1255,7 @@ static UINT rdpei_add_pen(RdpeiClientContext* context, INT32 externalId,
|
||||
{
|
||||
contactPoint->data = *contact;
|
||||
contactPoint->dirty = TRUE;
|
||||
SetEvent(rdpei->event);
|
||||
(void)SetEvent(rdpei->event);
|
||||
}
|
||||
LeaveCriticalSection(&rdpei->lock);
|
||||
|
||||
@ -1365,7 +1452,6 @@ static UINT rdpei_pen_raw_event_va(RdpeiClientContext* context, INT32 externalId
|
||||
|
||||
static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext, rdpSettings* settings)
|
||||
{
|
||||
RdpeiClientContext* context = NULL;
|
||||
RDPEI_PLUGIN* rdpei = (RDPEI_PLUGIN*)base;
|
||||
|
||||
WINPR_ASSERT(base);
|
||||
@ -1378,18 +1464,20 @@ static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext, rdp
|
||||
rdpei->maxPenContacts = MAX_PEN_CONTACTS;
|
||||
rdpei->rdpcontext = rcontext;
|
||||
|
||||
WINPR_ASSERT(rdpei->base.log);
|
||||
|
||||
InitializeCriticalSection(&rdpei->lock);
|
||||
rdpei->event = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
if (!rdpei->event)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
context = (RdpeiClientContext*)calloc(1, sizeof(*context));
|
||||
RdpeiClientContext* context = (RdpeiClientContext*)calloc(1, sizeof(RdpeiClientContext));
|
||||
if (!context)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
@ -1417,12 +1505,24 @@ static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext, rdp
|
||||
rdpei->context = context;
|
||||
rdpei->base.iface.pInterface = (void*)context;
|
||||
|
||||
rdpei->running = TRUE;
|
||||
rdpei->thread = CreateThread(NULL, 0, rdpei_periodic_update, rdpei, 0, NULL);
|
||||
if (!rdpei->thread)
|
||||
rdpei->async =
|
||||
!freerdp_settings_get_bool(rdpei->rdpcontext->settings, FreeRDP_SynchronousDynamicChannels);
|
||||
if (rdpei->async)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
rdpei->running = TRUE;
|
||||
|
||||
rdpei->thread = CreateThread(NULL, 0, rdpei_periodic_update, rdpei, 0, NULL);
|
||||
if (!rdpei->thread)
|
||||
{
|
||||
WLog_Print(rdpei->base.log, WLOG_ERROR, "calloc failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!freerdp_client_channel_register(rdpei->rdpcontext->channels, rdpei->event,
|
||||
rdpei_poll_run, rdpei))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
@ -1435,16 +1535,19 @@ static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
|
||||
|
||||
rdpei->running = FALSE;
|
||||
if (rdpei->event)
|
||||
SetEvent(rdpei->event);
|
||||
(void)SetEvent(rdpei->event);
|
||||
|
||||
if (rdpei->thread)
|
||||
{
|
||||
WaitForSingleObject(rdpei->thread, INFINITE);
|
||||
CloseHandle(rdpei->thread);
|
||||
(void)WaitForSingleObject(rdpei->thread, INFINITE);
|
||||
(void)CloseHandle(rdpei->thread);
|
||||
}
|
||||
|
||||
if (rdpei->event && !rdpei->async)
|
||||
(void)freerdp_client_channel_unregister(rdpei->rdpcontext->channels, rdpei->event);
|
||||
|
||||
if (rdpei->event)
|
||||
CloseHandle(rdpei->event);
|
||||
(void)CloseHandle(rdpei->event);
|
||||
|
||||
DeleteCriticalSection(&rdpei->lock);
|
||||
free(rdpei->context);
|
||||
@ -1459,9 +1562,9 @@ static const IWTSVirtualChannelCallback geometry_callbacks = { rdpei_on_data_rec
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT rdpei_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpei_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, RDPEI_DVC_CHANNEL_NAME,
|
||||
return freerdp_generic_DVCPluginEntry(pEntryPoints, RDPEI_TAG, RDPEI_DVC_CHANNEL_NAME,
|
||||
sizeof(RDPEI_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
|
||||
&geometry_callbacks, init_plugin_cb, terminate_plugin_cb);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user