parent
f88d3063fe
commit
db06ec1975
|
@ -182,6 +182,37 @@
|
|||
<Compile Include="public\novnc\vendor\promise.js" />
|
||||
<Compile Include="public\scripts\agent-redir-rtc-0.1.0.js" />
|
||||
<Compile Include="public\serviceworker.js" />
|
||||
<Compile Include="rdp\asn1\ber.js" />
|
||||
<Compile Include="rdp\asn1\index.js" />
|
||||
<Compile Include="rdp\asn1\spec.js" />
|
||||
<Compile Include="rdp\asn1\univ.js" />
|
||||
<Compile Include="rdp\core\error.js" />
|
||||
<Compile Include="rdp\core\index.js" />
|
||||
<Compile Include="rdp\core\layer.js" />
|
||||
<Compile Include="rdp\core\log.js" />
|
||||
<Compile Include="rdp\core\rle.js" />
|
||||
<Compile Include="rdp\core\type.js" />
|
||||
<Compile Include="rdp\index.js" />
|
||||
<Compile Include="rdp\protocol\cert.js" />
|
||||
<Compile Include="rdp\protocol\index.js" />
|
||||
<Compile Include="rdp\protocol\nla.js" />
|
||||
<Compile Include="rdp\protocol\pdu\caps.js" />
|
||||
<Compile Include="rdp\protocol\pdu\data.js" />
|
||||
<Compile Include="rdp\protocol\pdu\global.js" />
|
||||
<Compile Include="rdp\protocol\pdu\index.js" />
|
||||
<Compile Include="rdp\protocol\pdu\lic.js" />
|
||||
<Compile Include="rdp\protocol\pdu\sec.js" />
|
||||
<Compile Include="rdp\protocol\rdp.js" />
|
||||
<Compile Include="rdp\protocol\t125\gcc.js" />
|
||||
<Compile Include="rdp\protocol\t125\index.js" />
|
||||
<Compile Include="rdp\protocol\t125\mcs.js" />
|
||||
<Compile Include="rdp\protocol\t125\per.js" />
|
||||
<Compile Include="rdp\protocol\tpkt.js" />
|
||||
<Compile Include="rdp\protocol\x224.js" />
|
||||
<Compile Include="rdp\security\index.js" />
|
||||
<Compile Include="rdp\security\jsbn.js" />
|
||||
<Compile Include="rdp\security\rsa.js" />
|
||||
<Compile Include="rdp\security\x509.js" />
|
||||
<Compile Include="swarmserver.js" />
|
||||
<Compile Include="multiserver.js" />
|
||||
<Compile Include="pass.js" />
|
||||
|
@ -550,6 +581,8 @@
|
|||
<Content Include="public\styles\style.css" />
|
||||
<Content Include="public\translate.bat" />
|
||||
<Content Include="public\translator.htm" />
|
||||
<Content Include="rdp\LICENSE" />
|
||||
<Content Include="rdp\README.md" />
|
||||
<Content Include="readme.md" />
|
||||
<Content Include="sample-config-advanced.json" />
|
||||
<Content Include="sample-config.json" />
|
||||
|
@ -624,6 +657,13 @@
|
|||
<Folder Include="public\styles\font-awesome\fonts\" />
|
||||
<Folder Include="public\styles\font-awesome\less\" />
|
||||
<Folder Include="public\styles\font-awesome\scss\" />
|
||||
<Folder Include="rdp\" />
|
||||
<Folder Include="rdp\asn1\" />
|
||||
<Folder Include="rdp\core\" />
|
||||
<Folder Include="rdp\protocol\" />
|
||||
<Folder Include="rdp\protocol\pdu\" />
|
||||
<Folder Include="rdp\protocol\t125\" />
|
||||
<Folder Include="rdp\security\" />
|
||||
<Folder Include="translate\" />
|
||||
<Folder Include="typings\" />
|
||||
<Folder Include="typings\globals\" />
|
||||
|
|
|
@ -129,8 +129,9 @@ module.exports.CreateMstscRelay = function (parent, db, ws, req, args, domain) {
|
|||
function startRdp(port) {
|
||||
parent.parent.debug('relay', 'RDP: Starting RDP client on loopback port ' + port);
|
||||
try {
|
||||
rdpClient = require('node-rdpjs-2').createClient({
|
||||
logLevel: 'ERROR',
|
||||
//rdpClient = require('node-rdpjs-2').createClient({
|
||||
rdpClient = require('./rdp').createClient({
|
||||
logLevel: 'NONE', // 'ERROR',
|
||||
domain: obj.infos.domain,
|
||||
userName: obj.infos.username,
|
||||
password: obj.infos.password,
|
||||
|
|
|
@ -3478,13 +3478,16 @@ function mainStart() {
|
|||
const verSplit = process.version.substring(1).split('.');
|
||||
var nodeVersion = parseInt(verSplit[0]) + (parseInt(verSplit[1]) / 100);
|
||||
|
||||
// Check if RDP support if present
|
||||
var mstsc = true;
|
||||
try { require('./rdp') } catch (ex) { mstsc = false; }
|
||||
|
||||
// Check if Windows SSPI, LDAP, Passport and YubiKey OTP will be used
|
||||
var sspi = false;
|
||||
var ldap = false;
|
||||
var passport = null;
|
||||
var allsspi = true;
|
||||
var yubikey = false;
|
||||
var mstsc = false;
|
||||
var ssh = false;
|
||||
var sessionRecording = false;
|
||||
var domainCount = 0;
|
||||
|
@ -3499,7 +3502,7 @@ function mainStart() {
|
|||
if (config.domains[i].sendgrid != null) { sendgrid = true; }
|
||||
if (config.domains[i].yubikey != null) { yubikey = true; }
|
||||
if (config.domains[i].auth == 'ldap') { ldap = true; }
|
||||
if (config.domains[i].mstsc == true) { mstsc = true; }
|
||||
if (mstsc == false) { config.domains[i].mstsc = false; }
|
||||
if (config.domains[i].ssh == true) { if (nodeVersion < 11) { config.domains[i].ssh = false; } ssh = true; }
|
||||
if ((typeof config.domains[i].authstrategies == 'object')) {
|
||||
if (passport == null) { passport = ['passport']; }
|
||||
|
@ -3519,7 +3522,6 @@ function mainStart() {
|
|||
var modules = ['ws@5.2.3', 'cbor@5.2.0', '@yetzt/nedb', 'https', 'yauzl', 'ipcheck', 'express', 'archiver@4.0.2', 'multiparty', 'node-forge', 'express-ws@4.0.0', 'compression', 'body-parser', 'cookie-session@1.4.0', 'express-handlebars'];
|
||||
if (require('os').platform() == 'win32') { modules.push('node-windows@0.1.4'); modules.push('loadavg-windows'); if (sspi == true) { modules.push('node-sspi'); } } // Add Windows modules
|
||||
if (ldap == true) { modules.push('ldapauth-fork'); }
|
||||
if (mstsc == true) { modules.push('node-rdpjs-2'); }
|
||||
if (ssh == true) { if (nodeVersion < 11) { addServerWarning('MeshCentral SSH support requires NodeJS 11 or higher.', 1); } else { modules.push('ssh2'); } }
|
||||
if (passport != null) { modules.push(...passport); }
|
||||
if (sessionRecording == true) { modules.push('image-size'); } // Need to get the remote desktop JPEG sizes to index the recodring file.
|
||||
|
|
|
@ -0,0 +1,674 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
|
@ -0,0 +1,10 @@
|
|||
node-rdpjs
|
||||
==========
|
||||
|
||||
This folder contains a fork of the node-rdpjs2 package which is identical to [node-rdpjs](github.com/citronneur/node-rdpjs) v0.2.1 but just updated for
|
||||
latest versions of Node along with more modifications to remove dependencies and more importantly, added support for Network Layer Authentication (NLA).
|
||||
|
||||
NLA support was ported from [RDP-RS](https://github.com/citronneur/rdp-rs) which is a Rust RDP solution by the same author as node-rdpjs.
|
||||
|
||||
The license for node-rdpjs is GPL 3.0 and is limited the the "rdp" folder. If this is a concern, you can delete the "rdp" folder and MeshCentral will
|
||||
still work perfectly without RDP functionality.
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../core').type;
|
||||
var log = require('../core').log;
|
||||
var error = require('../core').error;
|
||||
|
||||
/**
|
||||
* Parse tag(T) field of BER TLV
|
||||
* And check with expected tag
|
||||
* @param s {type.Stream}
|
||||
* @param tag {spec.tag}
|
||||
* @returns {Boolean} True for valid tag matching
|
||||
*/
|
||||
function decodeTag(s, tag) {
|
||||
var nextTag = new type.UInt8().read(s).value;
|
||||
if (tag.tagNumber > 30) {
|
||||
nextTagNumber = new type.UInt8().read(s).value;
|
||||
}
|
||||
else {
|
||||
nextTagNumber = nextTag & 0x1F;
|
||||
}
|
||||
|
||||
return ((nextTag & 0xE0) === (tag.tagClass | tag.tagFormat)) && (nextTagNumber === tag.tagNumber);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse length(L) field of BER TLV
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer}
|
||||
*/
|
||||
function decodeLength(s) {
|
||||
var size = new type.UInt8().read(s).value;
|
||||
if(size & 0x80) {
|
||||
size &= ~0x80;
|
||||
if(size === 1) {
|
||||
size = new type.UInt8().read(s).value;
|
||||
}
|
||||
else if(size === 2) {
|
||||
size = new type.UInt16Be().read(s).value;
|
||||
}
|
||||
else{
|
||||
throw new error.ProtocolError('NODE_RDP_ASN1_BER_INVALID_LENGTH');
|
||||
}
|
||||
}
|
||||
return size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode tuple TLV (Tag Length Value) of BER
|
||||
* @param s {type.Stream}
|
||||
* @param tag {spec.Asn1Tag} expected tag
|
||||
* @returns {type.BinaryString} Value of tuple
|
||||
*/
|
||||
function decode(s, tag) {
|
||||
if (!decodeTag(s, tag)) {
|
||||
throw new error.ProtocolError('NODE_RDP_ASN1_BER_INVALID_TAG');
|
||||
}
|
||||
var length = decodeLength(s);
|
||||
|
||||
if (length === 0) {
|
||||
return new type.Stream(0);
|
||||
}
|
||||
return new type.BinaryString(null,{ readLength : new type.CallableValue(length) }).read(s);
|
||||
};
|
||||
|
||||
function encodeTag(tag) {
|
||||
if(tag.tagNumber > 30) {
|
||||
return new type.Component([new type.UInt8(tag.tagClass | tag.tagFormat | 0x1F), new type.UInt8(tag.tagNumber)]);
|
||||
}
|
||||
else {
|
||||
return new type.UInt8((tag.tagClass | tag.tagFormat) | (tag.tagNumber & 0x1F));
|
||||
}
|
||||
}
|
||||
|
||||
function encodeLength(length) {
|
||||
if(length > 0x7f) {
|
||||
return new type.Component([new type.UInt8(0x82), new type.UInt16Be(length)]);
|
||||
}
|
||||
else {
|
||||
return new type.UInt8(length);
|
||||
}
|
||||
}
|
||||
|
||||
function encode(tag, buffer) {
|
||||
return new type.Component([encodeTag(tag), encodeLength(buffer.size()), buffer]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module Export
|
||||
*/
|
||||
module.exports = {
|
||||
decode : decode,
|
||||
encode : encode
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var ber = require('./ber');
|
||||
var univ = require('./univ');
|
||||
var spec = require('./spec');
|
||||
|
||||
module.exports = {
|
||||
ber : ber,
|
||||
univ : univ,
|
||||
spec : spec
|
||||
};
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var type = require('../core').type;
|
||||
var error = require('../core').error;
|
||||
|
||||
/**
|
||||
* Tag Class
|
||||
*/
|
||||
var TagClass = {
|
||||
Universal : 0x00,
|
||||
Application : 0x40,
|
||||
Context : 0x80,
|
||||
Private : 0xC0
|
||||
};
|
||||
|
||||
/**
|
||||
* Tag Format
|
||||
*/
|
||||
var TagFormat = {
|
||||
Primitive : 0x00,
|
||||
Constructed : 0x20
|
||||
};
|
||||
|
||||
/**
|
||||
* ASN.1 tag
|
||||
* @param tagClass {TagClass}
|
||||
* @param tagFormat {TagFormat}
|
||||
* @param tagNumber {integer}
|
||||
*/
|
||||
function Asn1Tag(tagClass, tagFormat, tagNumber) {
|
||||
this.tagClass = tagClass;
|
||||
this.tagFormat = tagFormat;
|
||||
this.tagNumber = tagNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* ASN.1 Specification
|
||||
* @param tag {Asn1Tag}
|
||||
*/
|
||||
function Asn1Spec(tag) {
|
||||
this.tag = tag;
|
||||
this.opt = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an implicit tag
|
||||
* override tag
|
||||
* @param tag {Asn1Tag}
|
||||
* @returns {Asn1Spec}
|
||||
*/
|
||||
Asn1Spec.prototype.implicitTag = function(tag) {
|
||||
this.tag = tag;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set optional to true
|
||||
* @returns {Asn1Spec}
|
||||
*/
|
||||
Asn1Spec.prototype.optional = function() {
|
||||
this.opt = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add explicit tag
|
||||
* Append new tag header to existing tag
|
||||
* @param tag {Asn1Tag}
|
||||
* @returns {Asn1SpecExplicitTag}
|
||||
*/
|
||||
Asn1Spec.prototype.explicitTag = function(tag) {
|
||||
return new Asn1SpecExplicitTag(tag, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode must be implemented by all sub type
|
||||
* @param s {type.Stream}
|
||||
* @param decoder
|
||||
*/
|
||||
Asn1Spec.prototype.decode = function(s, decoder) {
|
||||
throw new error.FatalError('NODE_RDP_AS1_SPEC_DECODE_NOT_IMPLEMENTED');
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode must be implemented by all sub type
|
||||
* @param decoder
|
||||
*/
|
||||
Asn1Spec.prototype.encode = function(encoder) {
|
||||
throw new error.FatalError('NODE_RDP_AS1_SPEC_ENCODE_NOT_IMPLEMENTED');
|
||||
};
|
||||
|
||||
/**
|
||||
* Component Asn1Spec object
|
||||
*/
|
||||
function Asn1SpecExplicitTag(tag, spec) {
|
||||
Asn1Spec.call(this, tag);
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
inherits(Asn1SpecExplicitTag, Asn1Spec);
|
||||
|
||||
/**
|
||||
* Decode first header
|
||||
* @param s {type.Stream}
|
||||
* @param decoder
|
||||
*/
|
||||
Asn1Spec.prototype.decode = function(s, decoder) {
|
||||
var specStream = new type.Stream(decoder.decode(s, this.tag).value);
|
||||
this.spec.decode(specStream, decoder);
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
TagClass : TagClass,
|
||||
TagFormat : TagFormat,
|
||||
Asn1Tag : Asn1Tag,
|
||||
Asn1Spec : Asn1Spec,
|
||||
Asn1SpecExplicitTag : Asn1SpecExplicitTag
|
||||
};
|
|
@ -0,0 +1,606 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var spec = require('./spec');
|
||||
var type = require('../core').type;
|
||||
var error = require('../core').error;
|
||||
var inherits = require('util').inherits;
|
||||
|
||||
/**
|
||||
* ASN.1 Universal tags
|
||||
* @see http://www.obj-sys.com/asn1tutorial/node124.html
|
||||
*/
|
||||
var UniversalTag = {
|
||||
Boolean : 1,
|
||||
Integer : 2,
|
||||
BitString : 3,
|
||||
OctetString : 4,
|
||||
Null : 5,
|
||||
ObjectIdentifier : 6,
|
||||
ObjectDescriptor : 7,
|
||||
Enumerate : 10,
|
||||
UTF8String : 12,
|
||||
Sequence : 16,
|
||||
Set : 17,
|
||||
PrintableString : 19,
|
||||
T61String : 20,
|
||||
IA5String : 22,
|
||||
UTCTime : 23,
|
||||
GeneralizedTime : 24,
|
||||
UniversalString : 28,
|
||||
BMPString : 30
|
||||
};
|
||||
|
||||
/**
|
||||
* Boolean type
|
||||
* @param value {boolean} inner value
|
||||
*/
|
||||
function Boolean(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.Boolean));
|
||||
this.value = value || false;
|
||||
}
|
||||
|
||||
inherits(Boolean, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
Boolean.prototype.decode = function(s, decoder) {
|
||||
this.value = new type.UInt8().read(new type.Stream(decoder.decode(s, this.tag).value)).value !== 0;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {type.*}
|
||||
*/
|
||||
Boolean.prototype.encode = function(encoder) {
|
||||
if(this.value) {
|
||||
return encoder.encode(this.tag, new type.UInt8(0xff));
|
||||
}
|
||||
else {
|
||||
return encoder.encode(this.tag, new type.UInt8(0));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Integer type
|
||||
* @param value {integer | Buffer}
|
||||
*/
|
||||
function Integer(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.Integer));
|
||||
this.value = value || 0;
|
||||
}
|
||||
|
||||
inherits(Integer, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {Integer}
|
||||
*/
|
||||
Integer.prototype.decode = function(s, decoder) {
|
||||
var integerBuffer = decoder.decode(s, this.tag).value;
|
||||
if(integerBuffer.length < 5) {
|
||||
var integerStream = new type.Stream(integerBuffer);
|
||||
while (integerStream.availableLength() > 0) {
|
||||
this.value = this.value << 8;
|
||||
this.value |= new type.UInt8().read(integerStream).value;
|
||||
}
|
||||
}
|
||||
// bignum case
|
||||
else {
|
||||
this.value = integerBuffer;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param encoder {ber.decoder}
|
||||
* @returns {type.*}
|
||||
*/
|
||||
Integer.prototype.encode = function(encoder) {
|
||||
if(this.value <= 0xff) {
|
||||
return encoder.encode(this.tag, new type.UInt8(this.value));
|
||||
}
|
||||
else if(this.value <= 0xffff) {
|
||||
return encoder.encode(this.tag, new type.UInt16Be(this.value));
|
||||
}
|
||||
else {
|
||||
return encoder.encode(this.tag, new type.UInt32Be(this.value));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sequence type
|
||||
* @param value {object}
|
||||
*/
|
||||
function Sequence(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Constructed, UniversalTag.Sequence));
|
||||
this.value = value || [];
|
||||
}
|
||||
|
||||
inherits(Sequence, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {Sequence}
|
||||
*/
|
||||
Sequence.prototype.decode = function(s, decoder) {
|
||||
var sequenceStream = new type.Stream(decoder.decode(s, this.tag).value);
|
||||
for (var i in this.value) {
|
||||
var rec = sequenceStream.offset;
|
||||
try {
|
||||
this.value[i].decode(sequenceStream, decoder);
|
||||
} catch(e) {
|
||||
if ((e.message === 'NODE_RDP_ASN1_BER_INVALID_TAG') && !this.value[i].opt) {
|
||||
throw new error.ProtocolError('NODE_RDP_ASN1_UNIV_SEQUENCE_FIELD_NOT_PRESENT');
|
||||
}
|
||||
sequenceStream.offset = rec;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode sequence
|
||||
* @param encoder
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
Sequence.prototype.encode = function(encoder) {
|
||||
var sequence = new type.Component([]);
|
||||
for (var i in this.value) {
|
||||
sequence.obj.push(this.value[i].encode(encoder))
|
||||
}
|
||||
return encoder.encode(this.tag, sequence);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enumerate type
|
||||
* @param value {integer}
|
||||
*/
|
||||
function Enumerate(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.Enumerate));
|
||||
this.value = value || 0;
|
||||
}
|
||||
|
||||
inherits(Enumerate, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {Enumerate}
|
||||
*/
|
||||
Enumerate.prototype.decode = function(s, decoder) {
|
||||
this.value = new type.UInt8().read(new type.Stream(decoder.decode(s, this.tag).value)).value;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode enumerate type
|
||||
* @param encoder
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
Enumerate.prototype.encode = function(encoder) {
|
||||
return encoder.encode(this.tag, new type.UInt8(this.value));
|
||||
};
|
||||
|
||||
/**
|
||||
* OctetString type
|
||||
* @param value {Buffer}
|
||||
*/
|
||||
function OctetString(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.OctetString));
|
||||
this.value = value || Buffer.alloc(0);
|
||||
}
|
||||
|
||||
inherits(OctetString, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {OctetString}
|
||||
*/
|
||||
OctetString.prototype.decode = function(s, decoder) {
|
||||
this.value = decoder.decode(s, this.tag).value;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode Octet String
|
||||
* @param encoder
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
OctetString.prototype.encode = function(encoder) {
|
||||
return encoder.encode(this.tag, new type.BinaryString(this.value));
|
||||
};
|
||||
|
||||
/**
|
||||
* ObjectIdentifier type
|
||||
* @param value {Buffer}
|
||||
*/
|
||||
function ObjectIdentifier(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.ObjectIdentifier));
|
||||
this.value = value || Buffer.alloc(5);
|
||||
}
|
||||
|
||||
inherits(ObjectIdentifier, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {ObjectIdentifier}
|
||||
*/
|
||||
ObjectIdentifier.prototype.decode = function(s, decoder) {
|
||||
this.value = decoder.decode(s, this.tag).value;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Null type
|
||||
*/
|
||||
function Null() {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.Null));
|
||||
}
|
||||
|
||||
inherits(Null, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {Null}
|
||||
*/
|
||||
Null.prototype.decode = function(s, decoder) {
|
||||
decoder.decode(s, this.tag);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Choice type
|
||||
* @param value {object} list of available type
|
||||
*/
|
||||
function Choice(value) {
|
||||
// not tagged type
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag());
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
inherits(Choice, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {Choice}
|
||||
*/
|
||||
Choice.prototype.decode = function(s, decoder) {
|
||||
for (var i in this.value) {
|
||||
var rec = s.offset;
|
||||
try {
|
||||
this.value[i].decode(s, decoder);
|
||||
break;
|
||||
}
|
||||
catch(e) {
|
||||
s.offset = rec;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* SetOf type
|
||||
* @param factory {function} type builder
|
||||
* @param value {object} list of available type
|
||||
*/
|
||||
function SetOf(factory, value) {
|
||||
// not tagged type
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Constructed, UniversalTag.Set));
|
||||
this.factory = factory;
|
||||
this.value = value || [];
|
||||
}
|
||||
|
||||
inherits(SetOf, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {SetOf}
|
||||
*/
|
||||
SetOf.prototype.decode = function(s, decoder) {
|
||||
var setOfStream = new type.Stream(decoder.decode(s, this.tag).value);
|
||||
while (setOfStream.availableLength() > 0) {
|
||||
this.value.push(this.factory().decode(setOfStream, decoder));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* SequenceOf type
|
||||
* @param factory {function} type builder
|
||||
* @param value {object} list of available type
|
||||
*/
|
||||
function SequenceOf(factory, value) {
|
||||
// not tagged type
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Constructed, UniversalTag.Sequence));
|
||||
this.factory = factory;
|
||||
this.value = value || [];
|
||||
}
|
||||
|
||||
inherits(SequenceOf, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {SequenceOf}
|
||||
*/
|
||||
SequenceOf.prototype.decode = function(s, decoder) {
|
||||
var sequenceOfStream = new type.Stream(decoder.decode(s, this.tag).value);
|
||||
while (sequenceOfStream.availableLength() > 0) {
|
||||
this.value.push(this.factory().decode(sequenceOfStream, decoder));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* BitString type
|
||||
* @param value {Buffer}
|
||||
*/
|
||||
function BitString(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.BitString));
|
||||
this.value = [];
|
||||
}
|
||||
|
||||
inherits(BitString, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {BitString}
|
||||
*/
|
||||
BitString.prototype.decode = function(s, decoder) {
|
||||
var bitStream = new type.Stream(decoder.decode(s, this.tag).value);
|
||||
var padding = new type.UInt8().read(bitStream).value;
|
||||
var value = [];
|
||||
for(var i = 0; i < padding; i++) {
|
||||
value.push(0);
|
||||
}
|
||||
|
||||
while(bitStream.availableLength() > 0) {
|
||||
var octet = new type.UInt8().read(bitStream).value;
|
||||
var currentPadding = 0;
|
||||
if(bitStream.availableLength() === 0) {
|
||||
currentPadding = padding;
|
||||
}
|
||||
for(var i = 7; i >= currentPadding; i--) {
|
||||
value.push(((octet >> i) & 1)?1:0);
|
||||
}
|
||||
}
|
||||
this.value = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert bit string to buffer object
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
BitString.prototype.toBuffer = function () {
|
||||
var length = this.value.length / 8;
|
||||
var resultStream = new type.Stream(length);
|
||||
for (var i = 0; i < length; i ++) {
|
||||
var currentOctet = 0;
|
||||
for (var j = 0; j < 8; j++) {
|
||||
currentOctet = currentOctet | (this.value[i * 8 + j] << (7 - j));
|
||||
}
|
||||
new type.UInt8(currentOctet).write(resultStream);
|
||||
}
|
||||
return resultStream.buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* T61String type
|
||||
* @param value {Buffer}
|
||||
*/
|
||||
function T61String(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.T61String));
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
inherits(T61String, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {T61String}
|
||||
*/
|
||||
T61String.prototype.decode = function(s, decoder) {
|
||||
this.value = decoder.decode(s, this.tag).value;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* PrintableString type
|
||||
* @param value {Buffer}
|
||||
*/
|
||||
function PrintableString(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.PrintableString));
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
inherits(PrintableString, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {PrintableString}
|
||||
*/
|
||||
PrintableString.prototype.decode = function(s, decoder) {
|
||||
this.value = decoder.decode(s, this.tag).value;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* UniversalString type
|
||||
* @param value {Buffer}
|
||||
*/
|
||||
function UniversalString(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.UniversalString));
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
inherits(UniversalString, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {UniversalString}
|
||||
*/
|
||||
UniversalString.prototype.decode = function(s, decoder) {
|
||||
this.value = decoder.decode(s, this.tag).value;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* UTF8String type
|
||||
* @param value {Buffer}
|
||||
*/
|
||||
function UTF8String(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.UTF8String));
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
inherits(UTF8String, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {UTF8String}
|
||||
*/
|
||||
UTF8String.prototype.decode = function(s, decoder) {
|
||||
this.value = decoder.decode(s, this.tag).value;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* BMPString type
|
||||
* @param value {Buffer}
|
||||
*/
|
||||
function BMPString(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.BMPString));
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
inherits(BMPString, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {BMPString}
|
||||
*/
|
||||
BMPString.prototype.decode = function(s, decoder) {
|
||||
this.value = decoder.decode(s, this.tag).value;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* IA5String type
|
||||
* @param value {Buffer}
|
||||
*/
|
||||
function IA5String(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.IA5String));
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
inherits(IA5String, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {IA5String}
|
||||
*/
|
||||
IA5String.prototype.decode = function(s, decoder) {
|
||||
this.value = decoder.decode(s, this.tag).value;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* UTCTime type
|
||||
* @param value {Buffer}
|
||||
*/
|
||||
function UTCTime(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.UTCTime));
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
inherits(UTCTime, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {UTCTime}
|
||||
*/
|
||||
UTCTime.prototype.decode = function(s, decoder) {
|
||||
this.value = decoder.decode(s, this.tag).value;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* GeneralizedTime type
|
||||
* @param value {Buffer}
|
||||
*/
|
||||
function GeneralizedTime(value) {
|
||||
spec.Asn1Spec.call(this, new spec.Asn1Tag(spec.TagClass.Universal, spec.TagFormat.Primitive, UniversalTag.GeneralizedTime));
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
inherits(GeneralizedTime, spec.Asn1Spec);
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param decoder {ber.decoder}
|
||||
* @returns {GeneralizedTime}
|
||||
*/
|
||||
GeneralizedTime.prototype.decode = function(s, decoder) {
|
||||
this.value = decoder.decode(s, this.tag).value;
|
||||
return this;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Boolean : Boolean,
|
||||
Integer : Integer,
|
||||
Sequence : Sequence,
|
||||
Enumerate : Enumerate,
|
||||
OctetString : OctetString,
|
||||
ObjectIdentifier : ObjectIdentifier,
|
||||
Null : Null,
|
||||
Choice : Choice,
|
||||
SequenceOf : SequenceOf,
|
||||
SetOf : SetOf,
|
||||
BitString : BitString,
|
||||
T61String : T61String,
|
||||
PrintableString : PrintableString,
|
||||
UniversalString : UniversalString,
|
||||
UTF8String : UTF8String,
|
||||
BMPString : BMPString,
|
||||
IA5String : IA5String,
|
||||
UTCTime : UTCTime,
|
||||
GeneralizedTime : GeneralizedTime
|
||||
};
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
|
||||
/**
|
||||
* Fatal error stop program
|
||||
*/
|
||||
function FatalError(message, code) {
|
||||
Error.captureStackTrace(this);
|
||||
this.message = message || "";
|
||||
this.code = code || 'NODE_RDP_CORE_ERROR_NO_ERROR_CODE';
|
||||
}
|
||||
|
||||
/**
|
||||
* inherit from error
|
||||
*/
|
||||
inherits(FatalError, Error);
|
||||
|
||||
/**
|
||||
* Protocol error (non fatal);
|
||||
*/
|
||||
function ProtocolError(code, message) {
|
||||
Error.captureStackTrace(this);
|
||||
this.code = code;
|
||||
this.message = message || "";
|
||||
}
|
||||
|
||||
/**
|
||||
* inherit from error
|
||||
*/
|
||||
inherits(ProtocolError, Error);
|
||||
|
||||
/**
|
||||
* ImplementationError error (non fatal);
|
||||
*/
|
||||
function ImplementationError(code, message) {
|
||||
Error.captureStackTrace(this);
|
||||
this.code = code;
|
||||
this.message = message || "";
|
||||
}
|
||||
|
||||
/**
|
||||
* inherit from error
|
||||
*/
|
||||
inherits(ImplementationError, Error);
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
FatalError : FatalError,
|
||||
ProtocolError : ProtocolError,
|
||||
ImplementationError : ImplementationError
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var layer = require('./layer');
|
||||
var type = require('./type');
|
||||
var log = require('./log');
|
||||
var error = require('./error');
|
||||
var rle = require('./rle');
|
||||
|
||||
module.exports = {
|
||||
layer : layer,
|
||||
type : type,
|
||||
log : log,
|
||||
error : error,
|
||||
rle : rle
|
||||
};
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var fs = require('fs');
|
||||
var type = require('./type');
|
||||
var log = require('./log');
|
||||
var tls = require('tls');
|
||||
var crypto = require('crypto');
|
||||
var events = require('events');
|
||||
|
||||
/**
|
||||
* Buffer data from socket to present
|
||||
* well formed packets
|
||||
*/
|
||||
function BufferLayer(socket) {
|
||||
//for ssl connection
|
||||
this.secureSocket = null;
|
||||
this.socket = socket;
|
||||
|
||||
var self = this;
|
||||
// bind event
|
||||
this.socket.on('data', function(data) {
|
||||
try {
|
||||
self.recv(data);
|
||||
}
|
||||
catch(e) {
|
||||
self.socket.destroy();
|
||||
self.emit('error', e);
|
||||
}
|
||||
}).on('close', function() {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
//buffer data
|
||||
this.buffers = [];
|
||||
this.bufferLength = 0;
|
||||
//expected size
|
||||
this.expectedSize = 0;
|
||||
}
|
||||
|
||||
inherits(BufferLayer, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Call from tcp layer
|
||||
* @param data tcp stream
|
||||
*/
|
||||
BufferLayer.prototype.recv = function (data) {
|
||||
if (this.buffers.length == 0) { this.bufferLength = 0; } // CORRECT
|
||||
this.buffers[this.buffers.length] = data;
|
||||
this.bufferLength += data.length;
|
||||
|
||||
//console.log('TCP RECV', this.bufferLength, this.expectedSize, data.toString('hex'));
|
||||
//console.log('this.buffers', this.buffers);
|
||||
//console.log('this.expectedSize', this.expectedSize);
|
||||
//console.log('this.bufferLength', this.bufferLength);
|
||||
|
||||
if (this.expectedSize == 0) { console.log('this.expectedSize == 0'); return; }
|
||||
|
||||
while (this.bufferLength >= this.expectedSize) {
|
||||
//console.log('this.expectedSize', this.expectedSize);
|
||||
//console.log('this.bufferLength', this.bufferLength);
|
||||
|
||||
//linear buffer
|
||||
var expectedData = new type.Stream(this.expectedSize);
|
||||
|
||||
//create expected data
|
||||
while (expectedData.availableLength() > 0) {
|
||||
|
||||
var rest = expectedData.availableLength();
|
||||
var buffer = this.buffers.shift();
|
||||
|
||||
//console.log('xx', rest, buffer);
|
||||
|
||||
if (buffer.length > expectedData.availableLength()) {
|
||||
this.buffers.unshift(buffer.slice(rest));
|
||||
new type.BinaryString(buffer, { readLength : new type.CallableValue(expectedData.availableLength()) }).write(expectedData);
|
||||
} else {
|
||||
new type.BinaryString(buffer).write(expectedData);
|
||||
}
|
||||
}
|
||||
|
||||
this.bufferLength -= this.expectedSize;
|
||||
expectedData.offset = 0;
|
||||
|
||||
//console.log('TCP EMIT', expectedData);
|
||||
this.emit('data', expectedData);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Call tcp socket to write stream
|
||||
* @param {type.Type} packet
|
||||
*/
|
||||
BufferLayer.prototype.send = function(data) {
|
||||
var s = new type.Stream(data.size());
|
||||
data.write(s);
|
||||
if(this.secureSocket) {
|
||||
this.secureSocket.write(s.buffer);
|
||||
}
|
||||
else {
|
||||
this.socket.write(s.buffer);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Call tcp socket to write a buffer
|
||||
*/
|
||||
BufferLayer.prototype.sendBuffer = function (buffer) {
|
||||
if (this.secureSocket) {
|
||||
//console.log('SSL sendBuffer', buffer.length, buffer.toString('hex'));
|
||||
this.secureSocket.write(buffer);
|
||||
}
|
||||
else {
|
||||
//console.log('TCP sendBuffer', buffer.length, buffer.toString('hex'));
|
||||
this.socket.write(buffer);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wait expected size data before call callback function
|
||||
* @param {number} expectSize size expected
|
||||
*/
|
||||
BufferLayer.prototype.expect = function(expectedSize) {
|
||||
this.expectedSize = expectedSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert connection to TLS connection
|
||||
* @param callback {func} when connection is done
|
||||
*/
|
||||
BufferLayer.prototype.startTLS = function(callback) {
|
||||
var self = this;
|
||||
|
||||
this.secureSocket = tls.connect({
|
||||
socket: this.socket,
|
||||
secureContext: tls.createSecureContext(),
|
||||
isServer: false,
|
||||
requestCert: false,
|
||||
rejectUnauthorized: false
|
||||
}, (err) => {
|
||||
log.warn(err);
|
||||
callback(err);
|
||||
});
|
||||
|
||||
this.secureSocket.on('data', function (data) {
|
||||
|
||||
//console.log('SSL RECV', data.length, data);
|
||||
|
||||
try {
|
||||
self.recv(data);
|
||||
}
|
||||
catch (e) {
|
||||
//console.log('SSL RECV ERR', e);
|
||||
self.socket.destroy();
|
||||
self.emit('error', e);
|
||||
}
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert connection to TLS server
|
||||
* @param keyFilePath {string} key file path
|
||||
* @param crtFilePath {string} certificat file path
|
||||
* @param callback {function}
|
||||
*/
|
||||
BufferLayer.prototype.listenTLS = function(keyFilePath, crtFilePath, callback) {
|
||||
var self = this;
|
||||
|
||||
this.secureSocket = tls.connect({
|
||||
socket: this.socket,
|
||||
secureContext: tls.createSecureContext({
|
||||
key: fs.readFileSync(keyFilePath),
|
||||
cert: fs.readFileSync(crtFilePath),
|
||||
}),
|
||||
isServer: true,
|
||||
requestCert: false,
|
||||
rejectUnauthorized: false
|
||||
}, (err) => {
|
||||
log.warn(err);
|
||||
callback(err);
|
||||
});
|
||||
|
||||
this.secureSocket.on('data', function(data) {
|
||||
try {
|
||||
self.recv(data);
|
||||
}
|
||||
catch(e) {
|
||||
self.socket.destroy();
|
||||
self.emit('error', e);
|
||||
}
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
BufferLayer.prototype.close = function() {
|
||||
this.socket.end();
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
BufferLayer : BufferLayer
|
||||
};
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var Levels = {
|
||||
'DEBUG': 1,
|
||||
'INFO': 2,
|
||||
'WARN': 3,
|
||||
'ERROR': 4,
|
||||
'NONE': 5
|
||||
}
|
||||
|
||||
/*
|
||||
var Logger = require('bunyan');
|
||||
|
||||
let logStreams = [];
|
||||
|
||||
if (process.env.enable_log_file === 'true') {
|
||||
logStreams.push(
|
||||
{
|
||||
type: 'rotating-file',
|
||||
period: '1d',
|
||||
count: 2,
|
||||
path: `node-rdpjs${process.pid}.log`,
|
||||
level: process.env.log_level
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
logStreams.push(
|
||||
{
|
||||
stream: process.stderr,
|
||||
level: process.env.log_level
|
||||
}
|
||||
);
|
||||
|
||||
var logger = Logger.createLogger({
|
||||
name: 'node-rdpjs',
|
||||
streams: logStreams
|
||||
});
|
||||
*/
|
||||
|
||||
function log(level, message) {
|
||||
if (Levels[level] < module.exports.level) return;
|
||||
console.log("[node-rdpjs] " + level + ":\t" + message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
level: Levels.INFO, // Levels.INFO,
|
||||
Levels: Levels,
|
||||
debug: function (message) {
|
||||
//logger.debug(message);
|
||||
},
|
||||
info: function (message) {
|
||||
//logger.info(message);
|
||||
},
|
||||
warn: function (message) {
|
||||
//logger.warn(message);
|
||||
},
|
||||
error: function (message) {
|
||||
//logger.error(message);
|
||||
}
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,488 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var log = require('./log');
|
||||
var error = require('./error');
|
||||
|
||||
/**
|
||||
* Stream wrapper around buffer type
|
||||
* @param i {Buffer | integer} size of init buffer
|
||||
* @returns
|
||||
*/
|
||||
function Stream(i) {
|
||||
this.offset = 0;
|
||||
if (i instanceof Buffer) {
|
||||
this.buffer = i;
|
||||
}
|
||||
else {
|
||||
this.buffer = Buffer.alloc(i || 8192);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return length of available data in stream
|
||||
* @returns {Number} length of available data in stream
|
||||
*/
|
||||
Stream.prototype.availableLength = function() {
|
||||
return this.buffer.length - this.offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* increment offset
|
||||
* @param length {integer} length of padding
|
||||
*/
|
||||
Stream.prototype.readPadding = function(length) {
|
||||
this.offset += length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Format string buffer
|
||||
* @returns {string} buffer stringified
|
||||
*/
|
||||
Stream.prototype.getValue = function() {
|
||||
return this.buffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param value {object | function} inner value
|
||||
* @returns
|
||||
*/
|
||||
function CallableValue(value) {
|
||||
if(value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For syntaxic purpose
|
||||
*/
|
||||
Object.defineProperty(CallableValue.prototype, "value", {
|
||||
get: function() { return this._value(); },
|
||||
set: function(e) {
|
||||
if(typeof e !== 'function') {
|
||||
this._value = function () { return e; };
|
||||
}
|
||||
else {
|
||||
this._value = e;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Type readable or writable by binary stream
|
||||
* @param {object} opt
|
||||
* .conditional {boolean} read or write type depend on conditional call
|
||||
* @returns
|
||||
*/
|
||||
function Type(opt) {
|
||||
CallableValue.call(this);
|
||||
this.opt = opt || {};
|
||||
this.isReaded = false;
|
||||
this.isWritten = false;
|
||||
}
|
||||
|
||||
inherits(Type, CallableValue);
|
||||
|
||||
/**
|
||||
* Write type into binary stream s
|
||||
* @param {type.Stream} s binary stream
|
||||
*/
|
||||
Type.prototype.write = function(s) {
|
||||
//do not write false conditional type
|
||||
if(this.opt.conditional && !this.opt.conditional())
|
||||
return this;
|
||||
|
||||
this.isWritten = true;
|
||||
|
||||
this.writeValue(s);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read type from binary stream
|
||||
* @param {type.Stream} s binary stream
|
||||
* @returns this to chain call
|
||||
*/
|
||||
Type.prototype.read = function(s) {
|
||||
//do not read false conditional type
|
||||
if(this.opt.conditional && !this.opt.conditional())
|
||||
return this;
|
||||
|
||||
if(this.opt.optional && s.availableLength() < this.size())
|
||||
return this;
|
||||
|
||||
this.isReaded = true;
|
||||
|
||||
//constant case
|
||||
if(this.opt.constant) {
|
||||
var oldValue = this.value;
|
||||
try {
|
||||
this.readValue(s);
|
||||
}
|
||||
catch(e) {
|
||||
if (e instanceof RangeError) {
|
||||
throw new error.ProtocolError("NODE_RDP_CORE_TYPE_STREAM_TOO_SMALL");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
if(oldValue !== this.value) {
|
||||
log.error('constant value mismatch ' + oldValue + ' != ' + this.value);
|
||||
throw new error.ProtocolError("NODE_RDP_CORE_TYPE_CONSTANT_VALUE_MISMATCH");
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
this.readValue(s);
|
||||
}
|
||||
catch(e) {
|
||||
if (e instanceof RangeError) {
|
||||
throw new error.ProtocolError("NODE_RDP_CORE_TYPE_STREAM_TOO_SMALL");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Size of type
|
||||
* @returns {int} Size of type
|
||||
*/
|
||||
Type.prototype.size = function() {
|
||||
if(this.opt.conditional && !this.opt.conditional())
|
||||
return 0;
|
||||
return this._size_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert type to stream
|
||||
* Usefull when you want to buffer
|
||||
* @returns {Stream}
|
||||
*/
|
||||
Type.prototype.toStream = function() {
|
||||
var result = new Stream(this.size());
|
||||
this.write(result);
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Node of Raw types
|
||||
* @param {object} obj composite object
|
||||
* @param {object} opt Type parameters
|
||||
*/
|
||||
function Component(obj, opt) {
|
||||
Type.call(this, opt);
|
||||
this.obj = obj;
|
||||
}
|
||||
|
||||
//inherit from type
|
||||
inherits(Component, Type);
|
||||
|
||||
/**
|
||||
* ignore criterion
|
||||
* @param i {string} index name in obj
|
||||
* @returns {Boolean} true if can be ignore
|
||||
*/
|
||||
Component.prototype.ignore = function(i) {
|
||||
// ignore meta information
|
||||
if(i.lastIndexOf("__", 0) === 0) {
|
||||
return true;
|
||||
}
|
||||
// ignore function
|
||||
if(typeof(this.obj[i]) === 'function') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Write each sub type into stream
|
||||
* @param {Stream} s
|
||||
*/
|
||||
Component.prototype.writeValue = function(s) {
|
||||
for(var i in this.obj) {
|
||||
if(this.ignore(i)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
this.obj[i].write(s);
|
||||
}
|
||||
catch(e) {
|
||||
log.info('during write of field ' + i);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Read each sub type into stream
|
||||
* @param {Stream} s from read stream
|
||||
*/
|
||||
Component.prototype.readValue = function(s) {
|
||||
var readStream = s;
|
||||
if(this.opt.readLength) {
|
||||
readStream = new Stream(s.buffer.slice(s.offset, s.offset + this.opt.readLength.value));
|
||||
}
|
||||
|
||||
for(var i in this.obj) {
|
||||
// ignore meta information
|
||||
if(this.ignore(i)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
this.obj[i].read(readStream);
|
||||
}
|
||||
catch(e) {
|
||||
log.info('during read of field ' + i);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// padding
|
||||
if (this.opt.readLength) {
|
||||
s.offset += this.opt.readLength.value;
|
||||
if (readStream.offset < this.opt.readLength.value) {
|
||||
log.debug('still have available data : read it as padding');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sum size of sub types
|
||||
*/
|
||||
Component.prototype._size_ = function() {
|
||||
var size = 0;
|
||||
for(var i in this.obj) {
|
||||
if(this.ignore(i)) {
|
||||
continue;
|
||||
}
|
||||
size += this.obj[i].size();
|
||||
}
|
||||
return size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Leaf of tree type
|
||||
* @param {number} value of type
|
||||
* @param {function} readBufferCallback Buffer prototype read function
|
||||
* @param {function} writeBufferCallback Buffer prototype write function
|
||||
* @param {object} opt Type parameter
|
||||
*/
|
||||
function SingleType(value, nbBytes, readBufferCallback, writeBufferCallback, opt){
|
||||
Type.call(this, opt);
|
||||
this.value = value || 0;
|
||||
this.nbBytes = nbBytes;
|
||||
this.readBufferCallback = readBufferCallback;
|
||||
this.writeBufferCallback = writeBufferCallback;
|
||||
}
|
||||
|
||||
//inherit from type
|
||||
inherits(SingleType, Type);
|
||||
|
||||
/**
|
||||
* Write SingleType value into stream
|
||||
* @param s
|
||||
*/
|
||||
SingleType.prototype.writeValue = function(s) {
|
||||
this.writeBufferCallback.call(s.buffer, this.value, s.offset);
|
||||
s.offset += this._size_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Read SingleType value into stream
|
||||
* @param {Stream} s from read stream
|
||||
*/
|
||||
SingleType.prototype.readValue = function(s) {
|
||||
this.value = this.readBufferCallback.call(s.buffer, s.offset);
|
||||
s.offset += this._size_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Size of single type
|
||||
* @returns Size of single type
|
||||
*/
|
||||
SingleType.prototype._size_ = function() {
|
||||
return this.nbBytes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Integer on 1 byte
|
||||
* @param {number | function} value of type
|
||||
* @param {object} opt Type parameter
|
||||
* @returns
|
||||
*/
|
||||
function UInt8(value, opt) {
|
||||
SingleType.call(this, value, 1, Buffer.prototype.readUInt8, Buffer.prototype.writeUInt8, opt);
|
||||
}
|
||||
|
||||
//inherit from type
|
||||
inherits(UInt8, SingleType);
|
||||
|
||||
/**
|
||||
* Integer on 2 bytes in Little Endian
|
||||
* @param {number | function} value to write or compare if constant
|
||||
* @param {object} opt Type parameter
|
||||
* @returns
|
||||
*/
|
||||
function UInt16Le(value, opt) {
|
||||
SingleType.call(this, value, 2, Buffer.prototype.readUInt16LE, Buffer.prototype.writeUInt16LE, opt);
|
||||
}
|
||||
|
||||
//inherit from type
|
||||
inherits(UInt16Le, SingleType);
|
||||
|
||||
/**
|
||||
* Integer on 2 bytes in Big Endian
|
||||
* @param {number | function} value to write or compare if constant
|
||||
* @param {object} opt Type parameter
|
||||
* @returns
|
||||
*/
|
||||
function UInt16Be(value, opt) {
|
||||
SingleType.call(this, value, 2, Buffer.prototype.readUInt16BE, Buffer.prototype.writeUInt16BE, opt);
|
||||
}
|
||||
|
||||
//inherit from type
|
||||
inherits(UInt16Be, SingleType);
|
||||
|
||||
/**
|
||||
* Integer on 4 bytes in Little Endian
|
||||
* @param {number | function} value to write or compare if constant
|
||||
* @param {object} opt Type parameter
|
||||
* @returns
|
||||
*/
|
||||
function UInt32Le(value, opt) {
|
||||
SingleType.call(this, value, 4, Buffer.prototype.readUInt32LE, Buffer.prototype.writeUInt32LE, opt);
|
||||
}
|
||||
|
||||
//inherit from type
|
||||
inherits(UInt32Le, SingleType);
|
||||
|
||||
/**
|
||||
* Integer on 4 bytes in Big Endian
|
||||
* @param {number | function} value to write or compare if constant
|
||||
* @param {object} opt Type parameter
|
||||
* @returns
|
||||
*/
|
||||
function UInt32Be(value, opt) {
|
||||
SingleType.call(this, value, 4, Buffer.prototype.readUInt32BE, Buffer.prototype.writeUInt32BE, opt);
|
||||
}
|
||||
|
||||
//inherit from type
|
||||
inherits(UInt32Be, SingleType);
|
||||
|
||||
/**
|
||||
* @param value {Buffer} javascript source string
|
||||
* @param opt {object} type options
|
||||
* .readLength {type} length for reading operation
|
||||
* @returns {type.BinaryString}
|
||||
*/
|
||||
function BinaryString(value, opt) {
|
||||
Type.call(this, opt);
|
||||
this.value = value || Buffer.alloc(0);
|
||||
}
|
||||
|
||||
//inherit from type
|
||||
inherits(BinaryString, Type);
|
||||
|
||||
/**
|
||||
* Write value into string
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
BinaryString.prototype.writeValue = function(s) {
|
||||
this.value.copy(s.buffer, s.offset);
|
||||
s.offset += this._size_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Read string from offset to read length if specified or end of stream
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
BinaryString.prototype.readValue = function(s) {
|
||||
if(this.opt.readLength) {
|
||||
this.value = s.buffer.slice(s.offset, s.offset + this.opt.readLength.value);
|
||||
}
|
||||
else {
|
||||
this.value = s.buffer.slice(s.offset);
|
||||
}
|
||||
s.offset += this._size_();
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {integer} length of string
|
||||
*/
|
||||
BinaryString.prototype._size_ = function() {
|
||||
return this.value.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dynamic built type depend on factory function
|
||||
* @param message {object} parent object
|
||||
* @param field {string} name of object field
|
||||
* @param factory {function} factory use to built new type
|
||||
* @param opt {object} type options
|
||||
*/
|
||||
function Factory(factory, opt) {
|
||||
Type.call(this, opt);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
//inherit from type
|
||||
inherits(Factory, Type);
|
||||
|
||||
/**
|
||||
* build type and write into stream
|
||||
* @param s {Stream} input stream
|
||||
*/
|
||||
Factory.prototype.writeValue = function(s) {
|
||||
this.factory(s);
|
||||
};
|
||||
|
||||
/**
|
||||
* build type and read from stream
|
||||
* @param s {Stream} input stream
|
||||
*/
|
||||
Factory.prototype.readValue = function(s) {
|
||||
this.factory(s);
|
||||
};
|
||||
|
||||
/**
|
||||
* must be never called
|
||||
*/
|
||||
Factory.prototype._size_ = function() {
|
||||
throw new error.FatalError('NODE_RDP_CORE_TYPE_FACTORY_TYPE_HAVE_NO_SIZE');
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
Stream : Stream,
|
||||
Component : Component,
|
||||
UInt8 : UInt8,
|
||||
UInt16Le : UInt16Le,
|
||||
UInt16Be : UInt16Be,
|
||||
UInt32Le : UInt32Le,
|
||||
UInt32Be : UInt32Be,
|
||||
BinaryString : BinaryString,
|
||||
CallableValue : CallableValue,
|
||||
Factory : Factory
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var protocol = require('./protocol');
|
||||
module.exports = {
|
||||
createClient : protocol.rdp.createClient,
|
||||
createServer : protocol.rdp.createServer
|
||||
};
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../core').type;
|
||||
var log = require('../core').log;
|
||||
var x509 = require('../security').x509;
|
||||
var rsa = require('../security').rsa;
|
||||
var asn1 = require('../asn1');
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240521.aspx
|
||||
*/
|
||||
var CertificateType = {
|
||||
CERT_CHAIN_VERSION_1 : 0x00000001,
|
||||
CERT_CHAIN_VERSION_2 : 0x00000002
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240520.aspx
|
||||
* @returns
|
||||
*/
|
||||
function rsaPublicKey(opt) {
|
||||
var self = {
|
||||
magic : new type.UInt32Le(0x31415352, { constant : true }),
|
||||
keylen : new type.UInt32Le(function() {
|
||||
return self.modulus.size() + self.paddinf.size();
|
||||
}),
|
||||
bitlen : new type.UInt32Le(function() {
|
||||
return (self.keylen.value - 8) * 8;
|
||||
}),
|
||||
datalen : new type.UInt32Le(function() {
|
||||
return (self.bitlen.value / 8) - 1;
|
||||
}),
|
||||
pubExp : new type.UInt32Le(),
|
||||
modulus : new type.BinaryString(null, { readLength : new type.CallableValue(function() {
|
||||
return self.keylen.value - 8;
|
||||
}) }),
|
||||
padding : new type.BinaryString(Buffer.from(Array(8 + 1).join('\x00')), { readLength : new type.CallableValue(8) })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* http://msdn.microsoft.com/en-us/library/cc240519.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function proprietaryCertificate() {
|
||||
var self = {
|
||||
__TYPE__ : CertificateType.CERT_CHAIN_VERSION_1,
|
||||
dwSigAlgId : new type.UInt32Le(0x00000001, { constant : true }),
|
||||
dwKeyAlgId : new type.UInt32Le(0x00000001, { constant : true }),
|
||||
wPublicKeyBlobType : new type.UInt16Le(0x0006, { constant : true }),
|
||||
wPublicKeyBlobLen : new type.UInt16Le(function() {
|
||||
return self.PublicKeyBlob.size();
|
||||
}),
|
||||
PublicKeyBlob : rsaPublicKey({ readLength : new type.CallableValue(function() {
|
||||
return self.wPublicKeyBlobLen.value;
|
||||
}) }),
|
||||
wSignatureBlobType : new type.UInt16Le(0x0008, { constant : true }),
|
||||
wSignatureBlobLen : new type.UInt16Le(function() {
|
||||
return self.SignatureBlob.size() + self.padding.size();
|
||||
}),
|
||||
SignatureBlob : new type.BinaryString(null, { readLength : new type.CallableValue(function() {
|
||||
return self.wSignatureBlobLen.value - self.padding.size;
|
||||
}) }),
|
||||
padding : new type.BinaryString(Array(8 + 1).join('\x00'), { readLength : new type.CallableValue(8) }),
|
||||
/**
|
||||
* @return {object} rsa.publicKey
|
||||
*/
|
||||
getPublicKey : function() {
|
||||
return rsa.publicKey(self.PublicKeyBlob.obj.modulus.value, self.PublicKeyBlob.obj.pubExp.value);
|
||||
}
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* For x509 certificate
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241911.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function certBlob() {
|
||||
var self = {
|
||||
cbCert : new type.UInt32Le(function() {
|
||||
return self.abCert.size();
|
||||
}),
|
||||
abCert : new type.BinaryString(null, { readLength : new type.CallableValue(function() {
|
||||
return self.cbCert.value;
|
||||
}) })
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* x509 certificate chain
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241910.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function x509CertificateChain() {
|
||||
var self = {
|
||||
__TYPE__ : CertificateType.CERT_CHAIN_VERSION_2,
|
||||
NumCertBlobs : new type.UInt32Le(),
|
||||
CertBlobArray : new type.Factory(function(s) {
|
||||
self.CertBlobArray = new type.Component([]);
|
||||
for(var i = 0; i < self.NumCertBlobs.value; i++) {
|
||||
self.CertBlobArray.obj.push(certBlob().read(s));
|
||||
}
|
||||
}),
|
||||
padding : new type.BinaryString(null, { readLength : new type.CallableValue(function() {
|
||||
return 8 + 4 * self.NumCertBlobs.value;
|
||||
}) }),
|
||||
/**
|
||||
* @return {object} {n : modulus{bignum}, e : publicexponent{integer}
|
||||
*/
|
||||
getPublicKey : function(){
|
||||
var cert = x509.X509Certificate().decode(new type.Stream(self.CertBlobArray.obj[self.CertBlobArray.obj.length - 1].obj.abCert.value), asn1.ber);
|
||||
var publikeyStream = new type.Stream(cert.value.tbsCertificate.value.subjectPublicKeyInfo.value.subjectPublicKey.toBuffer());
|
||||
var asn1PublicKey = x509.RSAPublicKey().decode(publikeyStream, asn1.ber);
|
||||
return rsa.publicKey(asn1PublicKey.value.modulus.value, asn1PublicKey.value.publicExponent.value);
|
||||
}
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
function certificate() {
|
||||
var self = {
|
||||
dwVersion : new type.UInt32Le(function() {
|
||||
return self.certData.__TYPE__;
|
||||
}),
|
||||
certData : new type.Factory(function(s) {
|
||||
switch(self.dwVersion.value & 0x7fffffff) {
|
||||
case CertificateType.CERT_CHAIN_VERSION_1:
|
||||
log.debug('read proprietary certificate');
|
||||
self.certData = proprietaryCertificate().read(s);
|
||||
break;
|
||||
case CertificateType.CERT_CHAIN_VERSION_2:
|
||||
log.debug('read x.509 certificate chain');
|
||||
self.certData = x509CertificateChain().read(s);
|
||||
break;
|
||||
default:
|
||||
log.error('unknown cert type ' + self.dwVersion.value & 0x7fffffff);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
CertificateType : CertificateType,
|
||||
certificate : certificate
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var TPKT = require('./tpkt');
|
||||
var x224 = require('./x224');
|
||||
var t125 = require('./t125');
|
||||
var rdp = require('./rdp');
|
||||
|
||||
module.exports = {
|
||||
TPKT : TPKT,
|
||||
x224 : x224,
|
||||
t125 : t125,
|
||||
rdp : rdp
|
||||
};
|
|
@ -1,8 +1,223 @@
|
|||
const inherits = require('util').inherits;
|
||||
const type = require('../core').type;
|
||||
const events = require('events');
|
||||
const crypto = require('crypto');
|
||||
const forge = require('node-forge');
|
||||
const asn1 = forge.asn1;
|
||||
const pki = forge.pki;
|
||||
|
||||
/**
|
||||
* NLA layer of rdp stack
|
||||
*/
|
||||
function NLA(transport, nlaCompletedFunc, domain, user, password) {
|
||||
// Get NTLM ready
|
||||
const ntlm = Create_Ntlm();
|
||||
ntlm.domain = domain;
|
||||
ntlm.completedFunc = nlaCompletedFunc;
|
||||
ntlm.user = user;
|
||||
ntlm.password = password;
|
||||
ntlm.response_key_nt = ntowfv2(ntlm.password, ntlm.user, ntlm.domain);
|
||||
ntlm.response_key_lm = lmowfv2(ntlm.password, ntlm.user, ntlm.domain);
|
||||
this.ntlm = ntlm;
|
||||
this.state = 1;
|
||||
|
||||
// Get transport ready
|
||||
this.transport = transport;
|
||||
// Wait 2 bytes
|
||||
this.transport.expect(2);
|
||||
// Next state is receive header
|
||||
var self = this;
|
||||
|
||||
this.oldDataListeners = this.transport.listeners('data');
|
||||
this.oldCloseListeners = this.transport.listeners('close');
|
||||
this.oldErrorListeners = this.transport.listeners('error');
|
||||
|
||||
// Unhook the previous transport handler
|
||||
this.transport.removeAllListeners('data');
|
||||
this.transport.removeAllListeners('close');
|
||||
this.transport.removeAllListeners('error');
|
||||
|
||||
// Hook this module as the transport handler
|
||||
this.transport.once('data', function (s) {
|
||||
self.recvHeader(s);
|
||||
}).on('close', function () {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('close'); // Errors occur when NLA authentication fails, for now, just close.
|
||||
//self.emit('error', err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* inherit from a packet layer
|
||||
*/
|
||||
inherits(NLA, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Receive correct packet as expected
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
NLA.prototype.recvHeader = function (s) {
|
||||
//console.log('NLA - recvHeader', s);
|
||||
var self = this;
|
||||
var derType = new type.UInt8().read(s).value;
|
||||
var derLen = new type.UInt8().read(s).value;
|
||||
self.buffers = [ s.buffer ];
|
||||
|
||||
if (derLen < 128) {
|
||||
// wait for the entire data block
|
||||
this.transport.expect(derLen);
|
||||
this.transport.once('data', function (s) { self.recvData(s); });
|
||||
} else {
|
||||
// wait for the header size
|
||||
this.transport.expect(derLen - 128);
|
||||
this.transport.once('data', function (s) { self.recvHeaderSize(s); });
|
||||
}
|
||||
|
||||
//console.log('NLA - DER', derType, derLen);
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive correct packet as expected
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
NLA.prototype.recvHeaderSize = function (s) {
|
||||
//console.log('NLA - recvHeaderSize', s.buffer.length);
|
||||
var self = this;
|
||||
self.buffers.push(s.buffer);
|
||||
if (s.buffer.length == 1) {
|
||||
// wait for the entire data block
|
||||
var derLen = s.buffer.readUInt8(0);
|
||||
this.transport.expect(derLen);
|
||||
this.transport.once('data', function (s) { self.recvData(s); });
|
||||
} else if (s.buffer.length == 2) {
|
||||
// wait for the entire data block
|
||||
var derLen = s.buffer.readUInt16BE(0);
|
||||
this.transport.expect(derLen);
|
||||
this.transport.once('data', function (s) { self.recvData(s); });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive correct packet as expected
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
NLA.prototype.recvData = function (s) {
|
||||
//console.log('NLA - recvData', s.buffer.length);
|
||||
var self = this;
|
||||
self.buffers.push(s.buffer);
|
||||
var entireBuffer = Buffer.concat(self.buffers);
|
||||
//console.log('entireBuffer', entireBuffer.toString('hex'));
|
||||
|
||||
// We have a full ASN1 data block, decode it now
|
||||
const der = asn1.fromDer(entireBuffer.toString('binary'));
|
||||
const derNum = der.value[0].value[0].value.charCodeAt(0);
|
||||
//console.log('NLA - Number', derNum);
|
||||
|
||||
if (derNum == 6) {
|
||||
if (this.state == 1) {
|
||||
const derBuffer = Buffer.from(der.value[1].value[0].value[0].value[0].value[0].value, 'binary');
|
||||
const client_challenge = read_challenge_message(this.ntlm, derBuffer);
|
||||
self.security_interface = build_security_interface(this.ntlm);
|
||||
const peer_cert = this.transport.secureSocket.getPeerCertificate();
|
||||
const challenge = create_ts_authenticate(client_challenge, self.security_interface.gss_wrapex(peer_cert.pubkey.slice(24)));
|
||||
this.ntlm.publicKeyDer = peer_cert.pubkey.slice(24);
|
||||
this.send(challenge);
|
||||
this.state = 2;
|
||||
} else if (this.state == 2) {
|
||||
const derBuffer = Buffer.from(der.value[1].value[0].value, 'binary');
|
||||
const publicKeyDer = self.security_interface.gss_unwrapex(derBuffer);
|
||||
|
||||
// Check that the public key is identical except the first byte which is the DER encoding type.
|
||||
if (!this.ntlm.publicKeyDer.slice(1).equals(publicKeyDer.slice(1))) { console.log('RDP man-in-the-middle detected.'); close(); return; }
|
||||
delete this.ntlm.publicKeyDer; // Clean this up, we don't need it anymore.
|
||||
|
||||
var xdomain, xuser, xpassword;
|
||||
if (this.ntlm.is_unicode) {
|
||||
xdomain = toUnicode(this.ntlm.domain);
|
||||
xuser = toUnicode(this.ntlm.user);
|
||||
xpassword = toUnicode(this.ntlm.password);
|
||||
} else {
|
||||
xdomain = Buffer.from(this.ntlm.domain, 'utf8');
|
||||
xuser = Buffer.from(this.ntlm.user, 'utf8');
|
||||
xpassword = Buffer.from(this.ntlm.password, 'utf8');
|
||||
}
|
||||
|
||||
const credentials = create_ts_authinfo(self.security_interface.gss_wrapex(create_ts_credentials(xdomain, xuser, xpassword)));
|
||||
this.send(credentials);
|
||||
|
||||
// Rehook the previous transport handler
|
||||
this.transport.removeAllListeners('data');
|
||||
this.transport.removeAllListeners('close');
|
||||
this.transport.removeAllListeners('error');
|
||||
|
||||
for (var i in this.oldDataListeners) { this.transport.once('data', this.oldDataListeners[i]); }
|
||||
for (var i in this.oldCloseListeners) { this.transport.on('close', this.oldCloseListeners[i]); }
|
||||
for (var i in this.oldErrorListeners) { this.transport.on('error', this.oldErrorListeners[i]); }
|
||||
|
||||
// Done!
|
||||
this.transport.expect(2);
|
||||
this.state = 3;
|
||||
this.ntlm.completedFunc();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Receive next block of data
|
||||
this.transport.expect(2);
|
||||
this.transport.once('data', function (s) { self.recvHeader(s); });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send message throught NLA layer
|
||||
* @param message {type.*}
|
||||
*/
|
||||
NLA.prototype.send = function (message) {
|
||||
this.transport.sendBuffer(message);
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
NLA.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
|
||||
NLA.prototype.sendNegotiateMessage = function () {
|
||||
// Create create_ts_request
|
||||
this.ntlm.negotiate_message = create_negotiate_message();
|
||||
const asn1obj =
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(2)),
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, this.ntlm.negotiate_message.toString('binary'))
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
// Serialize an ASN.1 object to DER format
|
||||
this.send(Buffer.from(asn1.toDer(asn1obj).data, 'binary'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = NLA;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const NegotiateFlags = {
|
||||
NtlmsspNegociate56: 0x80000000,
|
||||
NtlmsspNegociateKeyExch: 0x40000000,
|
||||
|
@ -171,10 +386,10 @@ function build_security_interface(ntlm) {
|
|||
const encrypted_data = obj.encrypt.update(data);
|
||||
const signature = mac(obj.encrypt, obj.signing_key, obj.seq_num, data);
|
||||
obj.seq_num++;
|
||||
return Buffer.concat([ signature, encrypted_data ] );
|
||||
return Buffer.concat([signature, encrypted_data]);
|
||||
}
|
||||
|
||||
obj.gss_unwrapex = function(data) {
|
||||
obj.gss_unwrapex = function (data) {
|
||||
const version = data.readInt32LE(0);
|
||||
const checksum = data.slice(4, 12);
|
||||
const seqnum = data.readInt32LE(12);
|
||||
|
@ -183,9 +398,9 @@ function build_security_interface(ntlm) {
|
|||
const plaintext_checksum = obj.decrypt.update(checksum);
|
||||
const seqnumbuf = Buffer.alloc(4);
|
||||
seqnumbuf.writeInt32LE(seqnum, 0);
|
||||
const computed_checksum = hmac_md5(obj.verify_key, Buffer.concat([ seqnumbuf, plaintext_payload ])).slice(0, 8);
|
||||
const computed_checksum = hmac_md5(obj.verify_key, Buffer.concat([seqnumbuf, plaintext_payload])).slice(0, 8);
|
||||
if (!plaintext_checksum.equals(computed_checksum)) { console.log("Invalid checksum on NTLMv2"); }
|
||||
return plaintext_payload.toString();
|
||||
return plaintext_payload;
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
@ -220,19 +435,19 @@ function authenticate_message(lm_challenge_response, nt_challenge_response, doma
|
|||
buf.writeInt32LE(3, 8); // MessageType
|
||||
buf.writeInt16LE(lm_challenge_response.length, 12); // LmChallengeResponseLen
|
||||
buf.writeInt16LE(lm_challenge_response.length, 14); // LmChallengeResponseMaxLen
|
||||
if (lm_challenge_response.length > 0) { buf.writeInt32LE(offset, 16); } // LmChallengeResponseBufferOffset
|
||||
buf.writeInt32LE(offset, 16); // LmChallengeResponseBufferOffset
|
||||
buf.writeInt16LE(nt_challenge_response.length, 20); // NtChallengeResponseLen
|
||||
buf.writeInt16LE(nt_challenge_response.length, 22); // NtChallengeResponseMaxLen
|
||||
if (nt_challenge_response.length > 0) { buf.writeInt32LE(offset + lm_challenge_response.length, 24); } // NtChallengeResponseBufferOffset
|
||||
buf.writeInt32LE(offset + lm_challenge_response.length, 24); // NtChallengeResponseBufferOffset
|
||||
buf.writeInt16LE(domain.length, 28); // DomainNameLen
|
||||
buf.writeInt16LE(domain.length, 30); // DomainNameMaxLen
|
||||
if (domain.length > 0) { buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length, 32); } // DomainNameBufferOffset
|
||||
buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length, 32); // DomainNameBufferOffset
|
||||
buf.writeInt16LE(user.length, 36); // UserNameLen
|
||||
buf.writeInt16LE(user.length, 38); // UserNameMaxLen
|
||||
if (user.length > 0) { buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length, 40); } // UserNameBufferOffset
|
||||
buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length, 40); // UserNameBufferOffset
|
||||
buf.writeInt16LE(workstation.length, 44); // WorkstationLen
|
||||
buf.writeInt16LE(workstation.length, 46); // WorkstationMaxLen
|
||||
if (workstation.length > 0) { buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length + user.length, 48); } // WorkstationBufferOffset
|
||||
buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length + user.length, 48); // WorkstationBufferOffset
|
||||
buf.writeInt16LE(encrypted_random_session_key.length, 52); // EncryptedRandomSessionLen
|
||||
buf.writeInt16LE(encrypted_random_session_key.length, 54); // EncryptedRandomSessionMaxLen
|
||||
buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length + user.length + workstation.length, 56); // EncryptedRandomSessionBufferOffset
|
||||
|
@ -248,7 +463,81 @@ function authenticate_message(lm_challenge_response, nt_challenge_response, doma
|
|||
return [buf, payload];
|
||||
}
|
||||
|
||||
function create_ts_authinfo(auth_info) {
|
||||
asn1obj =
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(2)),
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, auth_info.toString('binary'))
|
||||
])
|
||||
]);
|
||||
return Buffer.from(asn1.toDer(asn1obj).data, 'binary');
|
||||
}
|
||||
|
||||
function create_ts_credentials(domain, user, password) {
|
||||
var asn1obj =
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, domain.toString('binary'))
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, user.toString('binary'))
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, password.toString('binary'))
|
||||
])
|
||||
]);
|
||||
const ts_password_cred_encoded = asn1.toDer(asn1obj).data;
|
||||
asn1obj =
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(1)),
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, ts_password_cred_encoded)
|
||||
])
|
||||
]);
|
||||
return Buffer.from(asn1.toDer(asn1obj).data, 'binary');
|
||||
}
|
||||
|
||||
function create_ts_authenticate(nego, pub_key_auth) {
|
||||
// Create create_ts_request
|
||||
const asn1obj =
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(2)),
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, nego.toString('binary'))
|
||||
])
|
||||
])
|
||||
])
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 3, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, pub_key_auth.toString('binary'))
|
||||
]),
|
||||
]);
|
||||
|
||||
// Serialize an ASN.1 object to DER format
|
||||
return Buffer.from(asn1.toDer(asn1obj).data, 'binary');
|
||||
}
|
||||
|
||||
function read_challenge_message(ntlm, derBuffer) {
|
||||
|
||||
//console.log('ntlm.negotiate_message', ntlm.negotiate_message.toString('hex'));
|
||||
//ntlm.negotiate_message = Buffer.from('4e544c4d53535000010000003582086000000000000000000000000000000000', 'hex');
|
||||
|
||||
// ********
|
||||
//ntlm.exported_session_key = Buffer.from('9a1ed052e932834a311daf90c2750219', 'hex'); // *************************
|
||||
//derBuffer = Buffer.from('4e544c4d53535000020000000e000e003800000035828a6259312ef59a4517dd000000000000000058005800460000000a00614a0000000f430045004e005400520041004c0002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c00070008007b7b3bee9e5ad80100000000', 'hex');
|
||||
|
||||
//console.log("YST: read_challenge_message1: ", derBuffer.toString('hex'));
|
||||
|
||||
const headerSignature = derBuffer.slice(0, 8);
|
||||
if (headerSignature.toString('hex') != '4e544c4d53535000') { console.log('BAD SIGNATURE'); }
|
||||
const messageType = derBuffer.readInt32LE(8);
|
||||
|
@ -264,22 +553,40 @@ function read_challenge_message(ntlm, derBuffer) {
|
|||
const targetInfoLenMax = derBuffer.readInt16LE(42);
|
||||
const targetInfoBufferOffset = derBuffer.readInt32LE(44);
|
||||
const targetName = derBuffer.slice(targetNameBufferOffset, targetNameBufferOffset + targetNameLen);
|
||||
const targetInfoBuf = derBuffer.slice(targetInfoBufferOffset, targetInfoBufferOffset + targetInfoLen);
|
||||
const targetInfo = decodeTargetInfo(derBuffer.slice(targetInfoBufferOffset, targetInfoBufferOffset + targetInfoLen));
|
||||
const timestamp = targetInfo[7];
|
||||
//const timestamp = Buffer.from('7b7b3bee9e5ad801', 'hex'); // **************
|
||||
if (timestamp == null) { console.log('NO TIMESTAMP'); }
|
||||
const clientChallenge = crypto.randomBytes(8);
|
||||
//const clientChallenge = Buffer.from('10aac9679ef64e66', 'hex'); // *****************************
|
||||
const response_key_nt = ntowfv2(ntlm.password, ntlm.user, ntlm.domain); // Password, Username, Domain
|
||||
const response_key_lm = lmowfv2(ntlm.password, ntlm.user, ntlm.domain); // Password, Username, Domain
|
||||
|
||||
var resp = compute_response_v2(response_key_nt, response_key_lm, serverChallenge, clientChallenge, timestamp, targetName);
|
||||
//console.log("YST: target_name:", targetInfoBuf.toString('hex'));
|
||||
//console.log("YST: timestamp:", timestamp.toString('hex'));
|
||||
//console.log('YST: client_challenge:', clientChallenge.toString('hex'));
|
||||
//console.log("YST: response_key_nt:", response_key_nt.toString('hex'));
|
||||
//console.log("YST: response_key_lm:", response_key_lm.toString('hex'));
|
||||
|
||||
var resp = compute_response_v2(response_key_nt, response_key_lm, serverChallenge, clientChallenge, timestamp, targetInfoBuf);
|
||||
const nt_challenge_response = resp[0];
|
||||
const lm_challenge_response = resp[1];
|
||||
const session_base_key = resp[2];
|
||||
|
||||
//console.log('YST: nt_challenge_response:', nt_challenge_response.toString('hex'));
|
||||
//console.log('YST: lm_challenge_response:', lm_challenge_response.toString('hex'));
|
||||
//console.log("YST: session_base_key:", session_base_key.toString('hex'));
|
||||
|
||||
const key_exchange_key = kx_key_v2(session_base_key, lm_challenge_response, serverChallenge);
|
||||
const encrypted_random_session_key = rc4k(key_exchange_key, ntlm.exported_session_key);
|
||||
|
||||
//console.log("YST: key_exchange_key:", key_exchange_key.toString('hex'));
|
||||
//console.log("YST: self.exported_session_key:", ntlm.exported_session_key.toString('hex'));
|
||||
//console.log("YST: encrypted_random_session_key:", encrypted_random_session_key.toString('hex'));
|
||||
|
||||
ntlm.is_unicode = ((negotiateFlags & 1) != 0)
|
||||
//console.log("YST: self.is_unicode: {}", ntlm.is_unicode);
|
||||
var xdomain = null;
|
||||
var xuser = null;
|
||||
if (ntlm.is_unicode) {
|
||||
|
@ -290,39 +597,28 @@ function read_challenge_message(ntlm, derBuffer) {
|
|||
xuser = Buffer.from(ntlm.user, 'utf8');
|
||||
}
|
||||
|
||||
//console.log("YST: domain:", xdomain.toString('hex'));
|
||||
//console.log("YST: user:", xuser.toString('hex'));
|
||||
|
||||
const auth_message_compute = authenticate_message(lm_challenge_response, nt_challenge_response, xdomain, xuser, zeroBuffer(0), encrypted_random_session_key, negotiateFlags);
|
||||
|
||||
// Write a tmp message to compute MIC and then include it into final message
|
||||
const tmp_final_auth_message = Buffer.concat([auth_message_compute[0], zeroBuffer(16), auth_message_compute[1]]);
|
||||
|
||||
//console.log("YST: tmp_final_auth_message: {}", tmp_final_auth_message.toString('hex'));
|
||||
|
||||
const signature = mic(ntlm.exported_session_key, ntlm.negotiate_message, derBuffer, tmp_final_auth_message);
|
||||
return Buffer.concat([auth_message_compute[0], signature, auth_message_compute[1]]);
|
||||
|
||||
//console.log("YST: signature: {}", signature.toString('hex'));
|
||||
|
||||
const r = Buffer.concat([auth_message_compute[0], signature, auth_message_compute[1]]);
|
||||
|
||||
//console.log("YST: read_challenge_message2: {}", r.toString('hex'));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// Create create_ts_request
|
||||
|
||||
const entireBuffer = Buffer.from('3081b2a003020106a181aa3081a73081a4a081a104819e4e544c4d53535000020000000e000e003800000035828a62f2290572b3cac375000000000000000058005800460000000a00614a0000000f430045004e005400520041004c0002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c0007000800afbc2c2a9256d80100000000', 'hex').toString('binary');
|
||||
|
||||
const ntml = Create_Ntlm();
|
||||
ntml.domain = "";
|
||||
ntml.user = "default";
|
||||
ntml.password = "";
|
||||
ntml.negotiate_message = Buffer.from('4e544c4d53535000010000003582086000000000000000000000000000000000', 'hex');
|
||||
|
||||
// We have a full ASN1 data block, decode it now
|
||||
const der = asn1.fromDer(entireBuffer.toString('binary'));
|
||||
const derNum = der.value[0].value[0].value.charCodeAt(0);
|
||||
const derBuffer = Buffer.from(der.value[1].value[0].value[0].value[0].value[0].value, 'binary');
|
||||
const client_challenge = read_challenge_message(ntml, derBuffer);
|
||||
|
||||
console.log('client_challenge', client_challenge.toString('hex'));
|
||||
|
||||
const NTLMv2SecurityInterface = build_security_interface(ntml);
|
||||
*/
|
||||
|
||||
|
||||
function unitTest() {
|
||||
console.log('--- Starting RDP NLA Unit Tests');
|
||||
|
||||
|
@ -387,9 +683,19 @@ function unitTest() {
|
|||
r = rc4.update(Buffer.from("bar"));
|
||||
console.log(compareArray(bufToArr(r), [201, 67, 159]) ? "RC4 1 passed." : "RC4 1 failed.");
|
||||
r = rc4.update(Buffer.from("bar"));
|
||||
console.log(compareArray(bufToArr(r), [75, 169, 19]) ? "RC4 2 passed." : "RC4 failed.");
|
||||
console.log(compareArray(bufToArr(r), [75, 169, 19]) ? "RC4 2 passed." : "RC4 2 failed.");
|
||||
|
||||
// Test create_ts_authenticate
|
||||
r = create_ts_authenticate(Buffer.from("000102", 'hex'), Buffer.from("000102", 'hex'));
|
||||
console.log(compareArray(bufToArr(r), [48, 25, 160, 3, 2, 1, 2, 161, 11, 48, 9, 48, 7, 160, 5, 4, 3, 0, 1, 2, 163, 5, 4, 3, 0, 1, 2]) ? "create_ts_authenticate passed." : "create_ts_authenticate failed.");
|
||||
|
||||
// Test test_create_ts_credentials
|
||||
r = create_ts_credentials(Buffer.from("domain"), Buffer.from("user"), Buffer.from("password"));
|
||||
console.log(compareArray(bufToArr(r), [48, 41, 160, 3, 2, 1, 1, 161, 34, 4, 32, 48, 30, 160, 8, 4, 6, 100, 111, 109, 97, 105, 110, 161, 6, 4, 4, 117, 115, 101, 114, 162, 10, 4, 8, 112, 97, 115, 115, 119, 111, 114, 100]) ? "test_create_ts_credentials passed." : "test_create_ts_credentials failed.");
|
||||
|
||||
// Test create_ts_authinfo
|
||||
r = create_ts_authinfo(Buffer.from("foo"));
|
||||
console.log(compareArray(bufToArr(r), [48, 12, 160, 3, 2, 1, 2, 162, 5, 4, 3, 102, 111, 111]) ? "create_ts_authinfo passed." : "create_ts_authinfo failed.");
|
||||
|
||||
console.log('--- RDP NLA Unit Tests Completed');
|
||||
}
|
||||
|
||||
unitTest();
|
||||
}
|
|
@ -0,0 +1,704 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../../core').type;
|
||||
var log = require('../../core').log;
|
||||
var error = require('../../core').error;
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240486.aspx
|
||||
*/
|
||||
var CapsType = {
|
||||
CAPSTYPE_GENERAL : 0x0001,
|
||||
CAPSTYPE_BITMAP : 0x0002,
|
||||
CAPSTYPE_ORDER : 0x0003,
|
||||
CAPSTYPE_BITMAPCACHE : 0x0004,
|
||||
CAPSTYPE_CONTROL : 0x0005,
|
||||
CAPSTYPE_ACTIVATION : 0x0007,
|
||||
CAPSTYPE_POINTER : 0x0008,
|
||||
CAPSTYPE_SHARE : 0x0009,
|
||||
CAPSTYPE_COLORCACHE : 0x000A,
|
||||
CAPSTYPE_SOUND : 0x000C,
|
||||
CAPSTYPE_INPUT : 0x000D,
|
||||
CAPSTYPE_FONT : 0x000E,
|
||||
CAPSTYPE_BRUSH : 0x000F,
|
||||
CAPSTYPE_GLYPHCACHE : 0x0010,
|
||||
CAPSTYPE_OFFSCREENCACHE : 0x0011,
|
||||
CAPSTYPE_BITMAPCACHE_HOSTSUPPORT : 0x0012,
|
||||
CAPSTYPE_BITMAPCACHE_REV2 : 0x0013,
|
||||
CAPSTYPE_VIRTUALCHANNEL : 0x0014,
|
||||
CAPSTYPE_DRAWNINEGRIDCACHE : 0x0015,
|
||||
CAPSTYPE_DRAWGDIPLUS : 0x0016,
|
||||
CAPSTYPE_RAIL : 0x0017,
|
||||
CAPSTYPE_WINDOW : 0x0018,
|
||||
CAPSETTYPE_COMPDESK : 0x0019,
|
||||
CAPSETTYPE_MULTIFRAGMENTUPDATE : 0x001A,
|
||||
CAPSETTYPE_LARGE_POINTER : 0x001B,
|
||||
CAPSETTYPE_SURFACE_COMMANDS : 0x001C,
|
||||
CAPSETTYPE_BITMAP_CODECS : 0x001D,
|
||||
CAPSSETTYPE_FRAME_ACKNOWLEDGE : 0x001E
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240549.aspx
|
||||
*/
|
||||
var MajorType = {
|
||||
OSMAJORTYPE_UNSPECIFIED : 0x0000,
|
||||
OSMAJORTYPE_WINDOWS : 0x0001,
|
||||
OSMAJORTYPE_OS2 : 0x0002,
|
||||
OSMAJORTYPE_MACINTOSH : 0x0003,
|
||||
OSMAJORTYPE_UNIX : 0x0004,
|
||||
OSMAJORTYPE_IOS : 0x0005,
|
||||
OSMAJORTYPE_OSX : 0x0006,
|
||||
OSMAJORTYPE_ANDROID : 0x0007
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240549.aspx
|
||||
*/
|
||||
var MinorType = {
|
||||
OSMINORTYPE_UNSPECIFIED : 0x0000,
|
||||
OSMINORTYPE_WINDOWS_31X : 0x0001,
|
||||
OSMINORTYPE_WINDOWS_95 : 0x0002,
|
||||
OSMINORTYPE_WINDOWS_NT : 0x0003,
|
||||
OSMINORTYPE_OS2_V21 : 0x0004,
|
||||
OSMINORTYPE_POWER_PC : 0x0005,
|
||||
OSMINORTYPE_MACINTOSH : 0x0006,
|
||||
OSMINORTYPE_NATIVE_XSERVER : 0x0007,
|
||||
OSMINORTYPE_PSEUDO_XSERVER : 0x0008,
|
||||
OSMINORTYPE_WINDOWS_RT : 0x0009
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240549.aspx
|
||||
*/
|
||||
var GeneralExtraFlag = {
|
||||
FASTPATH_OUTPUT_SUPPORTED : 0x0001,
|
||||
NO_BITMAP_COMPRESSION_HDR : 0x0400,
|
||||
LONG_CREDENTIALS_SUPPORTED : 0x0004,
|
||||
AUTORECONNECT_SUPPORTED : 0x0008,
|
||||
ENC_SALTED_CHECKSUM : 0x0010
|
||||
};
|
||||
|
||||
var Boolean = {
|
||||
FALSE : 0x00,
|
||||
TRUE : 0x01
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240556.aspx
|
||||
*/
|
||||
var OrderFlag = {
|
||||
NEGOTIATEORDERSUPPORT : 0x0002,
|
||||
ZEROBOUNDSDELTASSUPPORT : 0x0008,
|
||||
COLORINDEXSUPPORT : 0x0020,
|
||||
SOLIDPATTERNBRUSHONLY : 0x0040,
|
||||
ORDERFLAGS_EXTRA_FLAGS : 0x0080
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240556.aspx
|
||||
*/
|
||||
var Order = {
|
||||
TS_NEG_DSTBLT_INDEX : 0x00,
|
||||
TS_NEG_PATBLT_INDEX : 0x01,
|
||||
TS_NEG_SCRBLT_INDEX : 0x02,
|
||||
TS_NEG_MEMBLT_INDEX : 0x03,
|
||||
TS_NEG_MEM3BLT_INDEX : 0x04,
|
||||
TS_NEG_DRAWNINEGRID_INDEX : 0x07,
|
||||
TS_NEG_LINETO_INDEX : 0x08,
|
||||
TS_NEG_MULTI_DRAWNINEGRID_INDEX : 0x09,
|
||||
TS_NEG_SAVEBITMAP_INDEX : 0x0B,
|
||||
TS_NEG_MULTIDSTBLT_INDEX : 0x0F,
|
||||
TS_NEG_MULTIPATBLT_INDEX : 0x10,
|
||||
TS_NEG_MULTISCRBLT_INDEX : 0x11,
|
||||
TS_NEG_MULTIOPAQUERECT_INDEX : 0x12,
|
||||
TS_NEG_FAST_INDEX_INDEX : 0x13,
|
||||
TS_NEG_POLYGON_SC_INDEX : 0x14,
|
||||
TS_NEG_POLYGON_CB_INDEX : 0x15,
|
||||
TS_NEG_POLYLINE_INDEX : 0x16,
|
||||
TS_NEG_FAST_GLYPH_INDEX : 0x18,
|
||||
TS_NEG_ELLIPSE_SC_INDEX : 0x19,
|
||||
TS_NEG_ELLIPSE_CB_INDEX : 0x1A,
|
||||
TS_NEG_INDEX_INDEX : 0x1B
|
||||
};
|
||||
|
||||
var OrderEx = {
|
||||
ORDERFLAGS_EX_CACHE_BITMAP_REV3_SUPPORT : 0x0002,
|
||||
ORDERFLAGS_EX_ALTSEC_FRAME_MARKER_SUPPORT : 0x0004
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240563.aspx
|
||||
*/
|
||||
var InputFlags = {
|
||||
INPUT_FLAG_SCANCODES : 0x0001,
|
||||
INPUT_FLAG_MOUSEX : 0x0004,
|
||||
INPUT_FLAG_FASTPATH_INPUT : 0x0008,
|
||||
INPUT_FLAG_UNICODE : 0x0010,
|
||||
INPUT_FLAG_FASTPATH_INPUT2 : 0x0020,
|
||||
INPUT_FLAG_UNUSED1 : 0x0040,
|
||||
INPUT_FLAG_UNUSED2 : 0x0080,
|
||||
TS_INPUT_FLAG_MOUSE_HWHEEL : 0x0100
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240564.aspx
|
||||
*/
|
||||
var BrushSupport = {
|
||||
BRUSH_DEFAULT : 0x00000000,
|
||||
BRUSH_COLOR_8x8 : 0x00000001,
|
||||
BRUSH_COLOR_FULL : 0x00000002
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240565.aspx
|
||||
*/
|
||||
var GlyphSupport = {
|
||||
GLYPH_SUPPORT_NONE : 0x0000,
|
||||
GLYPH_SUPPORT_PARTIAL : 0x0001,
|
||||
GLYPH_SUPPORT_FULL : 0x0002,
|
||||
GLYPH_SUPPORT_ENCODE : 0x0003
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240550.aspx
|
||||
*/
|
||||
var OffscreenSupportLevel = {
|
||||
FALSE : 0x00000000,
|
||||
TRUE : 0x00000001
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240551.aspx
|
||||
*/
|
||||
var VirtualChannelCompressionFlag = {
|
||||
VCCAPS_NO_COMPR : 0x00000000,
|
||||
VCCAPS_COMPR_SC : 0x00000001,
|
||||
VCCAPS_COMPR_CS_8K : 0x00000002
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240552.aspx
|
||||
*/
|
||||
var SoundFlag = {
|
||||
NONE : 0x0000,
|
||||
SOUND_BEEPS_FLAG : 0x0001
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240549.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function generalCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_GENERAL,
|
||||
osMajorType : new type.UInt16Le(),
|
||||
osMinorType : new type.UInt16Le(),
|
||||
protocolVersion : new type.UInt16Le(0x0200, {constant : true}),
|
||||
pad2octetsA : new type.UInt16Le(),
|
||||
generalCompressionTypes : new type.UInt16Le(0, {constant : true}),
|
||||
extraFlags : new type.UInt16Le(),
|
||||
updateCapabilityFlag : new type.UInt16Le(0, {constant : true}),
|
||||
remoteUnshareFlag : new type.UInt16Le(0, {constant : true}),
|
||||
generalCompressionLevel : new type.UInt16Le(0, {constant : true}),
|
||||
refreshRectSupport : new type.UInt8(),
|
||||
suppressOutputSupport : new type.UInt8()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240554.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function bitmapCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_BITMAP,
|
||||
preferredBitsPerPixel : new type.UInt16Le(),
|
||||
receive1BitPerPixel : new type.UInt16Le(0x0001),
|
||||
receive4BitsPerPixel : new type.UInt16Le(0x0001),
|
||||
receive8BitsPerPixel : new type.UInt16Le(0x0001),
|
||||
desktopWidth : new type.UInt16Le(),
|
||||
desktopHeight : new type.UInt16Le(),
|
||||
pad2octets : new type.UInt16Le(),
|
||||
desktopResizeFlag : new type.UInt16Le(),
|
||||
bitmapCompressionFlag : new type.UInt16Le(0x0001, {constant : true}),
|
||||
highColorFlags : new type.UInt8(0),
|
||||
drawingFlags : new type.UInt8(),
|
||||
multipleRectangleSupport : new type.UInt16Le(0x0001, {constant : true}),
|
||||
pad2octetsB : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240556.aspx
|
||||
* @param orders {type.BinaryString|null} list of available orders
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function orderCapability(orders, opt) {
|
||||
if(orders && orders.size() !== 32) {
|
||||
throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_CAPS_BAD_ORDERS_SIZE');
|
||||
}
|
||||
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_ORDER,
|
||||
terminalDescriptor : new type.BinaryString(Buffer.from(Array(16 + 1).join('\x00'), 'binary'), {readLength : new type.CallableValue(16)}),
|
||||
pad4octetsA : new type.UInt32Le(0),
|
||||
desktopSaveXGranularity : new type.UInt16Le(1),
|
||||
desktopSaveYGranularity : new type.UInt16Le(20),
|
||||
pad2octetsA : new type.UInt16Le(0),
|
||||
maximumOrderLevel : new type.UInt16Le(1),
|
||||
numberFonts : new type.UInt16Le(),
|
||||
orderFlags : new type.UInt16Le(OrderFlag.NEGOTIATEORDERSUPPORT),
|
||||
orderSupport : orders || new type.Factory(function(s) {
|
||||
self.orderSupport = new type.BinaryString(null, {readLength : new type.CallableValue(32)}).read(s);
|
||||
}),
|
||||
textFlags : new type.UInt16Le(),
|
||||
orderSupportExFlags : new type.UInt16Le(),
|
||||
pad4octetsB : new type.UInt32Le(),
|
||||
desktopSaveSize : new type.UInt32Le(480 * 480),
|
||||
pad2octetsC : new type.UInt16Le(),
|
||||
pad2octetsD : new type.UInt16Le(),
|
||||
textANSICodePage : new type.UInt16Le(0),
|
||||
pad2octetsE : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240559.aspx
|
||||
* @param opt type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function bitmapCacheCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_BITMAPCACHE,
|
||||
pad1 : new type.UInt32Le(),
|
||||
pad2 : new type.UInt32Le(),
|
||||
pad3 : new type.UInt32Le(),
|
||||
pad4 : new type.UInt32Le(),
|
||||
pad5 : new type.UInt32Le(),
|
||||
pad6 : new type.UInt32Le(),
|
||||
cache0Entries : new type.UInt16Le(),
|
||||
cache0MaximumCellSize : new type.UInt16Le(),
|
||||
cache1Entries : new type.UInt16Le(),
|
||||
cache1MaximumCellSize : new type.UInt16Le(),
|
||||
cache2Entries : new type.UInt16Le(),
|
||||
cache2MaximumCellSize : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param isServer {boolean} true if in server mode
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function pointerCapability(isServer, opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_POINTER,
|
||||
colorPointerFlag : new type.UInt16Le(),
|
||||
colorPointerCacheSize : new type.UInt16Le(20),
|
||||
//old version of rdp doesn't support ...
|
||||
pointerCacheSize : new type.UInt16Le(null, {conditional : function() {
|
||||
return isServer || false;
|
||||
}})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240563.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function inputCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_INPUT,
|
||||
inputFlags : new type.UInt16Le(),
|
||||
pad2octetsA : new type.UInt16Le(),
|
||||
// same value as gcc.ClientCoreSettings.kbdLayout
|
||||
keyboardLayout : new type.UInt32Le(),
|
||||
// same value as gcc.ClientCoreSettings.keyboardType
|
||||
keyboardType : new type.UInt32Le(),
|
||||
// same value as gcc.ClientCoreSettings.keyboardSubType
|
||||
keyboardSubType : new type.UInt32Le(),
|
||||
// same value as gcc.ClientCoreSettings.keyboardFnKeys
|
||||
keyboardFunctionKey : new type.UInt32Le(),
|
||||
// same value as gcc.ClientCoreSettingrrs.imeFileName
|
||||
imeFileName : new type.BinaryString(Buffer.from(Array(64 + 1).join('\x00'), 'binary'), {readLength : new type.CallableValue(64)})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240564.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function brushCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_BRUSH,
|
||||
brushSupportLevel : new type.UInt32Le(BrushSupport.BRUSH_DEFAULT)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240566.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function cacheEntry(opt) {
|
||||
var self = {
|
||||
cacheEntries : new type.UInt16Le(),
|
||||
cacheMaximumCellSize : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240565.aspx
|
||||
* @param entries {type.Component} cache entries
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function glyphCapability(entries, opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_GLYPHCACHE,
|
||||
glyphCache : entries || new type.Factory(function(s) {
|
||||
self.glyphCache = new type.Component([]);
|
||||
for(var i = 0; i < 10; i++) {
|
||||
self.glyphCache.obj.push(cacheEntry().read(s));
|
||||
}
|
||||
}),
|
||||
fragCache : new type.UInt32Le(),
|
||||
// all fonts are sent with bitmap format (very expensive)
|
||||
glyphSupportLevel : new type.UInt16Le(GlyphSupport.GLYPH_SUPPORT_NONE),
|
||||
pad2octets : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240550.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function offscreenBitmapCacheCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_OFFSCREENCACHE,
|
||||
offscreenSupportLevel : new type.UInt32Le(OffscreenSupportLevel.FALSE),
|
||||
offscreenCacheSize : new type.UInt16Le(),
|
||||
offscreenCacheEntries : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240551.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function virtualChannelCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_VIRTUALCHANNEL,
|
||||
flags : new type.UInt32Le(VirtualChannelCompressionFlag.VCCAPS_NO_COMPR),
|
||||
VCChunkSize : new type.UInt32Le(null, {optional : true})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240552.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function soundCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_SOUND,
|
||||
soundFlags : new type.UInt16Le(SoundFlag.NONE),
|
||||
pad2octetsA : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240568.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function controlCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_CONTROL,
|
||||
controlFlags : new type.UInt16Le(),
|
||||
remoteDetachFlag : new type.UInt16Le(),
|
||||
controlInterest : new type.UInt16Le(0x0002),
|
||||
detachInterest : new type.UInt16Le(0x0002)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240569.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function windowActivationCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_ACTIVATION,
|
||||
helpKeyFlag : new type.UInt16Le(),
|
||||
helpKeyIndexFlag : new type.UInt16Le(),
|
||||
helpExtendedKeyFlag : new type.UInt16Le(),
|
||||
windowManagerKeyFlag : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240571.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function fontCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_FONT,
|
||||
fontSupportFlags : new type.UInt16Le(0x0001),
|
||||
pad2octets : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241564.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function colorCacheCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_COLORCACHE,
|
||||
colorTableCacheSize : new type.UInt16Le(0x0006),
|
||||
pad2octets : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240570.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function shareCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_SHARE,
|
||||
nodeId : new type.UInt16Le(),
|
||||
pad2octets : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240649.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function multiFragmentUpdate(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE,
|
||||
MaxRequestSize : new type.UInt32Le(0)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Capability wrapper packet
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240486.aspx
|
||||
* @param cap {type.Component}
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function capability(cap, opt) {
|
||||
var self = {
|
||||
capabilitySetType : new type.UInt16Le(function() {
|
||||
return self.capability.obj.__TYPE__;
|
||||
}),
|
||||
lengthCapability : new type.UInt16Le(function() {
|
||||
return new type.Component(self).size();
|
||||
}),
|
||||
capability : cap || new type.Factory(function(s) {
|
||||
switch(self.capabilitySetType.value) {
|
||||
case CapsType.CAPSTYPE_GENERAL:
|
||||
self.capability = generalCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_BITMAP:
|
||||
self.capability = bitmapCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_ORDER:
|
||||
self.capability = orderCapability(null, {readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_BITMAPCACHE:
|
||||
self.capability = bitmapCacheCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_POINTER:
|
||||
self.capability = pointerCapability(false, {readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_INPUT:
|
||||
self.capability = inputCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_BRUSH:
|
||||
self.capability = brushCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_GLYPHCACHE:
|
||||
self.capability = glyphCapability(null, {readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_OFFSCREENCACHE:
|
||||
self.capability = offscreenBitmapCacheCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_VIRTUALCHANNEL:
|
||||
self.capability = virtualChannelCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_SOUND:
|
||||
self.capability = soundCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_CONTROL:
|
||||
self.capability = controlCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_ACTIVATION:
|
||||
self.capability = windowActivationCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_FONT:
|
||||
self.capability = fontCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_COLORCACHE:
|
||||
self.capability = colorCacheCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_SHARE:
|
||||
self.capability = shareCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE:
|
||||
self.capability = multiFragmentUpdate({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
default:
|
||||
log.debug('unknown capability ' + self.capabilitySetType.value);
|
||||
self.capability = new type.BinaryString(null, {readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
CapsType : CapsType,
|
||||
MajorType : MajorType,
|
||||
MinorType : MinorType,
|
||||
GeneralExtraFlag : GeneralExtraFlag,
|
||||
Boolean : Boolean,
|
||||
OrderFlag : OrderFlag,
|
||||
Order : Order,
|
||||
OrderEx : OrderEx,
|
||||
InputFlags : InputFlags,
|
||||
BrushSupport : BrushSupport,
|
||||
GlyphSupport : GlyphSupport,
|
||||
OffscreenSupportLevel : OffscreenSupportLevel,
|
||||
VirtualChannelCompressionFlag : VirtualChannelCompressionFlag,
|
||||
SoundFlag : SoundFlag,
|
||||
generalCapability : generalCapability,
|
||||
bitmapCapability : bitmapCapability,
|
||||
orderCapability : orderCapability,
|
||||
bitmapCacheCapability : bitmapCacheCapability,
|
||||
pointerCapability : pointerCapability,
|
||||
inputCapability : inputCapability,
|
||||
brushCapability : brushCapability,
|
||||
cacheEntry : cacheEntry,
|
||||
glyphCapability : glyphCapability,
|
||||
offscreenBitmapCacheCapability : offscreenBitmapCacheCapability,
|
||||
virtualChannelCapability : virtualChannelCapability,
|
||||
soundCapability : soundCapability,
|
||||
controlCapability : controlCapability,
|
||||
windowActivationCapability : windowActivationCapability,
|
||||
fontCapability : fontCapability,
|
||||
colorCacheCapability : colorCacheCapability,
|
||||
shareCapability : shareCapability,
|
||||
multiFragmentUpdate : multiFragmentUpdate,
|
||||
capability : capability
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var events = require('events');
|
||||
var caps = require('./caps');
|
||||
var data = require('./data');
|
||||
var type = require('../../core').type;
|
||||
var log = require('../../core').log;
|
||||
|
||||
/**
|
||||
* Global channel for all graphic updates
|
||||
* capabilities exchange and input handles
|
||||
*/
|
||||
function Global(transport, fastPathTransport) {
|
||||
this.transport = transport;
|
||||
this.fastPathTransport = fastPathTransport;
|
||||
// must be init via connect event
|
||||
this.userId = 0;
|
||||
this.serverCapabilities = [];
|
||||
this.clientCapabilities = [];
|
||||
}
|
||||
|
||||
//inherit from Layer
|
||||
inherits(Global, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Send formated PDU message
|
||||
* @param message {type.Component} PDU message
|
||||
*/
|
||||
Global.prototype.sendPDU = function(message) {
|
||||
this.transport.send(data.pdu(this.userId, message));
|
||||
};
|
||||
|
||||
/**
|
||||
* Send formated Data PDU message
|
||||
* @param message {type.Component} PDU message
|
||||
*/
|
||||
Global.prototype.sendDataPDU = function(message) {
|
||||
this.sendPDU(data.dataPDU(message, this.shareId));
|
||||
};
|
||||
|
||||
/**
|
||||
* Client side of Global channel automata
|
||||
* @param transport
|
||||
*/
|
||||
function Client(transport, fastPathTransport) {
|
||||
Global.call(this, transport, fastPathTransport);
|
||||
var self = this;
|
||||
this.transport.once('connect', function(core, userId, channelId) {
|
||||
self.connect(core, userId, channelId);
|
||||
}).on('close', function() {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
if (this.fastPathTransport) {
|
||||
this.fastPathTransport.on('fastPathData', function (secFlag, s) {
|
||||
self.recvFastPath(secFlag, s);
|
||||
});
|
||||
}
|
||||
|
||||
// init client capabilities
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL] = caps.generalCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP] = caps.bitmapCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_ORDER] = caps.orderCapability(
|
||||
new type.Component([
|
||||
new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0),
|
||||
new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0),
|
||||
new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0),
|
||||
new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0)
|
||||
]));
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAPCACHE] = caps.bitmapCacheCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_POINTER] = caps.pointerCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_INPUT] = caps.inputCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_BRUSH] = caps.brushCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_GLYPHCACHE] = caps.glyphCapability(
|
||||
new type.Component([
|
||||
caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(),
|
||||
caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry()
|
||||
]));
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_OFFSCREENCACHE] = caps.offscreenBitmapCacheCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_VIRTUALCHANNEL] = caps.virtualChannelCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_SOUND] = caps.soundCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE] = caps.multiFragmentUpdate();
|
||||
}
|
||||
|
||||
// inherit from Layer
|
||||
inherits(Client, Global);
|
||||
|
||||
/**
|
||||
* connect function
|
||||
* @param gccCore {type.Component(clientCoreData)}
|
||||
*/
|
||||
Client.prototype.connect = function(gccCore, userId, channelId) {
|
||||
this.gccCore = gccCore;
|
||||
this.userId = userId;
|
||||
this.channelId = channelId;
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvDemandActivePDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
Client.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive capabilities from server
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvDemandActivePDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if (pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DEMANDACTIVEPDU) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvDemandActivePDU(s);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// store share id
|
||||
this.shareId = pdu.obj.pduMessage.obj.shareId.value;
|
||||
|
||||
// store server capabilities
|
||||
for(var i in pdu.obj.pduMessage.obj.capabilitySets.obj) {
|
||||
var cap = pdu.obj.pduMessage.obj.capabilitySets.obj[i].obj.capability;
|
||||
if(!cap.obj) {
|
||||
continue;
|
||||
}
|
||||
this.serverCapabilities[cap.obj.__TYPE__] = cap;
|
||||
}
|
||||
|
||||
this.transport.enableSecureCheckSum = !!(this.serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].obj.extraFlags.value & caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM);
|
||||
|
||||
this.sendConfirmActivePDU();
|
||||
this.sendClientFinalizeSynchronizePDU();
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerSynchronizePDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvServerSynchronizePDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
|
||||
|| pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_SYNCHRONIZE) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerSynchronizePDU(s);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerControlCooperatePDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvServerControlCooperatePDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
|
||||
|| pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_CONTROL
|
||||
|| pdu.obj.pduMessage.obj.pduData.obj.action.value !== data.Action.CTRLACTION_COOPERATE) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerControlCooperatePDU(s);
|
||||
});
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerControlGrantedPDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvServerControlGrantedPDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
|
||||
|| pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_CONTROL
|
||||
|| pdu.obj.pduMessage.obj.pduData.obj.action.value !== data.Action.CTRLACTION_GRANTED_CONTROL) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerControlGrantedPDU(s);
|
||||
});
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerFontMapPDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvServerFontMapPDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
|
||||
|| pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_FONTMAP) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerFontMapPDU(s);
|
||||
});
|
||||
}
|
||||
|
||||
this.emit('connect');
|
||||
var self = this;
|
||||
this.transport.on('data', function(s) {
|
||||
self.recvPDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Main reveive fast path
|
||||
* @param secFlag {integer}
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvFastPath = function (secFlag, s) {
|
||||
while (s.availableLength() > 0) {
|
||||
var pdu = data.fastPathUpdatePDU().read(s);
|
||||
switch (pdu.obj.updateHeader.value & 0xf) {
|
||||
case data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
|
||||
this.emit('bitmap', pdu.obj.updateData.obj.rectangles.obj);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvPDU = function(s) {
|
||||
while (s.availableLength() > 0) {
|
||||
var pdu = data.pdu().read(s);
|
||||
switch(pdu.obj.shareControlHeader.obj.pduType.value) {
|
||||
case data.PDUType.PDUTYPE_DEACTIVATEALLPDU:
|
||||
var self = this;
|
||||
this.transport.removeAllListeners('data');
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvDemandActivePDU(s);
|
||||
});
|
||||
break;
|
||||
case data.PDUType.PDUTYPE_DATAPDU:
|
||||
this.readDataPDU(pdu.obj.pduMessage)
|
||||
break;
|
||||
default:
|
||||
log.debug('ignore pdu type ' + pdu.obj.shareControlHeader.obj.pduType.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* main receive for data PDU packet
|
||||
* @param dataPDU {data.dataPDU}
|
||||
*/
|
||||
Client.prototype.readDataPDU = function (dataPDU) {
|
||||
switch(dataPDU.obj.shareDataHeader.obj.pduType2.value) {
|
||||
case data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
|
||||
break;
|
||||
case data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED:
|
||||
this.transport.close();
|
||||
break;
|
||||
case data.PDUType2.PDUTYPE2_SAVE_SESSION_INFO:
|
||||
this.emit('session');
|
||||
break;
|
||||
case data.PDUType2.PDUTYPE2_UPDATE:
|
||||
this.readUpdateDataPDU(dataPDU.obj.pduData)
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Main upadate pdu receive function
|
||||
* @param updateDataPDU
|
||||
*/
|
||||
Client.prototype.readUpdateDataPDU = function (updateDataPDU) {
|
||||
switch(updateDataPDU.obj.updateType.value) {
|
||||
case data.UpdateType.UPDATETYPE_BITMAP:
|
||||
this.emit('bitmap', updateDataPDU.obj.updateData.obj.rectangles.obj)
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* send all client capabilities
|
||||
*/
|
||||
Client.prototype.sendConfirmActivePDU = function () {
|
||||
var generalCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].obj;
|
||||
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS;
|
||||
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT;
|
||||
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED
|
||||
| caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR
|
||||
| caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM
|
||||
| caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED;
|
||||
|
||||
var bitmapCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].obj;
|
||||
bitmapCapability.preferredBitsPerPixel.value = this.gccCore.highColorDepth.value;
|
||||
bitmapCapability.desktopWidth.value = this.gccCore.desktopWidth.value;
|
||||
bitmapCapability.desktopHeight.value = this.gccCore.desktopHeight.value;
|
||||
|
||||
var orderCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].obj;
|
||||
orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT;
|
||||
|
||||
var inputCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_INPUT].obj;
|
||||
inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE;
|
||||
inputCapability.keyboardLayout = this.gccCore.kbdLayout;
|
||||
inputCapability.keyboardType = this.gccCore.keyboardType;
|
||||
inputCapability.keyboardSubType = this.gccCore.keyboardSubType;
|
||||
inputCapability.keyboardrFunctionKey = this.gccCore.keyboardFnKeys;
|
||||
inputCapability.imeFileName = this.gccCore.imeFileName;
|
||||
|
||||
var capabilities = new type.Component([]);
|
||||
for(var i in this.clientCapabilities) {
|
||||
capabilities.obj.push(caps.capability(this.clientCapabilities[i]));
|
||||
}
|
||||
|
||||
var confirmActivePDU = data.confirmActivePDU(capabilities, this.shareId);
|
||||
|
||||
this.sendPDU(confirmActivePDU);
|
||||
};
|
||||
|
||||
/**
|
||||
* send synchronize PDU
|
||||
*/
|
||||
Client.prototype.sendClientFinalizeSynchronizePDU = function() {
|
||||
this.sendDataPDU(data.synchronizeDataPDU(this.channelId));
|
||||
this.sendDataPDU(data.controlDataPDU(data.Action.CTRLACTION_COOPERATE));
|
||||
this.sendDataPDU(data.controlDataPDU(data.Action.CTRLACTION_REQUEST_CONTROL));
|
||||
this.sendDataPDU(data.fontListDataPDU());
|
||||
};
|
||||
|
||||
/**
|
||||
* Send input event as slow path input
|
||||
* @param inputEvents {array}
|
||||
*/
|
||||
Client.prototype.sendInputEvents = function (inputEvents) {
|
||||
var pdu = data.clientInputEventPDU(new type.Component(inputEvents.map(function (e) {
|
||||
return data.slowPathInputEvent(e);
|
||||
})));
|
||||
|
||||
this.sendDataPDU(pdu);
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
Client : Client
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var lic = require('./lic');
|
||||
var sec = require('./sec');
|
||||
var global = require('./global');
|
||||
var data = require('./data');
|
||||
|
||||
module.exports = {
|
||||
lic : lic,
|
||||
sec : sec,
|
||||
global : global,
|
||||
data : data
|
||||
};
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../../core').type;
|
||||
|
||||
var MessageType = {
|
||||
LICENSE_REQUEST : 0x01,
|
||||
PLATFORM_CHALLENGE : 0x02,
|
||||
NEW_LICENSE : 0x03,
|
||||
UPGRADE_LICENSE : 0x04,
|
||||
LICENSE_INFO : 0x12,
|
||||
NEW_LICENSE_REQUEST : 0x13,
|
||||
PLATFORM_CHALLENGE_RESPONSE : 0x15,
|
||||
ERROR_ALERT : 0xFF
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
*/
|
||||
var ErrorCode = {
|
||||
ERR_INVALID_SERVER_CERTIFICATE : 0x00000001,
|
||||
ERR_NO_LICENSE : 0x00000002,
|
||||
ERR_INVALID_SCOPE : 0x00000004,
|
||||
ERR_NO_LICENSE_SERVER : 0x00000006,
|
||||
STATUS_VALID_CLIENT : 0x00000007,
|
||||
ERR_INVALID_CLIENT : 0x00000008,
|
||||
ERR_INVALID_PRODUCTID : 0x0000000B,
|
||||
ERR_INVALID_MESSAGE_LEN : 0x0000000C,
|
||||
ERR_INVALID_MAC : 0x00000003
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
*/
|
||||
var StateTransition = {
|
||||
ST_TOTAL_ABORT : 0x00000001,
|
||||
ST_NO_TRANSITION : 0x00000002,
|
||||
ST_RESET_PHASE_TO_START : 0x00000003,
|
||||
ST_RESEND_LAST_MESSAGE : 0x00000004
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
||||
*/
|
||||
var BinaryBlobType = {
|
||||
BB_ANY_BLOB : 0x0000,
|
||||
BB_DATA_BLOB : 0x0001,
|
||||
BB_RANDOM_BLOB : 0x0002,
|
||||
BB_CERTIFICATE_BLOB : 0x0003,
|
||||
BB_ERROR_BLOB : 0x0004,
|
||||
BB_ENCRYPTED_DATA_BLOB : 0x0009,
|
||||
BB_KEY_EXCHG_ALG_BLOB : 0x000D,
|
||||
BB_SCOPE_BLOB : 0x000E,
|
||||
BB_CLIENT_USER_NAME_BLOB : 0x000F,
|
||||
BB_CLIENT_MACHINE_NAME_BLOB : 0x0010
|
||||
};
|
||||
|
||||
var Preambule = {
|
||||
PREAMBLE_VERSION_2_0 : 0x2,
|
||||
PREAMBLE_VERSION_3_0 : 0x3,
|
||||
EXTENDED_ERROR_MSG_SUPPORTED : 0x80
|
||||
};
|
||||
|
||||
/**
|
||||
* Binary blob to emcompass license information
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
||||
* @param blobType {BinaryBlobType.*}
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function licenseBinaryBlob(blobType) {
|
||||
blobType = blobType || BinaryBlobType.BB_ANY_BLOB;
|
||||
var self = {
|
||||
wBlobType : new type.UInt16Le(blobType, { constant : (blobType === BinaryBlobType.BB_ANY_BLOB)?false:true }),
|
||||
wBlobLen : new type.UInt16Le(function() {
|
||||
return self.blobData.size();
|
||||
}),
|
||||
blobData : new type.BinaryString(null, { readLength : new type.CallableValue(function() {
|
||||
return self.wBlobLen.value;
|
||||
})})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Error message in license PDU automata
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function licensingErrorMessage(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.ERROR_ALERT,
|
||||
dwErrorCode : new type.UInt32Le(),
|
||||
dwStateTransition : new type.UInt32Le(),
|
||||
blob : licenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* License product informations
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241915.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function productInformation() {
|
||||
var self = {
|
||||
dwVersion : new type.UInt32Le(),
|
||||
cbCompanyName : new type.UInt32Le(function() {
|
||||
return self.pbCompanyName.size();
|
||||
}),
|
||||
// may contain "Microsoft Corporation" from server microsoft
|
||||
pbCompanyName : new type.BinaryString(Buffer.from('Microsoft Corporation', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbCompanyName.value;
|
||||
})}),
|
||||
cbProductId : new type.UInt32Le(function() {
|
||||
return self.pbProductId.size();
|
||||
}),
|
||||
// may contain "A02" from microsoft license server
|
||||
pbProductId : new type.BinaryString(Buffer.from('A02', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbProductId.value;
|
||||
})})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use in license negotiation
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241917.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function scope() {
|
||||
var self = {
|
||||
scope : licenseBinaryBlob(BinaryBlobType.BB_SCOPE_BLOB)
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241916.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function scopeList() {
|
||||
var self = {
|
||||
scopeCount : new type.UInt32Le(function() {
|
||||
return self.scopeArray.length;
|
||||
}),
|
||||
scopeArray : new type.Factory(function(s) {
|
||||
self.scopeArray = new type.Component([]);
|
||||
for(var i = 0; i < self.scopeCount.value; i++) {
|
||||
self.scopeArray.obj.push(scope().read(s));
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241914.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverLicenseRequest(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.LICENSE_REQUEST,
|
||||
serverRandom : new type.BinaryString(Buffer.from(Array(32 + 1).join('\x00')), { readLength : new type.CallableValue(32) } ),
|
||||
productInfo : productInformation(),
|
||||
keyExchangeList : licenseBinaryBlob(BinaryBlobType.BB_KEY_EXCHG_ALG_BLOB),
|
||||
serverCertificate : licenseBinaryBlob(BinaryBlobType.BB_CERTIFICATE_BLOB),
|
||||
scopeList : scopeList()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241918.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientNewLicenseRequest(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.NEW_LICENSE_REQUEST,
|
||||
preferredKeyExchangeAlg : new type.UInt32Le(0x00000001, { constant : true }),
|
||||
// pure microsoft client ;-)
|
||||
// http://msdn.microsoft.com/en-us/library/1040af38-c733-4fb3-acd1-8db8cc979eda#id10
|
||||
platformId : new type.UInt32Le(0x04000000 | 0x00010000),
|
||||
clientRandom : new type.BinaryString(Buffer.from(Array(32 + 1).join('\x00')), { readLength : new type.CallableValue(32) }),
|
||||
encryptedPreMasterSecret : licenseBinaryBlob(BinaryBlobType.BB_RANDOM_BLOB),
|
||||
ClientUserName : licenseBinaryBlob(BinaryBlobType.BB_CLIENT_USER_NAME_BLOB),
|
||||
ClientMachineName : licenseBinaryBlob(BinaryBlobType.BB_CLIENT_MACHINE_NAME_BLOB)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241921.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverPlatformChallenge(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.PLATFORM_CHALLENGE,
|
||||
connectFlags : new type.UInt32Le(),
|
||||
encryptedPlatformChallenge : licenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB),
|
||||
MACData : new type.BinaryString(Buffer.from(Array(16 + 1).join('\x00')), { readLength : new type.CallableValue(16) })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241922.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientPLatformChallengeResponse(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.PLATFORM_CHALLENGE_RESPONSE,
|
||||
encryptedPlatformChallengeResponse : licenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB),
|
||||
encryptedHWID : licenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB),
|
||||
MACData : new type.BinaryString(Buffer.from(Array(16 + 1).join('\x00'), 'binary'), { readLength : new type.CallableValue(16) })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
};
|
||||
|
||||
/**
|
||||
* Global license packet
|
||||
* @param packet {type.* | null} send packet
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function licensePacket(message) {
|
||||
var self = {
|
||||
bMsgtype : new type.UInt8(function() {
|
||||
return self.licensingMessage.obj.__TYPE__;
|
||||
}),
|
||||
flag : new type.UInt8(Preambule.PREAMBLE_VERSION_3_0),
|
||||
wMsgSize : new type.UInt16Le(function() {
|
||||
return new type.Component(self).size();
|
||||
}),
|
||||
licensingMessage : message || new type.Factory(function(s) {
|
||||
switch(self.bMsgtype.value) {
|
||||
case MessageType.ERROR_ALERT:
|
||||
self.licensingMessage = licensingErrorMessage({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case MessageType.LICENSE_REQUEST:
|
||||
self.licensingMessage = serverLicenseRequest({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case MessageType.NEW_LICENSE_REQUEST:
|
||||
self.licensingMessage = clientNewLicenseRequest({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case MessageType.PLATFORM_CHALLENGE:
|
||||
self.licensingMessage = serverPlatformChallenge({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case MessageType.PLATFORM_CHALLENGE_RESPONSE:
|
||||
self.licensingMessage = clientPLatformChallengeResponse({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
default:
|
||||
log.error('unknown license message type ' + self.bMsgtype.value);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
MessageType : MessageType,
|
||||
ErrorCode : ErrorCode,
|
||||
StateTransition : StateTransition,
|
||||
licensePacket : licensePacket,
|
||||
clientNewLicenseRequest : clientNewLicenseRequest,
|
||||
clientPLatformChallengeResponse : clientPLatformChallengeResponse
|
||||
};
|
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var crypto = require('crypto');
|
||||
var events = require('events');
|
||||
var type = require('../../core').type;
|
||||
var error = require('../../core').error;
|
||||
var log = require('../../core').log;
|
||||
var gcc = require('../t125/gcc');
|
||||
var lic = require('./lic');
|
||||
var cert = require('../cert');
|
||||
var rsa = require('../../security').rsa;
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240579.aspx
|
||||
*/
|
||||
var SecurityFlag = {
|
||||
SEC_EXCHANGE_PKT : 0x0001,
|
||||
SEC_TRANSPORT_REQ : 0x0002,
|
||||
RDP_SEC_TRANSPORT_RSP : 0x0004,
|
||||
SEC_ENCRYPT : 0x0008,
|
||||
SEC_RESET_SEQNO : 0x0010,
|
||||
SEC_IGNORE_SEQNO : 0x0020,
|
||||
SEC_INFO_PKT : 0x0040,
|
||||
SEC_LICENSE_PKT : 0x0080,
|
||||
SEC_LICENSE_ENCRYPT_CS : 0x0200,
|
||||
SEC_LICENSE_ENCRYPT_SC : 0x0200,
|
||||
SEC_REDIRECTION_PKT : 0x0400,
|
||||
SEC_SECURE_CHECKSUM : 0x0800,
|
||||
SEC_AUTODETECT_REQ : 0x1000,
|
||||
SEC_AUTODETECT_RSP : 0x2000,
|
||||
SEC_HEARTBEAT : 0x4000,
|
||||
SEC_FLAGSHI_VALID : 0x8000
|
||||
};
|
||||
|
||||
/**
|
||||
* @see https://msdn.microsoft.com/en-us/library/cc240475.aspx
|
||||
*/
|
||||
var InfoFlag = {
|
||||
INFO_MOUSE : 0x00000001,
|
||||
INFO_DISABLECTRLALTDEL : 0x00000002,
|
||||
INFO_AUTOLOGON : 0x00000008,
|
||||
INFO_UNICODE : 0x00000010,
|
||||
INFO_MAXIMIZESHELL : 0x00000020,
|
||||
INFO_LOGONNOTIFY : 0x00000040,
|
||||
INFO_COMPRESSION : 0x00000080,
|
||||
INFO_ENABLEWINDOWSKEY : 0x00000100,
|
||||
INFO_REMOTECONSOLEAUDIO : 0x00002000,
|
||||
INFO_FORCE_ENCRYPTED_CS_PDU : 0x00004000,
|
||||
INFO_RAIL : 0x00008000,
|
||||
INFO_LOGONERRORS : 0x00010000,
|
||||
INFO_MOUSE_HAS_WHEEL : 0x00020000,
|
||||
INFO_PASSWORD_IS_SC_PIN : 0x00040000,
|
||||
INFO_NOAUDIOPLAYBACK : 0x00080000,
|
||||
INFO_USING_SAVED_CREDS : 0x00100000,
|
||||
INFO_AUDIOCAPTURE : 0x00200000,
|
||||
INFO_VIDEO_DISABLE : 0x00400000,
|
||||
INFO_CompressionTypeMask : 0x00001E00
|
||||
};
|
||||
|
||||
/**
|
||||
* @see https://msdn.microsoft.com/en-us/library/cc240476.aspx
|
||||
*/
|
||||
var AfInet = {
|
||||
AfInet : 0x00002,
|
||||
AF_INET6 : 0x0017
|
||||
};
|
||||
|
||||
/**
|
||||
* @see https://msdn.microsoft.com/en-us/library/cc240476.aspx
|
||||
*/
|
||||
var PerfFlag = {
|
||||
PERF_DISABLE_WALLPAPER : 0x00000001,
|
||||
PERF_DISABLE_FULLWINDOWDRAG : 0x00000002,
|
||||
PERF_DISABLE_MENUANIMATIONS : 0x00000004,
|
||||
PERF_DISABLE_THEMING : 0x00000008,
|
||||
PERF_DISABLE_CURSOR_SHADOW : 0x00000020,
|
||||
PERF_DISABLE_CURSORSETTINGS : 0x00000040,
|
||||
PERF_ENABLE_FONT_SMOOTHING : 0x00000080,
|
||||
PERF_ENABLE_DESKTOP_COMPOSITION : 0x00000100
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241992.aspx
|
||||
* @param input {Buffer} Binary data
|
||||
* @param salt {Buffer} salt for context call
|
||||
* @param salt1 {Buffer} another salt (ex : client random)
|
||||
* @param salt2 {Buffer} another salt (ex : server random)
|
||||
* @return {Buffer}
|
||||
*/
|
||||
function saltedHash(input, salt, salt1, salt2) {
|
||||
var sha1Digest = crypto.createHash('sha1');
|
||||
sha1Digest.update(input);
|
||||
sha1Digest.update(salt.slice(0, 48));
|
||||
sha1Digest.update(salt1);
|
||||
sha1Digest.update(salt2);
|
||||
|
||||
var sha1Sig = sha1Digest.digest();
|
||||
|
||||
var md5Digest = crypto.createHash('md5');
|
||||
md5Digest.update(salt.slice(0, 48));
|
||||
md5Digest.update(sha1Sig);
|
||||
return md5Digest.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key {Buffer} secret
|
||||
* @param random1 {Buffer} client random
|
||||
* @param random2 {Buffer} server random
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
function finalHash (key, random1, random2) {
|
||||
var md5Digest = crypto.createHash('md5');
|
||||
md5Digest.update(key);
|
||||
md5Digest.update(random1);
|
||||
md5Digest.update(random2);
|
||||
return md5Digest.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241992.aspx
|
||||
* @param secret {Buffer} secret
|
||||
* @param random1 {Buffer} client random
|
||||
* @param random2 {Buffer} server random
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
function masterSecret (secret, random1, random2) {
|
||||
var sh1 = saltedHash(Buffer.from('A'), secret, random1, random2);
|
||||
var sh2 = saltedHash(Buffer.from('BB'), secret, random1, random2);
|
||||
var sh3 = saltedHash(Buffer.from('CCC'), secret, random1, random2);
|
||||
|
||||
var ms = Buffer.alloc(sh1.length + sh2.length + sh3.length);
|
||||
sh1.copy(ms);
|
||||
sh2.copy(ms, sh1.length);
|
||||
sh3.copy(ms, sh1.length + sh2.length);
|
||||
return ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241995.aspx
|
||||
* @param macSaltKey {Buffer} key
|
||||
* @param data {Buffer} data
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
function macData(macSaltKey, data) {
|
||||
var salt1 = Buffer.alloc(40);
|
||||
salt1.fill(0x36);
|
||||
|
||||
var salt2 = Buffer.alloc(48);
|
||||
salt2.fill(0x5c);
|
||||
|
||||
var dataLength = new type.UInt32Le(data.length).toStream().buffer;
|
||||
|
||||
var sha1 = crypto.createHash('sha1');
|
||||
sha1.update(macSaltKey);
|
||||
sha1.update(salt1);
|
||||
sha1.update(dataLength);
|
||||
sha1.update(data);
|
||||
var sha1Digest = sha1.digest();
|
||||
|
||||
var md5 = crypto.createHash('md5');
|
||||
md5.update(macSaltKey);
|
||||
md5.update(salt2);
|
||||
md5.update(sha1Digest);
|
||||
|
||||
return md5.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* RDP client informations
|
||||
* @param extendedInfoConditional {boolean} true if RDP5+
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function rdpInfos(extendedInfoConditional) {
|
||||
var self = {
|
||||
codePage : new type.UInt32Le(),
|
||||
flag : new type.UInt32Le(InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_LOGONERRORS | InfoFlag.INFO_DISABLECTRLALTDEL | InfoFlag.INFO_ENABLEWINDOWSKEY),
|
||||
cbDomain : new type.UInt16Le(function() {
|
||||
return self.domain.size() - 2;
|
||||
}),
|
||||
cbUserName : new type.UInt16Le(function() {
|
||||
return self.userName.size() - 2;
|
||||
}),
|
||||
cbPassword : new type.UInt16Le(function() {
|
||||
return self.password.size() - 2;
|
||||
}),
|
||||
cbAlternateShell : new type.UInt16Le(function() {
|
||||
return self.alternateShell.size() - 2;
|
||||
}),
|
||||
cbWorkingDir : new type.UInt16Le(function() {
|
||||
return self.workingDir.size() - 2;
|
||||
}),
|
||||
domain : new type.BinaryString(Buffer.from('\x00', 'ucs2'),{ readLength : new type.CallableValue(function() {
|
||||
return self.cbDomain.value + 2;
|
||||
})}),
|
||||
userName : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbUserName.value + 2;
|
||||
})}),
|
||||
password : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function () {
|
||||
return self.cbPassword.value + 2;
|
||||
})}),
|
||||
alternateShell : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbAlternateShell.value + 2;
|
||||
})}),
|
||||
workingDir : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbWorkingDir.value + 2;
|
||||
})}),
|
||||
extendedInfo : rdpExtendedInfos({ conditional : extendedInfoConditional })
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* RDP client extended informations present in RDP5+
|
||||
* @param opt
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function rdpExtendedInfos(opt) {
|
||||
var self = {
|
||||
clientAddressFamily : new type.UInt16Le(AfInet.AfInet),
|
||||
cbClientAddress : new type.UInt16Le(function() {
|
||||
return self.clientAddress.size();
|
||||
}),
|
||||
clientAddress : new type.BinaryString(Buffer.from('\x00', 'ucs2'),{ readLength : new type.CallableValue(function() {
|
||||
return self.cbClientAddress;
|
||||
}) }),
|
||||
cbClientDir : new type.UInt16Le(function() {
|
||||
return self.clientDir.size();
|
||||
}),
|
||||
clientDir : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbClientDir;
|
||||
}) }),
|
||||
clientTimeZone : new type.BinaryString(Buffer.from(Array(172 + 1).join("\x00"))),
|
||||
clientSessionId : new type.UInt32Le(),
|
||||
performanceFlags : new type.UInt32Le()
|
||||
};
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Header of security header
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function securityHeader() {
|
||||
var self = {
|
||||
securityFlag : new type.UInt16Le(),
|
||||
securityFlagHi : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Security layer
|
||||
* @param transport {events.EventEmitter}
|
||||
*/
|
||||
function Sec(transport, fastPathTransport) {
|
||||
this.transport = transport;
|
||||
this.fastPathTransport = fastPathTransport;
|
||||
// init at connect event from transport layer
|
||||
this.gccClient = null;
|
||||
this.gccServer = null;
|
||||
var self = this;
|
||||
this.infos = rdpInfos(function() {
|
||||
return self.gccClient.core.rdpVersion.value === gcc.VERSION.RDP_VERSION_5_PLUS;
|
||||
});
|
||||
this.machineName = '';
|
||||
|
||||
|
||||
// basic encryption
|
||||
this.enableEncryption = false;
|
||||
|
||||
if (this.fastPathTransport) {
|
||||
this.fastPathTransport.on('fastPathData', function (secFlag, s) {
|
||||
self.recvFastPath(secFlag, s);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//inherit from Layer
|
||||
inherits(Sec, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Send message with security header
|
||||
* @param flag {integer} security flag
|
||||
* @param data {type.*} message
|
||||
*/
|
||||
Sec.prototype.sendFlagged = function(flag, data) {
|
||||
this.transport.send('global', new type.Component([
|
||||
new type.UInt16Le(flag),
|
||||
new type.UInt16Le(),
|
||||
data
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* Main send function
|
||||
* @param message {type.*} message to send
|
||||
*/
|
||||
Sec.prototype.send = function(message) {
|
||||
if (this.enableEncryption) {
|
||||
throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_SEC_ENCRYPT_NOT_IMPLEMENTED');
|
||||
}
|
||||
this.transport.send('global', message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Main receive function
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Sec.prototype.recv = function(s) {
|
||||
if (this.enableEncryption) {
|
||||
throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_SEC_ENCRYPT_NOT_IMPLEMENTED');
|
||||
}
|
||||
// not support yet basic RDP security layer
|
||||
this.emit('data', s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive fast path data
|
||||
* @param secFlag {integer} security flag
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Sec.prototype.recvFastPath = function (secFlag, s) {
|
||||
// transparent because basic RDP security layer not implemented
|
||||
this.emit('fastPathData', secFlag, s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Client security layer
|
||||
* @param transport {events.EventEmitter}
|
||||
*/
|
||||
function Client(transport, fastPathTransport) {
|
||||
Sec.call(this, transport, fastPathTransport);
|
||||
// for basic RDP layer (in futur)
|
||||
this.enableSecureCheckSum = false;
|
||||
var self = this;
|
||||
this.transport.on('connect', function(gccClient, gccServer, userId, channels) {
|
||||
self.connect(gccClient, gccServer, userId, channels);
|
||||
}).on('close', function() {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
};
|
||||
|
||||
//inherit from Layer
|
||||
inherits(Client, Sec);
|
||||
|
||||
/**
|
||||
* Connect event
|
||||
*/
|
||||
Client.prototype.connect = function(gccClient, gccServer, userId, channels) {
|
||||
//init gcc information
|
||||
this.gccClient = gccClient;
|
||||
this.gccServer = gccServer;
|
||||
this.userId = userId;
|
||||
this.channelId = channels.find(function(e) {
|
||||
if(e.name === 'global') return true;
|
||||
}).id;
|
||||
this.sendInfoPkt();
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
Client.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Send main information packet
|
||||
* VIP (very important packet) because contain credentials
|
||||
*/
|
||||
Client.prototype.sendInfoPkt = function() {
|
||||
this.sendFlagged(SecurityFlag.SEC_INFO_PKT, this.infos);
|
||||
var self = this;
|
||||
this.transport.once('global', function(s) {
|
||||
self.recvLicense(s);
|
||||
});
|
||||
};
|
||||
|
||||
function reverse(buffer) {
|
||||
var result = Buffer.alloc(buffer.length);
|
||||
for(var i = 0; i < buffer.length; i++) {
|
||||
result.writeUInt8(buffer.readUInt8(buffer.length - 1 - i), i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a valid license request
|
||||
* @param licenseRequest {object(lic.serverLicenseRequest)} license requets infos
|
||||
*/
|
||||
Client.prototype.sendClientNewLicenseRequest = function(licenseRequest) {
|
||||
log.debug('new license request');
|
||||
var serverRandom = licenseRequest.serverRandom.value;
|
||||
|
||||
// read server certificate
|
||||
var s = new type.Stream(licenseRequest.serverCertificate.obj.blobData.value);
|
||||
var certificate = cert.certificate().read(s).obj;
|
||||
var publicKey = certificate.certData.obj.getPublicKey();
|
||||
|
||||
var clientRandom = crypto.randomBytes(32);
|
||||
var preMasterSecret = crypto.randomBytes(48);
|
||||
var mSecret = masterSecret(preMasterSecret, clientRandom, serverRandom);
|
||||
var sessionKeyBlob = masterSecret(mSecret, serverRandom, clientRandom);
|
||||
|
||||
this.licenseMacSalt = sessionKeyBlob.slice(0, 16)
|
||||
this.licenseKey = finalHash(sessionKeyBlob.slice(16, 32), clientRandom, serverRandom);
|
||||
|
||||
var request = lic.clientNewLicenseRequest();
|
||||
request.obj.clientRandom.value = clientRandom;
|
||||
|
||||
var preMasterSecretEncrypted = reverse(rsa.encrypt(reverse(preMasterSecret), publicKey));
|
||||
var preMasterSecretEncryptedPadded = Buffer.alloc(preMasterSecretEncrypted.length + 8);
|
||||
preMasterSecretEncryptedPadded.fill(0);
|
||||
preMasterSecretEncrypted.copy(preMasterSecretEncryptedPadded);
|
||||
request.obj.encryptedPreMasterSecret.obj.blobData.value = preMasterSecretEncryptedPadded;
|
||||
|
||||
request.obj.ClientMachineName.obj.blobData.value = this.infos.obj.userName.value;
|
||||
request.obj.ClientUserName.obj.blobData.value = Buffer.from(this.machineName + '\x00');
|
||||
|
||||
this.sendFlagged(SecurityFlag.SEC_LICENSE_PKT, lic.licensePacket(request));
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a valid license request
|
||||
* @param platformChallenge {object(lic.serverPlatformChallenge)} platform challenge
|
||||
*/
|
||||
Client.prototype.sendClientChallengeResponse = function(platformChallenge) {
|
||||
log.debug('challenge license');
|
||||
var serverEncryptedChallenge = platformChallenge.encryptedPlatformChallenge.obj.blobData.value;
|
||||
var serverChallenge = crypto.createDecipheriv('rc4', this.licenseKey, '').update(serverEncryptedChallenge);
|
||||
if (serverChallenge.toString('ucs2') !== 'TEST\x00') {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_PDU_SEC_INVALID_LICENSE_CHALLENGE');
|
||||
}
|
||||
|
||||
var hwid = new type.Component([new type.UInt32Le(2), new type.BinaryString(crypto.randomBytes(16))]).toStream().buffer;
|
||||
|
||||
var response = lic.clientPLatformChallengeResponse();
|
||||
response.obj.encryptedPlatformChallengeResponse.obj.blobData.value = serverEncryptedChallenge;
|
||||
response.obj.encryptedHWID.obj.blobData.value = crypto.createCipheriv('rc4', this.licenseKey, '').update(hwid);
|
||||
|
||||
var sig = Buffer.alloc(serverChallenge.length + hwid.length);
|
||||
serverChallenge.copy(sig);
|
||||
hwid.copy(sig, serverChallenge.length);
|
||||
response.obj.MACData.value = macData(this.licenseMacSalt, sig);
|
||||
|
||||
this.sendFlagged(SecurityFlag.SEC_LICENSE_PKT, lic.licensePacket(response));
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive license informations
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Sec.prototype.recvLicense = function(s) {
|
||||
var header = securityHeader().read(s).obj;
|
||||
if (!(header.securityFlag.value & SecurityFlag.SEC_LICENSE_PKT)) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER');
|
||||
}
|
||||
|
||||
var message = lic.licensePacket().read(s).obj;
|
||||
// i'm accepted
|
||||
if (message.bMsgtype.value === lic.MessageType.NEW_LICENSE ||
|
||||
(message.bMsgtype.value === lic.MessageType.ERROR_ALERT
|
||||
&& message.licensingMessage.obj.dwErrorCode.value === lic.ErrorCode.STATUS_VALID_CLIENT
|
||||
&& message.licensingMessage.obj.dwStateTransition.value === lic.StateTransition.ST_NO_TRANSITION)) {
|
||||
this.emit('connect', this.gccClient.core, this.userId, this.channelId);
|
||||
var self = this;
|
||||
this.transport.on('global', function(s) {
|
||||
self.recv(s);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// server ask license request
|
||||
if (message.bMsgtype.value === lic.MessageType.LICENSE_REQUEST) {
|
||||
this.sendClientNewLicenseRequest(message.licensingMessage.obj);
|
||||
}
|
||||
|
||||
// server send challenge
|
||||
if (message.bMsgtype.value === lic.MessageType.PLATFORM_CHALLENGE) {
|
||||
this.sendClientChallengeResponse(message.licensingMessage.obj);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.emit('connect', this.gccClient.core);
|
||||
this.transport.once('global', function (s) {
|
||||
self.recvLicense(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
PerfFlag : PerfFlag,
|
||||
InfoFlag : InfoFlag,
|
||||
Client : Client
|
||||
};
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var net = require('net');
|
||||
var inherits = require('util').inherits;
|
||||
var events = require('events');
|
||||
var layer = require('../core').layer;
|
||||
var error = require('../core').error;
|
||||
var rle = require('../core').rle;
|
||||
var log = require('../core').log;
|
||||
var TPKT = require('./tpkt');
|
||||
var x224 = require('./x224');
|
||||
var t125 = require('./t125');
|
||||
var pdu = require('./pdu');
|
||||
|
||||
/**
|
||||
* decompress bitmap from RLE algorithm
|
||||
* @param bitmap {object} bitmap object of bitmap event of node-rdpjs
|
||||
*/
|
||||
function decompress (bitmap) {
|
||||
var fName = null;
|
||||
switch (bitmap.bitsPerPixel.value) {
|
||||
case 15:
|
||||
fName = 'bitmap_decompress_15';
|
||||
break;
|
||||
case 16:
|
||||
fName = 'bitmap_decompress_16';
|
||||
break;
|
||||
case 24:
|
||||
fName = 'bitmap_decompress_24';
|
||||
break;
|
||||
case 32:
|
||||
fName = 'bitmap_decompress_32';
|
||||
break;
|
||||
default:
|
||||
throw 'invalid bitmap data format';
|
||||
}
|
||||
|
||||
var input = new Uint8Array(bitmap.bitmapDataStream.value);
|
||||
var inputPtr = rle._malloc(input.length);
|
||||
var inputHeap = new Uint8Array(rle.HEAPU8.buffer, inputPtr, input.length);
|
||||
inputHeap.set(input);
|
||||
|
||||
var ouputSize = bitmap.width.value * bitmap.height.value * 4;
|
||||
var outputPtr = rle._malloc(ouputSize);
|
||||
|
||||
var outputHeap = new Uint8Array(rle.HEAPU8.buffer, outputPtr, ouputSize);
|
||||
|
||||
var res = rle.ccall(fName,
|
||||
'number',
|
||||
['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
|
||||
[outputHeap.byteOffset, bitmap.width.value, bitmap.height.value, bitmap.width.value, bitmap.height.value, inputHeap.byteOffset, input.length]
|
||||
);
|
||||
|
||||
var output = new Uint8ClampedArray(outputHeap.buffer, outputHeap.byteOffset, ouputSize);
|
||||
|
||||
rle._free(inputPtr);
|
||||
rle._free(outputPtr);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main RDP module
|
||||
*/
|
||||
function RdpClient(config) {
|
||||
config = config || {};
|
||||
this.connected = false;
|
||||
this.bufferLayer = new layer.BufferLayer(new net.Socket());
|
||||
this.tpkt = new TPKT(this.bufferLayer);
|
||||
this.x224 = new x224.Client(this.tpkt, config);
|
||||
this.mcs = new t125.mcs.Client(this.x224);
|
||||
this.sec = new pdu.sec.Client(this.mcs, this.tpkt);
|
||||
this.global = new pdu.global.Client(this.sec, this.sec);
|
||||
|
||||
// config log level
|
||||
log.level = log.Levels[config.logLevel || 'INFO'] || log.Levels.INFO;
|
||||
|
||||
// credentials
|
||||
if (config.domain) {
|
||||
this.sec.infos.obj.domain.value = Buffer.from(config.domain + '\x00', 'ucs2');
|
||||
}
|
||||
if (config.userName) {
|
||||
this.sec.infos.obj.userName.value = Buffer.from(config.userName + '\x00', 'ucs2');
|
||||
}
|
||||
if (config.password) {
|
||||
this.sec.infos.obj.password.value = Buffer.from(config.password + '\x00', 'ucs2');
|
||||
}
|
||||
if(config.workingDir) {
|
||||
this.sec.infos.obj.workingDir.value = Buffer.from(config.workingDir + '\x00', 'ucs2');
|
||||
}
|
||||
if(config.alternateShell) {
|
||||
this.sec.infos.obj.alternateShell.value = Buffer.from(config.alternateShell + '\x00', 'ucs2');
|
||||
}
|
||||
|
||||
if (config.enablePerf) {
|
||||
this.sec.infos.obj.extendedInfo.obj.performanceFlags.value =
|
||||
pdu.sec.PerfFlag.PERF_DISABLE_WALLPAPER
|
||||
| pdu.sec.PerfFlag.PERF_DISABLE_MENUANIMATIONS
|
||||
| pdu.sec.PerfFlag.PERF_DISABLE_CURSOR_SHADOW
|
||||
| pdu.sec.PerfFlag.PERF_DISABLE_THEMING
|
||||
| pdu.sec.PerfFlag.PERF_DISABLE_FULLWINDOWDRAG;
|
||||
}
|
||||
|
||||
if (config.autoLogin) {
|
||||
this.sec.infos.obj.flag.value |= pdu.sec.InfoFlag.INFO_AUTOLOGON;
|
||||
}
|
||||
|
||||
if (config.screen && config.screen.width && config.screen.height) {
|
||||
this.mcs.clientCoreData.obj.desktopWidth.value = config.screen.width;
|
||||
this.mcs.clientCoreData.obj.desktopHeight.value = config.screen.height;
|
||||
}
|
||||
|
||||
log.debug('screen ' + this.mcs.clientCoreData.obj.desktopWidth.value + 'x' + this.mcs.clientCoreData.obj.desktopHeight.value);
|
||||
|
||||
// config keyboard layout
|
||||
switch (config.locale) {
|
||||
case 'fr':
|
||||
log.debug('french keyboard layout');
|
||||
this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.FRENCH;
|
||||
break;
|
||||
case 'en':
|
||||
default:
|
||||
log.debug('english keyboard layout');
|
||||
this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.US;
|
||||
}
|
||||
|
||||
|
||||
//bind all events
|
||||
var self = this;
|
||||
this.global.on('connect', function () {
|
||||
self.connected = true;
|
||||
self.emit('connect');
|
||||
}).on('session', function () {
|
||||
self.emit('session');
|
||||
}).on('close', function () {
|
||||
self.connected = false;
|
||||
self.emit('close');
|
||||
}).on('bitmap', function (bitmaps) {
|
||||
for(var bitmap in bitmaps) {
|
||||
var bitmapData = bitmaps[bitmap].obj.bitmapDataStream.value;
|
||||
var isCompress = bitmaps[bitmap].obj.flags.value & pdu.data.BitmapFlag.BITMAP_COMPRESSION;
|
||||
|
||||
if (isCompress && config.decompress) {
|
||||
bitmapData = decompress(bitmaps[bitmap].obj);
|
||||
isCompress = false;
|
||||
}
|
||||
|
||||
self.emit('bitmap', {
|
||||
destTop : bitmaps[bitmap].obj.destTop.value,
|
||||
destLeft : bitmaps[bitmap].obj.destLeft.value,
|
||||
destBottom : bitmaps[bitmap].obj.destBottom.value,
|
||||
destRight : bitmaps[bitmap].obj.destRight.value,
|
||||
width : bitmaps[bitmap].obj.width.value,
|
||||
height : bitmaps[bitmap].obj.height.value,
|
||||
bitsPerPixel : bitmaps[bitmap].obj.bitsPerPixel.value,
|
||||
isCompress : isCompress,
|
||||
data : bitmapData
|
||||
});
|
||||
}
|
||||
}).on('error', function (err) {
|
||||
log.warn(err.code + '(' + err.message + ')\n' + err.stack);
|
||||
if (err instanceof error.FatalError) {
|
||||
throw err;
|
||||
}
|
||||
else {
|
||||
self.emit('error', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
inherits(RdpClient, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Connect RDP client
|
||||
* @param host {string} destination host
|
||||
* @param port {integer} destination port
|
||||
*/
|
||||
RdpClient.prototype.connect = function (host, port) {
|
||||
log.debug('connect to ' + host + ':' + port);
|
||||
var self = this;
|
||||
this.bufferLayer.socket.connect(port, host, function () {
|
||||
// in client mode connection start from x224 layer
|
||||
self.x224.connect();
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Close RDP client
|
||||
*/
|
||||
RdpClient.prototype.close = function () {
|
||||
if(this.connected) {
|
||||
this.global.close();
|
||||
}
|
||||
this.connected = false;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Send pointer event to server
|
||||
* @param x {integer} mouse x position
|
||||
* @param y {integer} mouse y position
|
||||
* @param button {integer} button number of mouse
|
||||
* @param isPressed {boolean} state of button
|
||||
*/
|
||||
RdpClient.prototype.sendPointerEvent = function (x, y, button, isPressed) {
|
||||
if (!this.connected)
|
||||
return;
|
||||
|
||||
var event = pdu.data.pointerEvent();
|
||||
if (isPressed) {
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN;
|
||||
}
|
||||
|
||||
switch(button) {
|
||||
case 1:
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON1;
|
||||
break;
|
||||
case 2:
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON2;
|
||||
break;
|
||||
case 3:
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON3
|
||||
break;
|
||||
default:
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE;
|
||||
}
|
||||
|
||||
event.obj.xPos.value = x;
|
||||
event.obj.yPos.value = y;
|
||||
|
||||
this.global.sendInputEvents([event]);
|
||||
};
|
||||
|
||||
/**
|
||||
* send scancode event
|
||||
* @param code {integer}
|
||||
* @param isPressed {boolean}
|
||||
* @param extended {boolenan} extended keys
|
||||
*/
|
||||
RdpClient.prototype.sendKeyEventScancode = function (code, isPressed, extended) {
|
||||
if (!this.connected)
|
||||
return;
|
||||
extended = extended || false;
|
||||
var event = pdu.data.scancodeKeyEvent();
|
||||
event.obj.keyCode.value = code;
|
||||
|
||||
if (!isPressed) {
|
||||
event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE;
|
||||
}
|
||||
|
||||
if (extended) {
|
||||
event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_EXTENDED;
|
||||
}
|
||||
|
||||
this.global.sendInputEvents([event]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send key event as unicode
|
||||
* @param code {integer}
|
||||
* @param isPressed {boolean}
|
||||
*/
|
||||
RdpClient.prototype.sendKeyEventUnicode = function (code, isPressed) {
|
||||
if (!this.connected)
|
||||
return;
|
||||
|
||||
var event = pdu.data.unicodeKeyEvent();
|
||||
event.obj.unicode.value = code;
|
||||
|
||||
if (!isPressed) {
|
||||
event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE;
|
||||
}
|
||||
this.global.sendInputEvents([event]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wheel mouse event
|
||||
* @param x {integer} mouse x position
|
||||
* @param y {integer} mouse y position
|
||||
* @param step {integer} wheel step
|
||||
* @param isNegative {boolean}
|
||||
* @param isHorizontal {boolean}
|
||||
*/
|
||||
RdpClient.prototype.sendWheelEvent = function (x, y, step, isNegative, isHorizontal) {
|
||||
if (!this.connected)
|
||||
return;
|
||||
|
||||
var event = pdu.data.pointerEvent();
|
||||
if (isHorizontal) {
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_HWHEEL;
|
||||
}
|
||||
else {
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL;
|
||||
}
|
||||
|
||||
|
||||
if (isNegative) {
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL_NEGATIVE;
|
||||
}
|
||||
|
||||
event.obj.pointerFlags.value |= (step & pdu.data.PointerFlag.WheelRotationMask)
|
||||
|
||||
event.obj.xPos.value = x;
|
||||
event.obj.yPos.value = y;
|
||||
|
||||
this.global.sendInputEvents([event]);
|
||||
}
|
||||
|
||||
function createClient(config) {
|
||||
return new RdpClient(config);
|
||||
};
|
||||
|
||||
/**
|
||||
* RDP server side protocol
|
||||
* @param config {object} configuration
|
||||
* @param socket {net.Socket}
|
||||
*/
|
||||
function RdpServer(config, socket) {
|
||||
if (!(config.key && config.cert)) {
|
||||
throw new error.FatalError('NODE_RDP_PROTOCOL_RDP_SERVER_CONFIG_MISSING', 'missing cryptographic tools')
|
||||
}
|
||||
this.connected = false;
|
||||
this.bufferLayer = new layer.BufferLayer(socket);
|
||||
this.tpkt = new TPKT(this.bufferLayer);
|
||||
this.x224 = new x224.Server(this.tpkt, config.key, config.cert);
|
||||
this.mcs = new t125.mcs.Server(this.x224);
|
||||
};
|
||||
|
||||
inherits(RdpServer, events.EventEmitter);
|
||||
|
||||
function createServer (config, next) {
|
||||
return net.createServer(function (socket) {
|
||||
next(new RdpServer(config, socket));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
createClient : createClient,
|
||||
createServer : createServer
|
||||
};
|
|
@ -0,0 +1,540 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../../core').type;
|
||||
var log = require('../../core').log;
|
||||
var error = require('../../core').error;
|
||||
var per = require('./per');
|
||||
|
||||
|
||||
var t124_02_98_oid = [ 0, 0, 20, 124, 0, 1 ];
|
||||
var h221_cs_key = "Duca";
|
||||
var h221_sc_key = "McDn";
|
||||
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240509.aspx
|
||||
*/
|
||||
var MessageType = {
|
||||
//server -> client
|
||||
SC_CORE : 0x0C01,
|
||||
SC_SECURITY : 0x0C02,
|
||||
SC_NET : 0x0C03,
|
||||
//client -> server
|
||||
CS_CORE : 0xC001,
|
||||
CS_SECURITY : 0xC002,
|
||||
CS_NET : 0xC003,
|
||||
CS_CLUSTER : 0xC004,
|
||||
CS_MONITOR : 0xC005
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var ColorDepth = {
|
||||
RNS_UD_COLOR_8BPP : 0xCA01,
|
||||
RNS_UD_COLOR_16BPP_555 : 0xCA02,
|
||||
RNS_UD_COLOR_16BPP_565 : 0xCA03,
|
||||
RNS_UD_COLOR_24BPP : 0xCA04
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var HighColor = {
|
||||
HIGH_COLOR_4BPP : 0x0004,
|
||||
HIGH_COLOR_8BPP : 0x0008,
|
||||
HIGH_COLOR_15BPP : 0x000f,
|
||||
HIGH_COLOR_16BPP : 0x0010,
|
||||
HIGH_COLOR_24BPP : 0x0018
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var Support = {
|
||||
RNS_UD_24BPP_SUPPORT : 0x0001,
|
||||
RNS_UD_16BPP_SUPPORT : 0x0002,
|
||||
RNS_UD_15BPP_SUPPORT : 0x0004,
|
||||
RNS_UD_32BPP_SUPPORT : 0x0008
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var CapabilityFlag = {
|
||||
RNS_UD_CS_SUPPORT_ERRINFO_PDU : 0x0001,
|
||||
RNS_UD_CS_WANT_32BPP_SESSION : 0x0002,
|
||||
RNS_UD_CS_SUPPORT_STATUSINFO_PDU : 0x0004,
|
||||
RNS_UD_CS_STRONG_ASYMMETRIC_KEYS : 0x0008,
|
||||
RNS_UD_CS_UNUSED : 0x0010,
|
||||
RNS_UD_CS_VALID_CONNECTION_TYPE : 0x0020,
|
||||
RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU : 0x0040,
|
||||
RNS_UD_CS_SUPPORT_NETCHAR_AUTODETECT : 0x0080,
|
||||
RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL : 0x0100,
|
||||
RNS_UD_CS_SUPPORT_DYNAMIC_TIME_ZONE : 0x0200,
|
||||
RNS_UD_CS_SUPPORT_HEARTBEAT_PDU : 0x0400
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var ConnectionType = {
|
||||
CONNECTION_TYPE_MODEM : 0x01,
|
||||
CONNECTION_TYPE_BROADBAND_LOW : 0x02,
|
||||
CONNECTION_TYPE_SATELLITE : 0x03,
|
||||
CONNECTION_TYPE_BROADBAND_HIGH : 0x04,
|
||||
CONNECTION_TYPE_WAN : 0x05,
|
||||
CONNECTION_TYPE_LAN : 0x06,
|
||||
CONNECTION_TYPE_AUTODETECT : 0x07
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var VERSION = {
|
||||
RDP_VERSION_4 : 0x00080001,
|
||||
RDP_VERSION_5_PLUS : 0x00080004
|
||||
};
|
||||
|
||||
var Sequence = {
|
||||
RNS_UD_SAS_DEL : 0xAA03
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240511.aspx
|
||||
*/
|
||||
var EncryptionMethod = {
|
||||
ENCRYPTION_FLAG_40BIT : 0x00000001,
|
||||
ENCRYPTION_FLAG_128BIT : 0x00000002,
|
||||
ENCRYPTION_FLAG_56BIT : 0x00000008,
|
||||
FIPS_ENCRYPTION_FLAG : 0x00000010
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240518.aspx
|
||||
*/
|
||||
var EncryptionLevel = {
|
||||
ENCRYPTION_LEVEL_NONE : 0x00000000,
|
||||
ENCRYPTION_LEVEL_LOW : 0x00000001,
|
||||
ENCRYPTION_LEVEL_CLIENT_COMPATIBLE : 0x00000002,
|
||||
ENCRYPTION_LEVEL_HIGH : 0x00000003,
|
||||
ENCRYPTION_LEVEL_FIPS : 0x00000004
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240513.aspx
|
||||
*/
|
||||
var ChannelOptions = {
|
||||
CHANNEL_OPTION_INITIALIZED : 0x80000000,
|
||||
CHANNEL_OPTION_ENCRYPT_RDP : 0x40000000,
|
||||
CHANNEL_OPTION_ENCRYPT_SC : 0x20000000,
|
||||
CHANNEL_OPTION_ENCRYPT_CS : 0x10000000,
|
||||
CHANNEL_OPTION_PRI_HIGH : 0x08000000,
|
||||
CHANNEL_OPTION_PRI_MED : 0x04000000,
|
||||
CHANNEL_OPTION_PRI_LOW : 0x02000000,
|
||||
CHANNEL_OPTION_COMPRESS_RDP : 0x00800000,
|
||||
CHANNEL_OPTION_COMPRESS : 0x00400000,
|
||||
CHANNEL_OPTION_SHOW_PROTOCOL : 0x00200000,
|
||||
REMOTE_CONTROL_PERSISTENT : 0x00100000
|
||||
};
|
||||
|
||||
/**
|
||||
* IBM_101_102_KEYS is the most common keyboard type
|
||||
*/
|
||||
var KeyboardType = {
|
||||
IBM_PC_XT_83_KEY : 0x00000001,
|
||||
OLIVETTI : 0x00000002,
|
||||
IBM_PC_AT_84_KEY : 0x00000003,
|
||||
IBM_101_102_KEYS : 0x00000004,
|
||||
NOKIA_1050 : 0x00000005,
|
||||
NOKIA_9140 : 0x00000006,
|
||||
JAPANESE : 0x00000007
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://technet.microsoft.com/en-us/library/cc766503%28WS.10%29.aspx
|
||||
*/
|
||||
var KeyboardLayout = {
|
||||
ARABIC : 0x00000401,
|
||||
BULGARIAN : 0x00000402,
|
||||
CHINESE_US_KEYBOARD : 0x00000404,
|
||||
CZECH : 0x00000405,
|
||||
DANISH : 0x00000406,
|
||||
GERMAN : 0x00000407,
|
||||
GREEK : 0x00000408,
|
||||
US : 0x00000409,
|
||||
SPANISH : 0x0000040a,
|
||||
FINNISH : 0x0000040b,
|
||||
FRENCH : 0x0000040c,
|
||||
HEBREW : 0x0000040d,
|
||||
HUNGARIAN : 0x0000040e,
|
||||
ICELANDIC : 0x0000040f,
|
||||
ITALIAN : 0x00000410,
|
||||
JAPANESE : 0x00000411,
|
||||
KOREAN : 0x00000412,
|
||||
DUTCH : 0x00000413,
|
||||
NORWEGIAN : 0x00000414
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240521.aspx
|
||||
*/
|
||||
var CertificateType = {
|
||||
CERT_CHAIN_VERSION_1 : 0x00000001,
|
||||
CERT_CHAIN_VERSION_2 : 0x00000002
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {type.Type} data
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function block(data) {
|
||||
var self = {
|
||||
// type of data block
|
||||
type : new type.UInt16Le(function() {
|
||||
return self.data.obj.__TYPE__;
|
||||
}),
|
||||
// length of entire packet
|
||||
length : new type.UInt16Le(function() {
|
||||
return new type.Component(self).size();
|
||||
}),
|
||||
// data block
|
||||
data : data || new type.Factory(function(s){
|
||||
var options = {
|
||||
readLength : new type.CallableValue( function () {
|
||||
return self.length.value - 4;
|
||||
})
|
||||
};
|
||||
switch(self.type.value) {
|
||||
case MessageType.SC_CORE:
|
||||
self.data = serverCoreData(options).read(s);
|
||||
break;
|
||||
case MessageType.SC_SECURITY:
|
||||
self.data = serverSecurityData(options).read(s);
|
||||
break;
|
||||
case MessageType.SC_NET:
|
||||
self.data = serverNetworkData(null, options).read(s);
|
||||
break;
|
||||
case MessageType.CS_CORE:
|
||||
self.data = clientCoreData(options).read(s);
|
||||
break;
|
||||
case MessageType.CS_SECURITY:
|
||||
self.data = clientSecurityData(options).read(s);
|
||||
break;
|
||||
case MessageType.CS_NET:
|
||||
self.data = clientNetworkData(null, options).read(s);
|
||||
break;
|
||||
default:
|
||||
log.debug("unknown gcc block type " + self.type.value);
|
||||
self.data = new type.BinaryString(null, options).read(s);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main client informations
|
||||
* keyboard
|
||||
* screen definition
|
||||
* color depth
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientCoreData(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.CS_CORE,
|
||||
rdpVersion : new type.UInt32Le(VERSION.RDP_VERSION_5_PLUS),
|
||||
desktopWidth : new type.UInt16Le(1280),
|
||||
desktopHeight : new type.UInt16Le(800),
|
||||
colorDepth : new type.UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP),
|
||||
sasSequence : new type.UInt16Le(Sequence.RNS_UD_SAS_DEL),
|
||||
kbdLayout : new type.UInt32Le(KeyboardLayout.FRENCH),
|
||||
clientBuild : new type.UInt32Le(3790),
|
||||
clientName : new type.BinaryString(Buffer.from('node-rdpjs\x00\x00\x00\x00\x00\x00', 'ucs2'), { readLength : new type.CallableValue(32) }),
|
||||
keyboardType : new type.UInt32Le(KeyboardType.IBM_101_102_KEYS),
|
||||
keyboardSubType : new type.UInt32Le(0),
|
||||
keyboardFnKeys : new type.UInt32Le(12),
|
||||
imeFileName : new type.BinaryString(Buffer.from(Array(64 + 1).join('\x00')), { readLength : new type.CallableValue(64), optional : true }),
|
||||
postBeta2ColorDepth : new type.UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP, { optional : true }),
|
||||
clientProductId : new type.UInt16Le(1, { optional : true }),
|
||||
serialNumber : new type.UInt32Le(0, { optional : true }),
|
||||
highColorDepth : new type.UInt16Le(HighColor.HIGH_COLOR_24BPP, { optional : true }),
|
||||
supportedColorDepths : new type.UInt16Le(Support.RNS_UD_15BPP_SUPPORT | Support.RNS_UD_16BPP_SUPPORT | Support.RNS_UD_24BPP_SUPPORT | Support.RNS_UD_32BPP_SUPPORT, { optional : true }),
|
||||
earlyCapabilityFlags : new type.UInt16Le(CapabilityFlag.RNS_UD_CS_SUPPORT_ERRINFO_PDU, { optional : true }),
|
||||
clientDigProductId : new type.BinaryString(Buffer.from(Array(64 + 1).join('\x00')), { optional : true, readLength : new type.CallableValue(64) }),
|
||||
connectionType : new type.UInt8(0, { optional : true }),
|
||||
pad1octet : new type.UInt8(0, { optional : true }),
|
||||
serverSelectedProtocol : new type.UInt32Le(0, { optional : true })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240517.aspx
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverCoreData(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.SC_CORE,
|
||||
rdpVersion : new type.UInt32Le(VERSION.RDP_VERSION_5_PLUS),
|
||||
clientRequestedProtocol : new type.UInt32Le(null, { optional : true }),
|
||||
earlyCapabilityFlags : new type.UInt32Le(null, { optional : true })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240511.aspx
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientSecurityData(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.CS_SECURITY,
|
||||
encryptionMethods : new type.UInt32Le(EncryptionMethod.ENCRYPTION_FLAG_40BIT | EncryptionMethod.ENCRYPTION_FLAG_56BIT | EncryptionMethod.ENCRYPTION_FLAG_128BIT),
|
||||
extEncryptionMethods : new type.UInt32Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only use for SSL (RDP security layer TODO)
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240518.aspx
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverSecurityData(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.SC_SECURITY,
|
||||
encryptionMethod : new type.UInt32Le(),
|
||||
encryptionLevel : new type.UInt32Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel definition
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function channelDef (opt) {
|
||||
var self = {
|
||||
name : new type.BinaryString(null, { readLength : new type.CallableValue(8) }),
|
||||
options : new type.UInt32Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional channel requests (sound, clipboard ...)
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientNetworkData(channelDefArray, opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.CS_NET,
|
||||
channelCount : new type.UInt32Le( function () {
|
||||
return self.channelDefArray.obj.length;
|
||||
}),
|
||||
channelDefArray : channelDefArray || new type.Factory( function (s) {
|
||||
self.channelDefArray = new type.Component([]);
|
||||
|
||||
for (var i = 0; i < self.channelCount.value; i++) {
|
||||
self.channelDefArray.obj.push(channelDef().read(s));
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param channelIds {type.Component} list of available channels
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverNetworkData (channelIds, opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.SC_NET,
|
||||
MCSChannelId : new type.UInt16Le(1003, { constant : true }),
|
||||
channelCount : new type.UInt16Le(function () {
|
||||
return self.channelIdArray.obj.length;
|
||||
}),
|
||||
channelIdArray : channelIds || new type.Factory( function (s) {
|
||||
self.channelIdArray = new type.Component([]);
|
||||
for (var i = 0; i < self.channelCount.value; i++) {
|
||||
self.channelIdArray.obj.push(new type.UInt16Le().read(s));
|
||||
}
|
||||
}),
|
||||
pad : new type.UInt16Le(null, { conditional : function () {
|
||||
return (self.channelCount.value % 2) === 1;
|
||||
}})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client or server GCC settings block
|
||||
* @param blocks {type.Component} array of gcc blocks
|
||||
* @param opt {object} options to component type
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function settings(blocks, opt) {
|
||||
var self = {
|
||||
blocks : blocks || new type.Factory(function(s) {
|
||||
self.blocks = new type.Component([]);
|
||||
// read until end of stream
|
||||
while(s.availableLength() > 0) {
|
||||
self.blocks.obj.push(block().read(s));
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read GCC response from server
|
||||
* @param s {type.Stream} current stream
|
||||
* @returns {Array(type.Component)} list of server block
|
||||
*/
|
||||
function readConferenceCreateResponse(s) {
|
||||
per.readChoice(s);
|
||||
|
||||
if(!per.readObjectIdentifier(s, t124_02_98_oid)) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_OBJECT_IDENTIFIER_T124');
|
||||
}
|
||||
|
||||
per.readLength(s);
|
||||
per.readChoice(s);
|
||||
per.readInteger16(s, 1001);
|
||||
per.readInteger(s);
|
||||
per.readEnumerates(s);
|
||||
per.readNumberOfSet(s);
|
||||
per.readChoice(s);
|
||||
|
||||
if (!per.readOctetStream(s, h221_sc_key, 4)) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_H221_SC_KEY');
|
||||
}
|
||||
|
||||
length = per.readLength(s);
|
||||
serverSettings = settings(null, { readLength : new type.CallableValue(length) });
|
||||
|
||||
// Object magic
|
||||
return serverSettings.read(s).obj.blocks.obj.map(function(e) {
|
||||
return e.obj.data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Read GCC request
|
||||
* @param s {type.Stream}
|
||||
* @returns {Array(type.Component)} list of client block
|
||||
*/
|
||||
function readConferenceCreateRequest (s) {
|
||||
per.readChoice(s);
|
||||
if (!per.readObjectIdentifier(s, t124_02_98_oid)) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_H221_SC_KEY');
|
||||
}
|
||||
per.readLength(s);
|
||||
per.readChoice(s);
|
||||
per.readSelection(s);
|
||||
per.readNumericString(s, 1);
|
||||
per.readPadding(s, 1);
|
||||
|
||||
if (per.readNumberOfSet(s) !== 1) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_SET');
|
||||
}
|
||||
|
||||
if (per.readChoice(s) !== 0xc0) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_CHOICE');
|
||||
}
|
||||
|
||||
per.readOctetStream(s, h221_cs_key, 4);
|
||||
|
||||
length = per.readLength(s);
|
||||
var clientSettings = settings(null, { readLength : new type.CallableValue(length) });
|
||||
|
||||
// Object magic
|
||||
return clientSettings.read(s).obj.blocks.obj.map(function(e) {
|
||||
return e.obj.data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Built {type.Componen} from gcc user data
|
||||
* @param userData {type.Component} GCC data from client
|
||||
* @returns {type.Component} GCC encoded client user data
|
||||
*/
|
||||
function writeConferenceCreateRequest (userData) {
|
||||
var userDataStream = new type.Stream(userData.size());
|
||||
userData.write(userDataStream);
|
||||
|
||||
return new type.Component([
|
||||
per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid),
|
||||
per.writeLength(userData.size() + 14), per.writeChoice(0),
|
||||
per.writeSelection(0x08), per.writeNumericString("1", 1), per.writePadding(1),
|
||||
per.writeNumberOfSet(1), per.writeChoice(0xc0),
|
||||
per.writeOctetStream(Buffer.from(h221_cs_key), 4), per.writeOctetStream(userDataStream.getValue())
|
||||
]);
|
||||
}
|
||||
|
||||
function writeConferenceCreateResponse (userData) {
|
||||
var userDataStream = new type.Stream(userData.size());
|
||||
userData.write(userDataStream);
|
||||
|
||||
return new type.Component([
|
||||
per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid),
|
||||
per.writeLength(userData.size() + 14), per.writeChoice(0x14),
|
||||
per.writeInteger16(0x79F3, 1001), per.writeInteger(1), per.writeEnumerates(0),
|
||||
per.writeNumberOfSet(1), per.writeChoice(0xc0),
|
||||
per.writeOctetStream(Buffer.from(h221_sc_key), 4), per.writeOctetStream(userDataStream.getValue())
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
MessageType : MessageType,
|
||||
VERSION : VERSION,
|
||||
KeyboardLayout : KeyboardLayout,
|
||||
block : block,
|
||||
clientCoreData : clientCoreData,
|
||||
clientNetworkData : clientNetworkData,
|
||||
clientSecurityData : clientSecurityData,
|
||||
serverCoreData : serverCoreData,
|
||||
serverSecurityData : serverSecurityData,
|
||||
serverNetworkData : serverNetworkData,
|
||||
readConferenceCreateResponse : readConferenceCreateResponse,
|
||||
readConferenceCreateRequest : readConferenceCreateRequest,
|
||||
writeConferenceCreateRequest : writeConferenceCreateRequest,
|
||||
writeConferenceCreateResponse : writeConferenceCreateResponse
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var mcs = require('./mcs');
|
||||
var gcc = require('./gcc');
|
||||
|
||||
module.exports = {
|
||||
mcs : mcs,
|
||||
gcc : gcc
|
||||
};
|
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var events = require('events');
|
||||
var type = require('../../core').type;
|
||||
var log = require('../../core').log;
|
||||
var error = require('../../core').error;
|
||||
var gcc = require('./gcc');
|
||||
var per = require('./per');
|
||||
var asn1 = require('../../asn1');
|
||||
|
||||
var Message = {
|
||||
MCS_TYPE_CONNECT_INITIAL : 0x65,
|
||||
MCS_TYPE_CONNECT_RESPONSE : 0x66
|
||||
};
|
||||
|
||||
var DomainMCSPDU = {
|
||||
ERECT_DOMAIN_REQUEST : 1,
|
||||
DISCONNECT_PROVIDER_ULTIMATUM : 8,
|
||||
ATTACH_USER_REQUEST : 10,
|
||||
ATTACH_USER_CONFIRM : 11,
|
||||
CHANNEL_JOIN_REQUEST : 14,
|
||||
CHANNEL_JOIN_CONFIRM : 15,
|
||||
SEND_DATA_REQUEST : 25,
|
||||
SEND_DATA_INDICATION : 26
|
||||
};
|
||||
|
||||
var Channel = {
|
||||
MCS_GLOBAL_CHANNEL : 1003,
|
||||
MCS_USERCHANNEL_BASE : 1001
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function DomainParameters(maxChannelIds, maxUserIds, maxTokenIds,
|
||||
numPriorities, minThoughput, maxHeight, maxMCSPDUsize, protocolVersion) {
|
||||
return new asn1.univ.Sequence({
|
||||
maxChannelIds : new asn1.univ.Integer(maxChannelIds),
|
||||
maxUserIds : new asn1.univ.Integer(maxUserIds),
|
||||
maxTokenIds : new asn1.univ.Integer(maxTokenIds),
|
||||
numPriorities : new asn1.univ.Integer(numPriorities),
|
||||
minThoughput : new asn1.univ.Integer(minThoughput),
|
||||
maxHeight : new asn1.univ.Integer(maxHeight),
|
||||
maxMCSPDUsize : new asn1.univ.Integer(maxMCSPDUsize),
|
||||
protocolVersion : new asn1.univ.Integer(protocolVersion)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
|
||||
* @param userData {Buffer}
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function ConnectInitial (userData) {
|
||||
return new asn1.univ.Sequence({
|
||||
callingDomainSelector : new asn1.univ.OctetString(Buffer.from('\x01', 'binary')),
|
||||
calledDomainSelector : new asn1.univ.OctetString(Buffer.from('\x01', 'binary')),
|
||||
upwardFlag : new asn1.univ.Boolean(true),
|
||||
targetParameters : DomainParameters(34, 2, 0, 1, 0, 1, 0xffff, 2),
|
||||
minimumParameters : DomainParameters(1, 1, 1, 1, 0, 1, 0x420, 2),
|
||||
maximumParameters : DomainParameters(0xffff, 0xfc17, 0xffff, 1, 0, 1, 0xffff, 2),
|
||||
userData : new asn1.univ.OctetString(userData)
|
||||
}).implicitTag(new asn1.spec.Asn1Tag(asn1.spec.TagClass.Application, asn1.spec.TagFormat.Constructed, 101));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function ConnectResponse (userData) {
|
||||
return new asn1.univ.Sequence({
|
||||
result : new asn1.univ.Enumerate(0),
|
||||
calledConnectId : new asn1.univ.Integer(0),
|
||||
domainParameters : DomainParameters(22, 3, 0, 1, 0, 1,0xfff8, 2),
|
||||
userData : new asn1.univ.OctetString(userData)
|
||||
}).implicitTag(new asn1.spec.Asn1Tag(asn1.spec.TagClass.Application, asn1.spec.TagFormat.Constructed, 102));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format MCS PDU header packet
|
||||
* @param mcsPdu {integer}
|
||||
* @param options {integer}
|
||||
* @returns {type.UInt8} headers
|
||||
*/
|
||||
function writeMCSPDUHeader(mcsPdu, options) {
|
||||
options = options || 0;
|
||||
return new type.UInt8((mcsPdu << 2) | options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read MCS PDU header
|
||||
* @param opcode
|
||||
* @param mcsPdu
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function readMCSPDUHeader(opcode, mcsPdu) {
|
||||
return (opcode >> 2) === mcsPdu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-Channel Services
|
||||
* @param transport {events.EventEmitter} transport layer listen (connect, data) events
|
||||
* @param recvOpCode {DomainMCSPDU} opcode use in receive automata
|
||||
* @param sendOpCode {DomainMCSPDU} opcode use to send message
|
||||
*/
|
||||
function MCS(transport, recvOpCode, sendOpCode) {
|
||||
this.transport = transport;
|
||||
this.recvOpCode = recvOpCode;
|
||||
this.sendOpCode = sendOpCode;
|
||||
this.channels = [{id : Channel.MCS_GLOBAL_CHANNEL, name : 'global'}];
|
||||
this.channels.find = function(callback) {
|
||||
for(var i in this) {
|
||||
if(callback(this[i])) return this[i];
|
||||
};
|
||||
};
|
||||
|
||||
// bind events
|
||||
var self = this;
|
||||
this.transport.on('close', function () {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
}
|
||||
|
||||
//inherit from Layer
|
||||
inherits(MCS, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Send message to a specific channel
|
||||
* @param channelName {string} name of channel
|
||||
* @param data {type.*} message to send
|
||||
*/
|
||||
MCS.prototype.send = function(channelName, data) {
|
||||
var channelId = this.channels.find(function(element) {
|
||||
if (element.name === channelName) return true;
|
||||
}).id;
|
||||
|
||||
this.transport.send(new type.Component([
|
||||
writeMCSPDUHeader(this.sendOpCode),
|
||||
per.writeInteger16(this.userId, Channel.MCS_USERCHANNEL_BASE),
|
||||
per.writeInteger16(channelId),
|
||||
new type.UInt8(0x70),
|
||||
per.writeLength(data.size()),
|
||||
data
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* Main receive function
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
MCS.prototype.recv = function(s) {
|
||||
opcode = new type.UInt8().read(s).value;
|
||||
|
||||
if (readMCSPDUHeader(opcode, DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM)) {
|
||||
log.info("MCS DISCONNECT_PROVIDER_ULTIMATUM");
|
||||
this.transport.close();
|
||||
return
|
||||
}
|
||||
else if(!readMCSPDUHeader(opcode, this.recvOpCode)) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_BAD_RECEIVE_OPCODE');
|
||||
}
|
||||
|
||||
per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE);
|
||||
|
||||
var channelId = per.readInteger16(s);
|
||||
|
||||
per.readEnumerates(s);
|
||||
per.readLength(s);
|
||||
|
||||
var channelName = this.channels.find(function(e) {
|
||||
if (e.id === channelId) return true;
|
||||
}).name;
|
||||
|
||||
this.emit(channelName, s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Only main channels handle actually
|
||||
* @param transport {event.EventEmitter} bind connect and data events
|
||||
* @returns
|
||||
*/
|
||||
function Client(transport) {
|
||||
MCS.call(this, transport, DomainMCSPDU.SEND_DATA_INDICATION, DomainMCSPDU.SEND_DATA_REQUEST);
|
||||
|
||||
// channel context automata
|
||||
this.channelsConnected = 0;
|
||||
|
||||
// init gcc information
|
||||
this.clientCoreData = gcc.clientCoreData();
|
||||
this.clientNetworkData = gcc.clientNetworkData(new type.Component([]));
|
||||
this.clientSecurityData = gcc.clientSecurityData();
|
||||
|
||||
// must be readed from protocol
|
||||
this.serverCoreData = null;
|
||||
this.serverSecurityData = null;
|
||||
this.serverNetworkData = null;
|
||||
|
||||
var self = this;
|
||||
this.transport.on('connect', function(s) {
|
||||
self.connect(s);
|
||||
});
|
||||
}
|
||||
|
||||
inherits(Client, MCS);
|
||||
|
||||
/**
|
||||
* Connect event layer
|
||||
*/
|
||||
Client.prototype.connect = function(selectedProtocol) {
|
||||
this.clientCoreData.obj.serverSelectedProtocol.value = selectedProtocol;
|
||||
this.sendConnectInitial();
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
Client.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* MCS connect response (server->client)
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvConnectResponse = function(s) {
|
||||
var userData = new type.Stream(ConnectResponse().decode(s, asn1.ber).value.userData.value);
|
||||
var serverSettings = gcc.readConferenceCreateResponse(userData);
|
||||
// record server gcc block
|
||||
for(var i in serverSettings) {
|
||||
if(!serverSettings[i].obj) {
|
||||
continue;
|
||||
}
|
||||
switch(serverSettings[i].obj.__TYPE__) {
|
||||
case gcc.MessageType.SC_CORE:
|
||||
this.serverCoreData = serverSettings[i];
|
||||
break;
|
||||
case gcc.MessageType.SC_SECURITY:
|
||||
this.serverSecurityData = serverSettings[i];
|
||||
break;
|
||||
case gcc.MessageType.SC_NET:
|
||||
this.serverNetworkData = serverSettings[i];
|
||||
break;
|
||||
default:
|
||||
log.warn('unhandle server gcc block : ' + serverSettings[i].obj.__TYPE__);
|
||||
}
|
||||
}
|
||||
|
||||
// send domain request
|
||||
this.sendErectDomainRequest();
|
||||
// send attach user request
|
||||
this.sendAttachUserRequest();
|
||||
// now wait user confirm from server
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvAttachUserConfirm(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* MCS connection automata step
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvAttachUserConfirm = function(s) {
|
||||
if (!readMCSPDUHeader(new type.UInt8().read(s).value, DomainMCSPDU.ATTACH_USER_CONFIRM)) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER');
|
||||
}
|
||||
|
||||
if (per.readEnumerates(s) !== 0) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_REJECT_USER');
|
||||
}
|
||||
|
||||
this.userId = per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE);
|
||||
//ask channel for specific user
|
||||
this.channels.push({ id : this.userId, name : 'user' });
|
||||
// channel connect automata
|
||||
this.connectChannels();
|
||||
};
|
||||
|
||||
/**
|
||||
* Last state in channel connection automata
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvChannelJoinConfirm = function(s) {
|
||||
var opcode = new type.UInt8().read(s).value;
|
||||
|
||||
if (!readMCSPDUHeader(opcode, DomainMCSPDU.CHANNEL_JOIN_CONFIRM)) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_WAIT_CHANNEL_JOIN_CONFIRM');
|
||||
}
|
||||
|
||||
var confirm = per.readEnumerates(s);
|
||||
|
||||
var userId = per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE);
|
||||
if (this.userId !== userId) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_INVALID_USER_ID');
|
||||
}
|
||||
|
||||
var channelId = per.readInteger16(s);
|
||||
|
||||
if ((confirm !== 0) && (channelId === Channel.MCS_GLOBAL_CHANNEL || channelId === this.userId)) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL');
|
||||
}
|
||||
|
||||
this.connectChannels();
|
||||
};
|
||||
|
||||
/**
|
||||
* First MCS message
|
||||
*/
|
||||
Client.prototype.sendConnectInitial = function() {
|
||||
|
||||
var ccReq = gcc.writeConferenceCreateRequest(new type.Component([
|
||||
gcc.block(this.clientCoreData),
|
||||
gcc.block(this.clientNetworkData),
|
||||
gcc.block(this.clientSecurityData)
|
||||
])).toStream().getValue();
|
||||
|
||||
this.transport.send(ConnectInitial(ccReq).encode(asn1.ber));
|
||||
|
||||
// next event is connect response
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvConnectResponse(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* MCS connection automata step
|
||||
*/
|
||||
Client.prototype.sendErectDomainRequest = function() {
|
||||
this.transport.send(new type.Component([
|
||||
writeMCSPDUHeader(DomainMCSPDU.ERECT_DOMAIN_REQUEST),
|
||||
per.writeInteger(0),
|
||||
per.writeInteger(0)
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* MCS connection automata step
|
||||
*/
|
||||
Client.prototype.sendAttachUserRequest = function() {
|
||||
this.transport.send(writeMCSPDUHeader(DomainMCSPDU.ATTACH_USER_REQUEST));
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a channel join request
|
||||
* @param channelId {integer} channel id
|
||||
*/
|
||||
Client.prototype.sendChannelJoinRequest = function(channelId) {
|
||||
this.transport.send(new type.Component([
|
||||
writeMCSPDUHeader(DomainMCSPDU.CHANNEL_JOIN_REQUEST),
|
||||
per.writeInteger16(this.userId, Channel.MCS_USERCHANNEL_BASE),
|
||||
per.writeInteger16(channelId)
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect channels automata
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.connectChannels = function(s) {
|
||||
if(this.channelsConnected == this.channels.length) {
|
||||
var self = this;
|
||||
this.transport.on('data', function(s) {
|
||||
self.recv(s);
|
||||
});
|
||||
|
||||
// send client and sever gcc informations
|
||||
this.emit('connect',
|
||||
{
|
||||
core : this.clientCoreData.obj,
|
||||
security : this.clientSecurityData.obj,
|
||||
net : this.clientNetworkData.obj
|
||||
},
|
||||
{
|
||||
core : this.serverCoreData.obj,
|
||||
security : this.serverSecurityData.obj
|
||||
}, this.userId, this.channels);
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendChannelJoinRequest(this.channels[this.channelsConnected++].id);
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvChannelJoinConfirm(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Server side of MCS layer
|
||||
* @param transport
|
||||
*/
|
||||
function Server (transport) {
|
||||
MCS.call(this, transport, DomainMCSPDU.SEND_DATA_REQUEST, DomainMCSPDU.SEND_DATA_INDICATION);
|
||||
|
||||
// must be readed from protocol
|
||||
this.clientCoreData = null;
|
||||
this.clientNetworkData = null;
|
||||
this.clientSecurityData = null;
|
||||
|
||||
// init gcc information
|
||||
this.serverCoreData = gcc.serverCoreData();
|
||||
this.serverSecurityData = gcc.serverSecurityData();
|
||||
this.serverNetworkData = gcc.serverNetworkData(new type.Component([]));
|
||||
|
||||
var self = this;
|
||||
this.transport.on('connect', function (selectedProtocol) {
|
||||
self.serverCoreData.obj.clientRequestedProtocol.value = selectedProtocol;
|
||||
}).once('data', function (s) {
|
||||
self.recvConnectInitial(s);
|
||||
});
|
||||
}
|
||||
|
||||
inherits(Server, MCS);
|
||||
|
||||
/**
|
||||
* First state of server automata
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Server.prototype.recvConnectInitial = function (s) {
|
||||
var userData = new type.Stream(ConnectInitial().decode(s, asn1.ber).value.userData.value);
|
||||
var clientSettings = gcc.readConferenceCreateRequest(userData);
|
||||
// record server gcc block
|
||||
for(var i in clientSettings) {
|
||||
if(!clientSettings[i].obj) {
|
||||
continue;
|
||||
}
|
||||
switch(clientSettings[i].obj.__TYPE__) {
|
||||
case gcc.MessageType.CS_CORE:
|
||||
this.clientCoreData = clientSettings[i];
|
||||
break;
|
||||
case gcc.MessageType.CS_SECURITY:
|
||||
this.clientSecurityData = clientSettings[i];
|
||||
break;
|
||||
case gcc.MessageType.CS_NET:
|
||||
this.clientNetworkData = clientSettings[i];
|
||||
for (var i = 0; i < this.clientNetworkData.obj.channelCount.value; i++) {
|
||||
this.serverNetworkData.obj.channelIdArray.obj.push(new type.UInt16Le( i + 1 + Channel.MCS_GLOBAL_CHANNEL));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log.debug('unhandle client gcc block : ' + clientSettings[i].obj.__TYPE__);
|
||||
}
|
||||
}
|
||||
|
||||
this.sendConnectResponse();
|
||||
};
|
||||
|
||||
/**
|
||||
* State 2 in mcs server connetion automata
|
||||
*/
|
||||
Server.prototype.sendConnectResponse = function () {
|
||||
var ccReq = gcc.writeConferenceCreateResponse(new type.Component([
|
||||
gcc.block(this.serverCoreData),
|
||||
gcc.block(this.serverSecurityData),
|
||||
gcc.block(this.serverNetworkData),
|
||||
])).toStream().getValue();
|
||||
|
||||
this.transport.send(ConnectResponse(ccReq).encode(asn1.ber));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
Client : Client,
|
||||
Server : Server
|
||||
};
|
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../../core').type;
|
||||
var error = require('../../core').error;
|
||||
|
||||
/**
|
||||
* @param s {type.Stream} read value from stream
|
||||
* @returns read length from per format
|
||||
*/
|
||||
function readLength(s) {
|
||||
var byte = new type.UInt8().read(s).value;
|
||||
var size = 0;
|
||||
if(byte & 0x80) {
|
||||
byte &= ~0x80;
|
||||
size = byte << 8;
|
||||
size += new type.UInt8().read(s).value;
|
||||
}
|
||||
else {
|
||||
size = byte;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value {raw} value to convert to per format
|
||||
* @returns type objects per encoding value
|
||||
*/
|
||||
function writeLength(value) {
|
||||
if(value > 0x7f) {
|
||||
return new type.UInt16Be(value | 0x8000);
|
||||
}
|
||||
else {
|
||||
return new type.UInt8(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} choice decoding from per encoding
|
||||
*/
|
||||
function readChoice(s) {
|
||||
return new type.UInt8().read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param choice {integer}
|
||||
* @returns {type.UInt8} choice per encoded
|
||||
*/
|
||||
function writeChoice(choice) {
|
||||
return new type.UInt8(choice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} number represent selection
|
||||
*/
|
||||
function readSelection(s) {
|
||||
return new type.UInt8().read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selection {integer}
|
||||
* @returns {type.UInt8} per encoded selection
|
||||
*/
|
||||
function writeSelection(selection) {
|
||||
return new type.UInt8(selection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} number of sets
|
||||
*/
|
||||
function readNumberOfSet(s) {
|
||||
return new type.UInt8().read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numberOfSet {integer}
|
||||
* @returns {type.UInt8} per encoded nuimber of sets
|
||||
*/
|
||||
function writeNumberOfSet(numberOfSet) {
|
||||
return new type.UInt8(numberOfSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} enumerates number
|
||||
*/
|
||||
function readEnumerates(s) {
|
||||
return new type.UInt8().read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param enumerate {integer}
|
||||
* @returns {type.UInt8} per encoded enumerate
|
||||
*/
|
||||
function writeEnumerates(enumerate) {
|
||||
return new type.UInt8(enumerate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} integer per decoded
|
||||
*/
|
||||
function readInteger(s) {
|
||||
var result;
|
||||
var size = readLength(s);
|
||||
switch(size) {
|
||||
case 1:
|
||||
result = new type.UInt8();
|
||||
break;
|
||||
case 2:
|
||||
result = new type.UInt16Be();
|
||||
break;
|
||||
case 4:
|
||||
result = new type.UInt32Be();
|
||||
break;
|
||||
default:
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_PER_BAD_INTEGER_LENGTH');
|
||||
}
|
||||
return result.read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value {integer}
|
||||
* @returns {type.Component} per encoded integer
|
||||
*/
|
||||
function writeInteger(value) {
|
||||
if(value <= 0xff) {
|
||||
return new type.Component([writeLength(1), new type.UInt8(value)]);
|
||||
}
|
||||
else if(value < 0xffff) {
|
||||
return new type.Component([writeLength(2), new type.UInt16Be(value)]);
|
||||
}
|
||||
else {
|
||||
return new type.Component([writeLength(4), new type.UInt32Be(value)]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param minimum {integer} increment (default 0)
|
||||
* @returns {integer} per decoded integer 16 bits
|
||||
*/
|
||||
function readInteger16(s, minimum) {
|
||||
return new type.UInt16Be().read(s).value + (minimum || 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value {integer}
|
||||
* @param minimum {integer} decrement (default 0)
|
||||
* @returns {type.UInt16Be} per encoded integer 16 bits
|
||||
*/
|
||||
function writeInteger16(value, minimum) {
|
||||
return new type.UInt16Be(value - (minimum || 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check object identifier
|
||||
* @param s {type.Stream}
|
||||
* @param oid {array} object identifier to check
|
||||
*/
|
||||
function readObjectIdentifier(s, oid) {
|
||||
var size = readLength(s);
|
||||
if(size !== 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var a_oid = [0, 0, 0, 0, 0, 0];
|
||||
var t12 = new type.UInt8().read(s).value;
|
||||
a_oid[0] = t12 >> 4;
|
||||
a_oid[1] = t12 & 0x0f;
|
||||
a_oid[2] = new type.UInt8().read(s).value;
|
||||
a_oid[3] = new type.UInt8().read(s).value;
|
||||
a_oid[4] = new type.UInt8().read(s).value;
|
||||
a_oid[5] = new type.UInt8().read(s).value;
|
||||
|
||||
for(var i in oid) {
|
||||
if(oid[i] !== a_oid[i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oid {array} oid to write
|
||||
* @returns {type.Component} per encoded object identifier
|
||||
*/
|
||||
function writeObjectIdentifier(oid) {
|
||||
return new type.Component([new type.UInt8(5), new type.UInt8((oid[0] << 4) & (oid[1] & 0x0f)), new type.UInt8(oid[2]), new type.UInt8(oid[3]), new type.UInt8(oid[4]), new type.UInt8(oid[5])]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read as padding...
|
||||
* @param s {type.Stream}
|
||||
* @param minValue
|
||||
*/
|
||||
function readNumericString(s, minValue) {
|
||||
var length = readLength(s);
|
||||
length = (length + minValue + 1) / 2;
|
||||
s.readPadding(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nStr {String}
|
||||
* @param minValue {integer}
|
||||
* @returns {type.Component} per encoded numeric string
|
||||
*/
|
||||
function writeNumericString(nStr, minValue) {
|
||||
var length = nStr.length;
|
||||
var mlength = minValue;
|
||||
if(length - minValue >= 0) {
|
||||
mlength = length - minValue;
|
||||
}
|
||||
|
||||
var result = [];
|
||||
|
||||
for(var i = 0; i < length; i += 2) {
|
||||
var c1 = nStr.charCodeAt(i);
|
||||
var c2 = 0;
|
||||
if(i + 1 < length) {
|
||||
c2 = nStr.charCodeAt(i + 1);
|
||||
}
|
||||
else {
|
||||
c2 = 0x30;
|
||||
}
|
||||
c1 = (c1 - 0x30) % 10;
|
||||
c2 = (c2 - 0x30) % 10;
|
||||
|
||||
result[result.length] = new type.UInt8((c1 << 4) | c2);
|
||||
}
|
||||
|
||||
return new type.Component([writeLength(mlength), new type.Component(result)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param length {integer} length of padding
|
||||
*/
|
||||
function readPadding(s, length) {
|
||||
s.readPadding(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param length {integer} length of padding
|
||||
* @returns {type.BinaryString} per encoded padding
|
||||
*/
|
||||
function writePadding(length) {
|
||||
return new type.BinaryString(Buffer.from(Array(length + 1).join("\x00")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param octetStream {String}
|
||||
* @param minValue {integer} default 0
|
||||
* @returns {Boolean} true if read octectStream is equal to octetStream
|
||||
*/
|
||||
function readOctetStream(s, octetStream, minValue) {
|
||||
var size = readLength(s) + (minValue || 0);
|
||||
if(size !== octetStream.length) {
|
||||
return false;
|
||||
}
|
||||
for(var i = 0; i < size; i++) {
|
||||
var c = new type.UInt8().read(s);
|
||||
if(octetStream.charCodeAt(i) !== c.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oStr {String}
|
||||
* @param minValue {integer} default 0
|
||||
* @returns {type.Component} per encoded octet stream
|
||||
*/
|
||||
function writeOctetStream(oStr, minValue) {
|
||||
minValue = minValue || 0;
|
||||
var length = oStr.length;
|
||||
var mlength = minValue;
|
||||
|
||||
if(length - minValue >= 0) {
|
||||
mlength = length - minValue;
|
||||
}
|
||||
|
||||
result = [];
|
||||
for(var i = 0; i < length; i++) {
|
||||
result[result.length] = new type.UInt8(oStr[i]);
|
||||
}
|
||||
|
||||
return new type.Component([writeLength(mlength), new type.Component(result)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
readLength : readLength,
|
||||
writeLength : writeLength,
|
||||
readChoice : readChoice,
|
||||
writeChoice : writeChoice,
|
||||
readSelection : readSelection,
|
||||
writeSelection : writeSelection,
|
||||
readNumberOfSet : readNumberOfSet,
|
||||
writeNumberOfSet : writeNumberOfSet,
|
||||
readEnumerates : readEnumerates,
|
||||
writeEnumerates : writeEnumerates,
|
||||
readInteger : readInteger,
|
||||
writeInteger : writeInteger,
|
||||
readInteger16 : readInteger16,
|
||||
writeInteger16 : writeInteger16,
|
||||
readObjectIdentifier : readObjectIdentifier,
|
||||
writeObjectIdentifier : writeObjectIdentifier,
|
||||
readNumericString : readNumericString,
|
||||
writeNumericString : writeNumericString,
|
||||
readPadding : readPadding,
|
||||
writePadding : writePadding,
|
||||
readOctetStream : readOctetStream,
|
||||
writeOctetStream : writeOctetStream
|
||||
};
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var type = require('../core').type;
|
||||
var events = require('events');
|
||||
|
||||
/**
|
||||
* Type of tpkt packet
|
||||
* Fastpath is use to shortcut RDP stack
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240621.aspx
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240589.aspx
|
||||
*/
|
||||
var Action = {
|
||||
FASTPATH_ACTION_FASTPATH : 0x0,
|
||||
FASTPATH_ACTION_X224 : 0x3
|
||||
};
|
||||
|
||||
/**
|
||||
* TPKT layer of rdp stack
|
||||
*/
|
||||
function TPKT(transport) {
|
||||
this.transport = transport;
|
||||
// wait 2 bytes
|
||||
this.transport.expect(2);
|
||||
// next state is receive header
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvHeader(s);
|
||||
}).on('close', function() {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* inherit from a packet layer
|
||||
*/
|
||||
inherits(TPKT, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Receive correct packet as expected
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
TPKT.prototype.recvHeader = function (s) {
|
||||
var version = new type.UInt8().read(s).value;
|
||||
var self = this;
|
||||
if(version === Action.FASTPATH_ACTION_X224) {
|
||||
new type.UInt8().read(s);
|
||||
this.transport.expect(2);
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvExtendedHeader(s);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.secFlag = ((version >> 6) & 0x3);
|
||||
var length = new type.UInt8().read(s).value;
|
||||
if (length & 0x80) {
|
||||
this.transport.expect(1);
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvExtendedFastPathHeader(s, length);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.transport.expect(length - 2);
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvFastPath(s);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive second part of header packet
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
TPKT.prototype.recvExtendedHeader = function (s) {
|
||||
var size = new type.UInt16Be().read(s);
|
||||
this.transport.expect(size.value - 4);
|
||||
//next state receive packet
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvData(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive data available for presentation layer
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
TPKT.prototype.recvData = function (s) {
|
||||
this.emit('data', s);
|
||||
this.transport.expect(2);
|
||||
//next state receive header
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvHeader(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Read extended fastpath header
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
TPKT.prototype.recvExtendedFastPathHeader = function (s, length) {
|
||||
var rightPart = new type.UInt8().read(s).value;
|
||||
var leftPart = length & ~0x80;
|
||||
var packetSize = (leftPart << 8) + rightPart;
|
||||
|
||||
var self = this;
|
||||
this.transport.expect(packetSize - 3);
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvFastPath(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Read fast path data
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
TPKT.prototype.recvFastPath = function (s) {
|
||||
this.emit('fastPathData', this.secFlag, s);
|
||||
var self = this;
|
||||
this.transport.expect(2);
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvHeader(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Send message throught TPKT layer
|
||||
* @param message {type.*}
|
||||
*/
|
||||
TPKT.prototype.send = function(message) {
|
||||
this.transport.send(new type.Component([
|
||||
new type.UInt8(Action.FASTPATH_ACTION_X224),
|
||||
new type.UInt8(0),
|
||||
new type.UInt16Be(message.size() + 4),
|
||||
message
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
TPKT.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = TPKT;
|
||||
|
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var events = require('events');
|
||||
var type = require('../core').type;
|
||||
var log = require('../core').log;
|
||||
var error = require('../core').error;
|
||||
|
||||
/**
|
||||
* Message type present in X224 packet header
|
||||
*/
|
||||
var MessageType = {
|
||||
X224_TPDU_CONNECTION_REQUEST : 0xE0,
|
||||
X224_TPDU_CONNECTION_CONFIRM : 0xD0,
|
||||
X224_TPDU_DISCONNECT_REQUEST : 0x80,
|
||||
X224_TPDU_DATA : 0xF0,
|
||||
X224_TPDU_ERROR : 0x70
|
||||
};
|
||||
|
||||
/**
|
||||
* Type of negotiation present in negotiation packet
|
||||
*/
|
||||
var NegotiationType = {
|
||||
TYPE_RDP_NEG_REQ : 0x01,
|
||||
TYPE_RDP_NEG_RSP : 0x02,
|
||||
TYPE_RDP_NEG_FAILURE : 0x03
|
||||
};
|
||||
|
||||
/**
|
||||
* Protocols available for x224 layer
|
||||
*/
|
||||
var Protocols = {
|
||||
PROTOCOL_RDP : 0x00000000,
|
||||
PROTOCOL_SSL : 0x00000001,
|
||||
PROTOCOL_HYBRID : 0x00000002,
|
||||
PROTOCOL_HYBRID_EX : 0x00000008
|
||||
};
|
||||
|
||||
/**
|
||||
* Use to negotiate security layer of RDP stack
|
||||
* In node-rdpjs only ssl is available
|
||||
* @param opt {object} component type options
|
||||
* @see request -> http://msdn.microsoft.com/en-us/library/cc240500.aspx
|
||||
* @see response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx
|
||||
* @see failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx
|
||||
*/
|
||||
function negotiation(opt) {
|
||||
var self = {
|
||||
type : new type.UInt8(),
|
||||
flag : new type.UInt8(),
|
||||
length : new type.UInt16Le(0x0008, { constant : true }),
|
||||
result : new type.UInt32Le()
|
||||
};
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* X224 client connection request
|
||||
* @param opt {object} component type options
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240470.aspx
|
||||
*/
|
||||
function clientConnectionRequestPDU(opt, cookie) {
|
||||
var self = {
|
||||
len : new type.UInt8(function() {
|
||||
return new type.Component(self).size() - 1;
|
||||
}),
|
||||
code : new type.UInt8(MessageType.X224_TPDU_CONNECTION_REQUEST, { constant : true }),
|
||||
padding : new type.Component([new type.UInt16Le(), new type.UInt16Le(), new type.UInt8()]),
|
||||
cookie : cookie || new type.Factory( function (s) {
|
||||
var offset = 0;
|
||||
while (true) {
|
||||
var token = s.buffer.readUInt16LE(s.offset + offset);
|
||||
if (token === 0x0a0d) {
|
||||
self.cookie = new type.BinaryString(null, { readLength : new type.CallableValue(offset + 2) }).read(s);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
}, { conditional : function () {
|
||||
return self.len.value > 14;
|
||||
}}),
|
||||
protocolNeg : negotiation({ optional : true })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* X224 Server connection confirm
|
||||
* @param opt {object} component type options
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240506.aspx
|
||||
*/
|
||||
function serverConnectionConfirm(opt) {
|
||||
var self = {
|
||||
len : new type.UInt8(function() {
|
||||
return new type.Component(self).size() - 1;
|
||||
}),
|
||||
code : new type.UInt8(MessageType.X224_TPDU_CONNECTION_CONFIRM, { constant : true }),
|
||||
padding : new type.Component([new type.UInt16Le(), new type.UInt16Le(), new type.UInt8()]),
|
||||
protocolNeg : negotiation({ optional : true })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Header of each data message from x224 layer
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function x224DataHeader() {
|
||||
var self = {
|
||||
header : new type.UInt8(2),
|
||||
messageType : new type.UInt8(MessageType.X224_TPDU_DATA, { constant : true }),
|
||||
separator : new type.UInt8(0x80, { constant : true })
|
||||
};
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common X224 Automata
|
||||
* @param presentation {Layer} presentation layer
|
||||
*/
|
||||
function X224(transport) {
|
||||
this.transport = transport;
|
||||
this.requestedProtocol = Protocols.PROTOCOL_SSL | Protocols.PROTOCOL_HYBRID;
|
||||
this.selectedProtocol = Protocols.PROTOCOL_SSL | Protocols.PROTOCOL_HYBRID;
|
||||
|
||||
var self = this;
|
||||
this.transport.on('close', function() {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
}
|
||||
|
||||
//inherit from Layer
|
||||
inherits(X224, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Main data received function
|
||||
* after connection sequence
|
||||
* @param s {type.Stream} stream formated from transport layer
|
||||
*/
|
||||
X224.prototype.recvData = function(s) {
|
||||
// check header
|
||||
x224DataHeader().read(s);
|
||||
this.emit('data', s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Format message from x224 layer to transport layer
|
||||
* @param message {type}
|
||||
* @returns {type.Component} x224 formated message
|
||||
*/
|
||||
X224.prototype.send = function(message) {
|
||||
this.transport.send(new type.Component([x224DataHeader(), message]));
|
||||
};
|
||||
|
||||
/**
|
||||
* Client x224 automata
|
||||
* @param transport {events.EventEmitter} (bind data events)
|
||||
*/
|
||||
function Client(transport, config) {
|
||||
this.config = config;
|
||||
X224.call(this, transport);
|
||||
}
|
||||
|
||||
//inherit from X224 automata
|
||||
inherits(Client, X224);
|
||||
|
||||
/**
|
||||
* Client automata connect event
|
||||
*/
|
||||
Client.prototype.connect = function() {
|
||||
var message = clientConnectionRequestPDU(null, new type.BinaryString());
|
||||
message.obj.protocolNeg.obj.type.value = NegotiationType.TYPE_RDP_NEG_REQ;
|
||||
message.obj.protocolNeg.obj.result.value = this.requestedProtocol;
|
||||
this.transport.send(message);
|
||||
|
||||
// next state wait connection confirm packet
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvConnectionConfirm(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
Client.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive connection from server
|
||||
* @param s {Stream}
|
||||
*/
|
||||
Client.prototype.recvConnectionConfirm = function(s) {
|
||||
var message = serverConnectionConfirm().read(s);
|
||||
|
||||
if (message.obj.protocolNeg.obj.type.value == NegotiationType.TYPE_RDP_NEG_FAILURE) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_X224_NEG_FAILURE',
|
||||
'Failure code:' + message.obj.protocolNeg.obj.result.value + " (see https://msdn.microsoft.com/en-us/library/cc240507.aspx)");
|
||||
}
|
||||
|
||||
if (message.obj.protocolNeg.obj.type.value == NegotiationType.TYPE_RDP_NEG_RSP) {
|
||||
this.selectedProtocol = message.obj.protocolNeg.obj.result.value;
|
||||
}
|
||||
|
||||
if ([Protocols.PROTOCOL_HYBRID_EX].indexOf(this.selectedProtocol) !== -1) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_X224_NLA_NOT_SUPPORTED');
|
||||
}
|
||||
|
||||
if (this.selectedProtocol == Protocols.PROTOCOL_RDP) {
|
||||
log.debug("RDP standard security selected");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.selectedProtocol == Protocols.PROTOCOL_HYBRID) {
|
||||
log.debug("NLA security layer selected");
|
||||
var self = this;
|
||||
var transportEx = this.transport.transport;
|
||||
this.transport.transport.startTLS(function () {
|
||||
//console.log('TLS connected, start cssp_connect()');
|
||||
var NLA = require('./nla');
|
||||
self.nla = new NLA(transportEx, function () { self.nlaCompleted(); }, self.config.domain, self.config.userName, self.config.password);
|
||||
self.nla.sendNegotiateMessage();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// finish connection sequence
|
||||
var self = this;
|
||||
this.transport.on('data', function(s) {
|
||||
self.recvData(s);
|
||||
});
|
||||
|
||||
if (this.selectedProtocol == Protocols.PROTOCOL_SSL) {
|
||||
log.debug("SSL standard security selected");
|
||||
this.transport.transport.startTLS(function() {
|
||||
self.emit('connect', self.selectedProtocol);
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when NLA is completed
|
||||
*/
|
||||
Client.prototype.nlaCompleted = function () {
|
||||
const self = this;
|
||||
delete self.nla;
|
||||
this.transport.on('data', function (s) { self.recvData(s); });
|
||||
this.emit('connect', this.selectedProtocol);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Server x224 automata
|
||||
*/
|
||||
function Server(transport, keyFilePath, crtFilePath) {
|
||||
X224.call(this, transport);
|
||||
this.keyFilePath = keyFilePath;
|
||||
this.crtFilePath = crtFilePath;
|
||||
var self = this;
|
||||
this.transport.once('data', function (s) {
|
||||
self.recvConnectionRequest(s);
|
||||
});
|
||||
}
|
||||
|
||||
//inherit from X224 automata
|
||||
inherits(Server, X224);
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240470.aspx
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Server.prototype.recvConnectionRequest = function (s) {
|
||||
var request = clientConnectionRequestPDU().read(s);
|
||||
if (!request.obj.protocolNeg.isReaded) {
|
||||
throw new Error('NODE_RDP_PROTOCOL_X224_NO_BASIC_SECURITY_LAYER');
|
||||
}
|
||||
|
||||
this.requestedProtocol = request.obj.protocolNeg.obj.result.value;
|
||||
this.selectedProtocol = this.requestedProtocol & Protocols.PROTOCOL_SSL;
|
||||
|
||||
if (!(this.selectedProtocol & Protocols.PROTOCOL_SSL)) {
|
||||
var confirm = serverConnectionConfirm();
|
||||
confirm.obj.protocolNeg.obj.type.value = NegociationType.TYPE_RDP_NEG_FAILURE;
|
||||
confirm.obj.protocolNeg.obj.result.value = NegotiationFailureCode.SSL_REQUIRED_BY_SERVER;
|
||||
this.transport.send(confirm);
|
||||
this.close();
|
||||
}
|
||||
else {
|
||||
this.sendConnectionConfirm();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Start SSL connection if needed
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240501.aspx
|
||||
*/
|
||||
Server.prototype.sendConnectionConfirm = function () {
|
||||
var confirm = serverConnectionConfirm();
|
||||
confirm.obj.protocolNeg.obj.type.value = NegotiationType.TYPE_RDP_NEG_RSP;
|
||||
confirm.obj.protocolNeg.obj.result.value = this.selectedProtocol;
|
||||
this.transport.send(confirm);
|
||||
|
||||
// finish connection sequence
|
||||
var self = this;
|
||||
this.transport.on('data', function(s) {
|
||||
self.recvData(s);
|
||||
});
|
||||
|
||||
this.transport.transport.listenTLS(this.keyFilePath, this.crtFilePath, function() {
|
||||
log.debug('start SSL connection');
|
||||
self.emit('connect', self.requestedProtocol);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
Client : Client,
|
||||
Server : Server
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var x509 = require('./x509');
|
||||
var rsa = require('./rsa');
|
||||
|
||||
module.exports = {
|
||||
x509 : x509,
|
||||
rsa : rsa
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var BigInteger = require('./jsbn');
|
||||
|
||||
/**
|
||||
* @param modulus {Buffer}
|
||||
* @param pubExp {integer}
|
||||
*/
|
||||
function publicKey(modulus, pubExp) {
|
||||
return {
|
||||
n : modulus,
|
||||
e : pubExp
|
||||
}
|
||||
}
|
||||
|
||||
function encrypt(data, publicKey) {
|
||||
return new BigInteger(data).modPowInt(publicKey.e, new BigInteger(publicKey.n)).toBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Module Export
|
||||
*/
|
||||
module.exports = {
|
||||
publicKey : publicKey,
|
||||
encrypt : encrypt
|
||||
};
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// https://tools.ietf.org/html/rfc5280
|
||||
|
||||
var asn1 = require('../asn1');
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 20
|
||||
* @returns {asn1.univ.Choice}
|
||||
*/
|
||||
function DirectoryString() {
|
||||
return new asn1.univ.Choice({
|
||||
teletexString : new asn1.univ.T61String(),
|
||||
printableString : new asn1.univ.PrintableString(),
|
||||
universalString : new asn1.univ.UniversalString(),
|
||||
utf8String : new asn1.univ.UTF8String(),
|
||||
bmpString : new asn1.univ.BMPString(),
|
||||
ia5String : new asn1.univ.IA5String()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* https://tools.ietf.org/html/rfc5280 page 20
|
||||
* @returns {asn1.univ.Choice}
|
||||
*/
|
||||
function AttributeValue() {
|
||||
return DirectoryString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 20
|
||||
* @returns {asn1.univ.ObjectIdentifier}
|
||||
*/
|
||||
function AttributeType() {
|
||||
return new asn1.univ.ObjectIdentifier();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 20
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function AttributeTypeAndValue() {
|
||||
return new asn1.univ.Sequence({
|
||||
type : AttributeType(),
|
||||
value : AttributeValue()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* https://tools.ietf.org/html/rfc5280 page 116
|
||||
* @returns {asn1.univ.SetOf}
|
||||
*/
|
||||
function RelativeDistinguishedName() {
|
||||
return new asn1.univ.SetOf(AttributeTypeAndValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* https://tools.ietf.org/html/rfc5280 page 116
|
||||
* @returns {asn1.univ.SequenceOf}
|
||||
*/
|
||||
function RDNSequence() {
|
||||
return new asn1.univ.SequenceOf(RelativeDistinguishedName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 116
|
||||
* @returns {asn1.univ.Choice}
|
||||
*/
|
||||
function Name() {
|
||||
return new asn1.univ.Choice({
|
||||
rdnSequence : RDNSequence()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 18
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function AlgorithmIdentifier() {
|
||||
return new asn1.univ.Sequence({
|
||||
algorithm : new asn1.univ.ObjectIdentifier(),
|
||||
parameters : new asn1.univ.Null()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 117
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function Extension() {
|
||||
return new asn1.univ.Sequence({
|
||||
extnID : new asn1.univ.ObjectIdentifier(),
|
||||
critical : new asn1.univ.Boolean(),
|
||||
extnValue : new asn1.univ.OctetString()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 117
|
||||
* @returns {asn1.univ.SequenceOf}
|
||||
*/
|
||||
function Extensions() {
|
||||
return new asn1.univ.SequenceOf(Extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 117
|
||||
* @returns {asn1.univ.Choice}
|
||||
*/
|
||||
function Time() {
|
||||
return new asn1.univ.Choice({
|
||||
utcTime : new asn1.univ.UTCTime(),
|
||||
generalTime : new asn1.univ.GeneralizedTime()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 117
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function Validity() {
|
||||
return new asn1.univ.Sequence({
|
||||
notBefore : Time(),
|
||||
notAfter : Time()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 117
|
||||
* @returns {asn1.univ.Integer}
|
||||
*/
|
||||
function CertificateSerialNumber() {
|
||||
return new asn1.univ.Integer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 117
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function SubjectPublicKeyInfo() {
|
||||
return new asn1.univ.Sequence({
|
||||
algorithm : AlgorithmIdentifier(),
|
||||
subjectPublicKey : new asn1.univ.BitString()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 117
|
||||
* @returns {asn1.univ.BitString}
|
||||
*/
|
||||
function UniqueIdentifier() {
|
||||
return new asn1.univ.BitString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 117
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function TbsCertificate() {
|
||||
return new asn1.univ.Sequence({
|
||||
version : CertificateSerialNumber().explicitTag(new asn1.spec.Asn1Tag(asn1.spec.TagClass.Context, asn1.spec.TagFormat.Constructed, 0)),
|
||||
serialNumber : new asn1.univ.Integer(),
|
||||
signature : AlgorithmIdentifier(),
|
||||
issuer : Name(),
|
||||
validity : Validity(),
|
||||
subject : Name(),
|
||||
subjectPublicKeyInfo : SubjectPublicKeyInfo(),
|
||||
issuerUniqueID : UniqueIdentifier().implicitTag(asn1.spec.TagClass.Context, asn1.spec.TagFormat.Primitive, 1).optional(),
|
||||
subjectUniqueID : UniqueIdentifier().implicitTag(asn1.spec.TagClass.Context, asn1.spec.TagFormat.Primitive, 2).optional(),
|
||||
extensions : Extensions().implicitTag(asn1.spec.TagClass.Context, asn1.spec.TagFormat.Primitive, 3).optional()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc5280 page 117
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function X509Certificate() {
|
||||
return new asn1.univ.Sequence({
|
||||
tbsCertificate : TbsCertificate(),
|
||||
signatureAlgorithm : AlgorithmIdentifier(),
|
||||
signatureValue : new asn1.univ.BitString()
|
||||
});
|
||||
}
|
||||
|
||||
function RSAPublicKey() {
|
||||
return new asn1.univ.Sequence({
|
||||
modulus : new asn1.univ.Integer(),
|
||||
publicExponent : new asn1.univ.Integer()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Module Export
|
||||
*/
|
||||
module.exports = {
|
||||
X509Certificate : X509Certificate,
|
||||
RSAPublicKey : RSAPublicKey
|
||||
};
|
|
@ -2931,7 +2931,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
|
|||
if (domain.sessionrecording != null) { features += 0x08000000; } // Server recordings enabled
|
||||
if (domain.urlswitching === false) { features += 0x10000000; } // Disables the URL switching feature
|
||||
if (domain.novnc === false) { features += 0x20000000; } // Disables noVNC
|
||||
if (domain.mstsc !== true) { features += 0x40000000; } // Disables MSTSC.js
|
||||
if (domain.mstsc === false) { features += 0x40000000; } // Disables MSTSC.js
|
||||
if (obj.isTrustedCert(domain) == false) { features += 0x80000000; } // Indicate we are not using a trusted certificate
|
||||
if (obj.parent.amtManager != null) { features2 += 0x00000001; } // Indicates that the Intel AMT manager is active
|
||||
if (obj.parent.firebase != null) { features2 += 0x00000002; } // Indicates the server supports Firebase push messaging
|
||||
|
|
Loading…
Reference in New Issue