Merge libvncclient into the project
parent
9a61d8b159
commit
60574ba34b
|
@ -0,0 +1,340 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) 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
|
||||
this service 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 make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. 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.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
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
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the 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 a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE 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.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: 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
|
||||
convey 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) 19yy <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 2 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, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision 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, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This 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 Library General
|
||||
Public License instead of this License.
|
|
@ -0,0 +1,46 @@
|
|||
#ifndef _RFB_CRYPTO_H
|
||||
#define _RFB_CRYPTO_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
#include "config.h"
|
||||
|
||||
#define SHA1_HASH_SIZE 20
|
||||
#define MD5_HASH_SIZE 16
|
||||
|
||||
/* Generates an MD5 hash of 'in' and writes it to 'out', which must be 16 bytes in size. */
|
||||
int hash_md5(void *out, const void *in, const size_t in_len);
|
||||
|
||||
/* Generates an SHA1 hash of 'in' and writes it to 'out', which must be 20 bytes in size. */
|
||||
int hash_sha1(void *out, const void *in, const size_t in_len);
|
||||
|
||||
/* Fill 'out' with 'len' random bytes. */
|
||||
void random_bytes(void *out, size_t len);
|
||||
|
||||
/*
|
||||
Takes the 8-byte key in 'key', reverses the bits in each byte of key as required by the RFB protocol,
|
||||
encrypts 'in' with the resulting key using single-key 56-bit DES and writes the result to 'out'.
|
||||
*/
|
||||
int encrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len);
|
||||
|
||||
/*
|
||||
Takes the 8-byte key in 'key', reverses the bits in each byte of key as required by the RFB protocol,
|
||||
decrypts 'in' with the resulting key using single-key 56-bit DES and writes the result to 'out'.
|
||||
*/
|
||||
int decrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len);
|
||||
|
||||
/* Encrypts 'in' with the the 16-byte key in 'key' using AES-128-ECB and writes the result to 'out'. */
|
||||
int encrypt_aes128ecb(void *out, int *out_len, const unsigned char key[16], const void *in, const size_t in_len);
|
||||
|
||||
/*
|
||||
Generates a Diffie-Hellman public-private keypair using the generator value 'gen' and prime modulo
|
||||
'prime', writing the result to 'pub_out' and 'priv_out', which must be 'keylen' in size.
|
||||
*/
|
||||
int dh_generate_keypair(uint8_t *priv_out, uint8_t *pub_out, const uint8_t *gen, const size_t gen_len, const uint8_t *prime, const size_t keylen);
|
||||
|
||||
/*
|
||||
Computes the shared Diffie-Hellman secret using the private key 'priv', the other side's public
|
||||
key 'pub' and the modulo prime 'prime' and writes it to 'shared_out', which must be 'keylen' in size.
|
||||
*/
|
||||
int dh_compute_shared_key(uint8_t *shared_out, const uint8_t *priv, const uint8_t *pub, const uint8_t *prime, const size_t keylen);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,831 @@
|
|||
#ifndef RFBCLIENT_H
|
||||
#define RFBCLIENT_H
|
||||
|
||||
/**
|
||||
* @defgroup libvncclient_api LibVNCClient API Reference
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 D. R. Commander. All Rights Reserved.
|
||||
* Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
|
||||
* Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
|
||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file rfbclient.h
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <rfb/rfbproto.h>
|
||||
#include <rfb/keysym.h>
|
||||
#include <rfb/threading.h>
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
#include <sasl/sasl.h>
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
|
||||
#define rfbClientSwap16IfLE(s) \
|
||||
(*(char *)&client->endianTest ? ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) : (s))
|
||||
|
||||
#define rfbClientSwap32IfLE(l) \
|
||||
(*(char *)&client->endianTest ? ((((l) >> 24) & 0x000000ff) | \
|
||||
(((l) & 0x00ff0000) >> 8) | \
|
||||
(((l) & 0x0000ff00) << 8) | \
|
||||
(((l) & 0x000000ff) << 24)) : (l))
|
||||
|
||||
#define rfbClientSwap64IfLE(l) \
|
||||
(*(char *)&client->endianTest ? ((((l) >> 56 ) & 0x00000000000000ffULL) | \
|
||||
(((l) & 0x00ff000000000000ULL) >> 40) | \
|
||||
(((l) & 0x0000ff0000000000ULL) >> 24) | \
|
||||
(((l) & 0x000000ff00000000ULL) >> 8) | \
|
||||
(((l) & 0x00000000ff000000ULL) << 8) | \
|
||||
(((l) & 0x0000000000ff0000ULL) << 24) | \
|
||||
(((l) & 0x000000000000ff00ULL) << 40) | \
|
||||
(((l) & 0x00000000000000ffULL) << 56)) : (l))
|
||||
|
||||
#define FLASH_PORT_OFFSET 5400
|
||||
#define LISTEN_PORT_OFFSET 5500
|
||||
#define TUNNEL_PORT_OFFSET 5500
|
||||
#define SERVER_PORT_OFFSET 5900
|
||||
|
||||
#define DEFAULT_CONNECT_TIMEOUT 60
|
||||
#define DEFAULT_READ_TIMEOUT 0
|
||||
|
||||
#define DEFAULT_SSH_CMD "/usr/bin/ssh"
|
||||
#define DEFAULT_TUNNEL_CMD \
|
||||
(DEFAULT_SSH_CMD " -f -L %L:localhost:%R %H sleep 20")
|
||||
#define DEFAULT_VIA_CMD \
|
||||
(DEFAULT_SSH_CMD " -f -L %L:%H:%R %G sleep 20")
|
||||
|
||||
#if(defined __cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** vncrec */
|
||||
|
||||
typedef struct {
|
||||
FILE* file;
|
||||
struct timeval tv;
|
||||
rfbBool readTimestamp;
|
||||
rfbBool doNotSleep;
|
||||
} rfbVNCRec;
|
||||
|
||||
/** client data */
|
||||
|
||||
typedef struct rfbClientData {
|
||||
void* tag;
|
||||
void* data;
|
||||
struct rfbClientData* next;
|
||||
} rfbClientData;
|
||||
|
||||
/** app data (belongs into rfbClient?) */
|
||||
|
||||
typedef struct {
|
||||
rfbBool shareDesktop;
|
||||
rfbBool viewOnly;
|
||||
|
||||
const char* encodingsString;
|
||||
|
||||
rfbBool useBGR233;
|
||||
int nColours;
|
||||
rfbBool forceOwnCmap;
|
||||
rfbBool forceTrueColour;
|
||||
int requestedDepth;
|
||||
|
||||
int compressLevel;
|
||||
int qualityLevel;
|
||||
rfbBool enableJPEG;
|
||||
rfbBool useRemoteCursor;
|
||||
rfbBool palmVNC; /**< use palmvnc specific SetScale (vs ultravnc) */
|
||||
int scaleSetting; /**< 0 means no scale set, else 1/scaleSetting */
|
||||
} AppData;
|
||||
|
||||
/** For GetCredentialProc callback function to return */
|
||||
typedef union _rfbCredential
|
||||
{
|
||||
/** X509 (VeNCrypt) */
|
||||
struct
|
||||
{
|
||||
char *x509CACertFile;
|
||||
char *x509CACrlFile;
|
||||
char *x509ClientCertFile;
|
||||
char *x509ClientKeyFile;
|
||||
uint8_t x509CrlVerifyMode; /* Only required for OpenSSL - see meanings below */
|
||||
} x509Credential;
|
||||
/** Plain (VeNCrypt), MSLogon (UltraVNC) */
|
||||
struct
|
||||
{
|
||||
char *username;
|
||||
char *password;
|
||||
} userCredential;
|
||||
} rfbCredential;
|
||||
|
||||
#define rfbCredentialTypeX509 1
|
||||
#define rfbCredentialTypeUser 2
|
||||
|
||||
/* When using OpenSSL, CRLs can be included in both the x509CACrlFile and appended
|
||||
to the x509CACertFile as is common with OpenSSL. When rfbX509CrlVerifyAll is
|
||||
specified the CRL list must include CRLs for all certificates in the chain */
|
||||
#define rfbX509CrlVerifyNone 0 /* No CRL checking is performed */
|
||||
#define rfbX509CrlVerifyClient 1 /* Only the leaf server certificate is checked */
|
||||
#define rfbX509CrlVerifyAll 2 /* All certificates in the server chain are checked */
|
||||
|
||||
struct _rfbClient;
|
||||
|
||||
/**
|
||||
* Handles a text chat message. If your application should accept text messages
|
||||
* from the server, define a function with this prototype and set
|
||||
* client->HandleTextChat to a pointer to that function subsequent to your
|
||||
* rfbGetClient() call.
|
||||
* @param client The client which called the text chat handler
|
||||
* @param value text length if text != NULL, or one of rfbTextChatOpen,
|
||||
* rfbTextChatClose, rfbTextChatFinished if text == NULL
|
||||
* @param text The text message from the server
|
||||
*/
|
||||
typedef void (*HandleTextChatProc)(struct _rfbClient* client, int value, char *text);
|
||||
/**
|
||||
* Handles XVP server messages. If your application sends XVP messages to the
|
||||
* server, you'll want to handle the server's XVP_FAIL and XVP_INIT responses.
|
||||
* Define a function with this prototype and set client->HandleXvpMsg to a
|
||||
* pointer to that function subsequent to your rfbGetClient() call.
|
||||
* @param client The client which called the XVP message handler
|
||||
* @param version The highest XVP extension version that the server supports
|
||||
* @param opcode The opcode. 0 is XVP_FAIL, 1 is XVP_INIT
|
||||
*/
|
||||
typedef void (*HandleXvpMsgProc)(struct _rfbClient* client, uint8_t version, uint8_t opcode);
|
||||
typedef void (*HandleKeyboardLedStateProc)(struct _rfbClient* client, int value, int pad);
|
||||
typedef rfbBool (*HandleCursorPosProc)(struct _rfbClient* client, int x, int y);
|
||||
typedef void (*SoftCursorLockAreaProc)(struct _rfbClient* client, int x, int y, int w, int h);
|
||||
typedef void (*SoftCursorUnlockScreenProc)(struct _rfbClient* client);
|
||||
/**
|
||||
Callback indicating that a rectangular area of the client's framebuffer was updated.
|
||||
As a server will usually send several rects per rfbFramebufferUpdate message, this
|
||||
callback is usually called multiple times per rfbFramebufferUpdate message.
|
||||
@param client The client whose framebuffer was (partially) updated
|
||||
@param x The x-coordinate of the upper left corner of the updated rectangle
|
||||
@param y The y-coordinate of the upper left corner of the updated rectangle
|
||||
@param w The width of the updated rectangle
|
||||
@param h The heigth of the updated rectangle
|
||||
*/
|
||||
typedef void (*GotFrameBufferUpdateProc)(struct _rfbClient* client, int x, int y, int w, int h);
|
||||
/**
|
||||
Callback indicating that a client has completely processed an rfbFramebufferUpdate
|
||||
message sent by a server.
|
||||
This is called exactly once per each handled rfbFramebufferUpdate message.
|
||||
@param client The client which finished processing an rfbFramebufferUpdate
|
||||
*/
|
||||
typedef void (*FinishedFrameBufferUpdateProc)(struct _rfbClient* client);
|
||||
typedef char* (*GetPasswordProc)(struct _rfbClient* client);
|
||||
typedef rfbCredential* (*GetCredentialProc)(struct _rfbClient* client, int credentialType);
|
||||
typedef rfbBool (*MallocFrameBufferProc)(struct _rfbClient* client);
|
||||
typedef void (*GotXCutTextProc)(struct _rfbClient* client, const char *text, int textlen);
|
||||
typedef void (*BellProc)(struct _rfbClient* client);
|
||||
/**
|
||||
Called when a cursor shape update was received from the server. The decoded cursor shape
|
||||
will be in client->rcSource. It's up to the application to do something with this, e.g. draw
|
||||
into a viewer's window. If you want the server to draw the cursor into the framebuffer, be
|
||||
careful not to announce remote cursor support, i.e. not include rfbEncodingXCursor or
|
||||
rfbEncodingRichCursor in SetFormatAndEncodings().
|
||||
*/
|
||||
typedef void (*GotCursorShapeProc)(struct _rfbClient* client, int xhot, int yhot, int width, int height, int bytesPerPixel);
|
||||
typedef void (*GotCopyRectProc)(struct _rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y);
|
||||
typedef void (*GotFillRectProc)(struct _rfbClient* client, int x, int y, int w, int h, uint32_t colour);
|
||||
typedef void (*GotBitmapProc)(struct _rfbClient* client, const uint8_t* buffer, int x, int y, int w, int h);
|
||||
typedef rfbBool (*GotJpegProc)(struct _rfbClient* client, const uint8_t* buffer, int length, int x, int y, int w, int h);
|
||||
typedef rfbBool (*LockWriteToTLSProc)(struct _rfbClient* client); /** @deprecated */
|
||||
typedef rfbBool (*UnlockWriteToTLSProc)(struct _rfbClient* client); /** @deprecated */
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
typedef char* (*GetUserProc)(struct _rfbClient* client);
|
||||
typedef char* (*GetSASLMechanismProc)(struct _rfbClient* client, char* mechlist);
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
|
||||
typedef struct _rfbClient {
|
||||
uint8_t* frameBuffer;
|
||||
int width, height;
|
||||
|
||||
int endianTest;
|
||||
|
||||
AppData appData;
|
||||
|
||||
const char* programName;
|
||||
char* serverHost;
|
||||
int serverPort; /**< if -1, then use file recorded by vncrec */
|
||||
rfbBool listenSpecified;
|
||||
int listenPort, flashPort;
|
||||
|
||||
struct {
|
||||
int x, y, w, h;
|
||||
} updateRect;
|
||||
|
||||
/** Note that the CoRRE encoding uses this buffer and assumes it is big enough
|
||||
to hold 255 * 255 * 32 bits -> 260100 bytes. 640*480 = 307200 bytes.
|
||||
Hextile also assumes it is big enough to hold 16 * 16 * 32 bits.
|
||||
Tight encoding assumes BUFFER_SIZE is at least 16384 bytes. */
|
||||
|
||||
#define RFB_BUFFER_SIZE (640*480)
|
||||
char buffer[RFB_BUFFER_SIZE];
|
||||
|
||||
/* rfbproto.c */
|
||||
|
||||
rfbSocket sock;
|
||||
rfbBool canUseCoRRE;
|
||||
rfbBool canUseHextile;
|
||||
char *desktopName;
|
||||
rfbPixelFormat format;
|
||||
rfbServerInitMsg si;
|
||||
|
||||
/* sockets.c */
|
||||
#define RFB_BUF_SIZE 8192
|
||||
char buf[RFB_BUF_SIZE];
|
||||
char *bufoutptr;
|
||||
unsigned int buffered;
|
||||
|
||||
/* The zlib encoding requires expansion/decompression/deflation of the
|
||||
compressed data in the "buffer" above into another, result buffer.
|
||||
However, the size of the result buffer can be determined precisely
|
||||
based on the bitsPerPixel, height and width of the rectangle. We
|
||||
allocate this buffer one time to be the full size of the buffer. */
|
||||
|
||||
/* Ultra Encoding uses this buffer too */
|
||||
|
||||
int ultra_buffer_size;
|
||||
char *ultra_buffer;
|
||||
|
||||
int raw_buffer_size;
|
||||
char *raw_buffer;
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||
z_stream decompStream;
|
||||
rfbBool decompStreamInited;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||
/*
|
||||
* Variables for the ``tight'' encoding implementation.
|
||||
*/
|
||||
|
||||
/** Separate buffer for compressed data. */
|
||||
#define ZLIB_BUFFER_SIZE 30000
|
||||
char zlib_buffer[ZLIB_BUFFER_SIZE];
|
||||
|
||||
/* Four independent compression streams for zlib library. */
|
||||
z_stream zlibStream[4];
|
||||
rfbBool zlibStreamActive[4];
|
||||
|
||||
/* Filter stuff. Should be initialized by filter initialization code. */
|
||||
rfbBool cutZeros;
|
||||
int rectWidth, rectColors;
|
||||
char tightPalette[256*4];
|
||||
uint8_t tightPrevRow[2048*3*sizeof(uint16_t)];
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
||||
/** JPEG decoder state (obsolete-- do not use). */
|
||||
rfbBool jpegError;
|
||||
|
||||
struct jpeg_source_mgr* jpegSrcManager;
|
||||
void* jpegBufferPtr;
|
||||
size_t jpegBufferLen;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* cursor.c */
|
||||
/** Holds cursor shape data when received from server. */
|
||||
uint8_t *rcSource, *rcMask;
|
||||
|
||||
/** private data pointer */
|
||||
rfbClientData* clientData;
|
||||
|
||||
rfbVNCRec* vncRec;
|
||||
|
||||
/* Keyboard State support (is 'Caps Lock' set on the remote display???) */
|
||||
int KeyboardLedStateEnabled;
|
||||
int CurrentKeyboardLedState;
|
||||
|
||||
int canHandleNewFBSize;
|
||||
|
||||
/* hooks */
|
||||
HandleTextChatProc HandleTextChat;
|
||||
HandleKeyboardLedStateProc HandleKeyboardLedState;
|
||||
HandleCursorPosProc HandleCursorPos;
|
||||
SoftCursorLockAreaProc SoftCursorLockArea;
|
||||
SoftCursorUnlockScreenProc SoftCursorUnlockScreen;
|
||||
GotFrameBufferUpdateProc GotFrameBufferUpdate;
|
||||
/** the pointer returned by GetPassword will be freed after use! */
|
||||
GetPasswordProc GetPassword;
|
||||
MallocFrameBufferProc MallocFrameBuffer;
|
||||
GotXCutTextProc GotXCutText;
|
||||
BellProc Bell;
|
||||
|
||||
GotCursorShapeProc GotCursorShape;
|
||||
GotCopyRectProc GotCopyRect;
|
||||
|
||||
/** Which messages are supported by the server
|
||||
* This is a *guess* for most servers.
|
||||
* (If we can even detect the type of server)
|
||||
*
|
||||
* If the server supports the "rfbEncodingSupportedMessages"
|
||||
* then this will be updated when the encoding is received to
|
||||
* accurately reflect the servers capabilities.
|
||||
*/
|
||||
rfbSupportedMessages supportedMessages;
|
||||
|
||||
/** negotiated protocol version */
|
||||
int major, minor;
|
||||
|
||||
/** The selected security types */
|
||||
uint32_t authScheme, subAuthScheme;
|
||||
|
||||
/** The TLS session for Anonymous TLS and VeNCrypt */
|
||||
void* tlsSession;
|
||||
|
||||
/** To support security types that requires user input (except VNC password
|
||||
* authentication), for example VeNCrypt and MSLogon, this callback function
|
||||
* must be set before the authentication. Otherwise, it implicates that the
|
||||
* caller application does not support it and related security types should
|
||||
* be bypassed.
|
||||
*/
|
||||
GetCredentialProc GetCredential;
|
||||
|
||||
/** The 0-terminated security types supported by the client.
|
||||
* Set by function SetClientAuthSchemes() */
|
||||
uint32_t *clientAuthSchemes;
|
||||
|
||||
/** When the server is a repeater, this specifies the final destination */
|
||||
char *destHost;
|
||||
int destPort;
|
||||
|
||||
/** the QoS IP DSCP for this client */
|
||||
int QoS_DSCP;
|
||||
|
||||
/** hook to handle xvp server messages */
|
||||
HandleXvpMsgProc HandleXvpMsg;
|
||||
|
||||
/* listen.c */
|
||||
rfbSocket listenSock;
|
||||
|
||||
FinishedFrameBufferUpdateProc FinishedFrameBufferUpdate;
|
||||
|
||||
char *listenAddress;
|
||||
/* IPv6 listen socket, address and port*/
|
||||
rfbSocket listen6Sock;
|
||||
char* listen6Address;
|
||||
int listen6Port;
|
||||
|
||||
/* Output Window ID. When set, client application enables libvncclient to perform direct rendering in its window */
|
||||
unsigned long outputWindow;
|
||||
|
||||
/**
|
||||
* These lock/unlock hooks are not used anymore. LibVNCClient will now use
|
||||
* platform-specific synchronization library to protect concurrent TLS R/W.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
LockWriteToTLSProc LockWriteToTLS;
|
||||
UnlockWriteToTLSProc UnlockWriteToTLS;
|
||||
|
||||
/** Hooks for custom rendering
|
||||
*
|
||||
* VNC rendering boils down to 3 activities:
|
||||
* - GotCopyRect: copy an area of the framebuffer
|
||||
* - GotFillRect: fill an area of the framebuffer with a solid color
|
||||
* - GotBitmap: copy the bitmap in the buffer into the framebuffer
|
||||
* The client application should either set all three of these or none!
|
||||
*/
|
||||
GotFillRectProc GotFillRect;
|
||||
GotBitmapProc GotBitmap;
|
||||
/** Hook for custom JPEG decoding and rendering */
|
||||
GotJpegProc GotJpeg;
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
sasl_conn_t *saslconn;
|
||||
const char *saslDecoded;
|
||||
unsigned int saslDecodedLength;
|
||||
unsigned int saslDecodedOffset;
|
||||
sasl_secret_t *saslSecret;
|
||||
|
||||
/* Callback to allow the client to choose a preferred mechanism. The string returned will
|
||||
be freed once no longer required. */
|
||||
GetSASLMechanismProc GetSASLMechanism;
|
||||
GetUserProc GetUser;
|
||||
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
||||
/** JPEG decoder state. */
|
||||
void *tjhnd;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
/* timeout in seconds for select() after connect() */
|
||||
unsigned int connectTimeout;
|
||||
|
||||
/* timeout in seconds when reading from half-open connections in
|
||||
* ReadFromRFBServer() - keep at 0 to disable timeout detection and handling */
|
||||
unsigned int readTimeout;
|
||||
|
||||
/**
|
||||
* Mutex to protect concurrent TLS read/write.
|
||||
* For internal use only.
|
||||
*/
|
||||
MUTEX(tlsRwMutex);
|
||||
|
||||
rfbBool requestedResize;
|
||||
/**
|
||||
* Used for intended dimensions, rfbClient.width and rfbClient.height are used to manage the real framebuffer dimensions.
|
||||
*/
|
||||
rfbExtDesktopScreen screen;
|
||||
} rfbClient;
|
||||
|
||||
/* cursor.c */
|
||||
/**
|
||||
* Handles XCursor and RichCursor shape updates from the server.
|
||||
* We emulate cursor operating on the frame buffer (that is
|
||||
* why we call it "software cursor"). This decodes the received cursor
|
||||
* shape and hands it over to GotCursorShapeProc, if set.
|
||||
*/
|
||||
extern rfbBool HandleCursorShape(rfbClient* client,int xhot, int yhot, int width, int height, uint32_t enc);
|
||||
|
||||
/* listen.c */
|
||||
|
||||
extern void listenForIncomingConnections(rfbClient* viewer);
|
||||
extern int listenForIncomingConnectionsNoFork(rfbClient* viewer, int usec_timeout);
|
||||
|
||||
/* rfbproto.c */
|
||||
|
||||
extern rfbBool rfbEnableClientLogging;
|
||||
typedef void (*rfbClientLogProc)(const char *format, ...);
|
||||
extern rfbClientLogProc rfbClientLog,rfbClientErr;
|
||||
extern rfbBool ConnectToRFBServer(rfbClient* client,const char *hostname, int port);
|
||||
extern rfbBool ConnectToRFBRepeater(rfbClient* client,const char *repeaterHost, int repeaterPort, const char *destHost, int destPort);
|
||||
extern void SetClientAuthSchemes(rfbClient* client,const uint32_t *authSchemes, int size);
|
||||
extern rfbBool InitialiseRFBConnection(rfbClient* client);
|
||||
/**
|
||||
* Sends format and encoding parameters to the server. Your application can
|
||||
* modify the 'client' data structure directly. However some changes to this
|
||||
* structure must be communicated back to the server. For instance, if you
|
||||
* change the encoding to hextile, the server needs to know that it should send
|
||||
* framebuffer updates in hextile format. Likewise if you change the pixel
|
||||
* format of the framebuffer, the server must be notified about this as well.
|
||||
* Call this function to propagate your changes of the local 'client' structure
|
||||
* over to the server.
|
||||
* @li Encoding type
|
||||
* @li RFB protocol extensions announced via pseudo-encodings
|
||||
* @li Framebuffer pixel format (like RGB vs ARGB)
|
||||
* @li Remote cursor support
|
||||
* @param client The client in which the format or encodings have been changed
|
||||
* @return true if the format or encodings were sent to the server successfully,
|
||||
* false otherwise
|
||||
*/
|
||||
extern rfbBool SetFormatAndEncodings(rfbClient* client);
|
||||
extern rfbBool SendIncrementalFramebufferUpdateRequest(rfbClient* client);
|
||||
/**
|
||||
* Sends a framebuffer update request to the server. A VNC client may request an
|
||||
* update from the server at any time. You can also specify which portions of
|
||||
* the screen you want updated. This can be handy if a pointer is at certain
|
||||
* location and the user pressed a mouse button, for instance. Then you can
|
||||
* immediately request an update of the region around the pointer from the
|
||||
* server.
|
||||
* @note The coordinate system is a left-handed Cartesian coordinate system with
|
||||
* the Z axis (unused) pointing out of the screen. Alternately you can think of
|
||||
* it as a right-handed Cartesian coordinate system with the Z axis pointing
|
||||
* into the screen. The origin is at the upper left corner of the framebuffer.
|
||||
* @param client The client through which to send the request
|
||||
* @param x The horizontal position of the update request rectangle
|
||||
* @param y The vertical position of the update request rectangle
|
||||
* @param w The width of the update request rectangle
|
||||
* @param h The height of the update request rectangle
|
||||
* @param incremental false: server sends rectangle even if nothing changed.
|
||||
* true: server only sends changed parts of rectangle.
|
||||
* @return true if the update request was sent successfully, false otherwise
|
||||
*/
|
||||
extern rfbBool SendFramebufferUpdateRequest(rfbClient* client,
|
||||
int x, int y, int w, int h,
|
||||
rfbBool incremental);
|
||||
extern rfbBool SendScaleSetting(rfbClient* client,int scaleSetting);
|
||||
/**
|
||||
* Sends a pointer event to the server. A pointer event includes a cursor
|
||||
* location and a button mask. The button mask indicates which buttons on the
|
||||
* pointing device are pressed. Each button is represented by a bit in the
|
||||
* button mask. A 1 indicates the button is pressed while a 0 indicates that it
|
||||
* is not pressed. You may use these pre-defined button masks by ORing them
|
||||
* together: rfbButton1Mask, rfbButton2Mask, rfbButton3Mask, rfbButton4Mask
|
||||
* rfbButton5Mask
|
||||
* @note The cursor location is relative to the client's framebuffer, not the
|
||||
* client's screen itself.
|
||||
* @note The coordinate system is a left-handed Cartesian coordinate system with
|
||||
* the Z axis (unused) pointing out of the screen. Alternately you can think of
|
||||
* it as a right-handed Cartesian coordinate system with the Z axis pointing
|
||||
* into the screen. The origin is at the upper left corner of the screen.
|
||||
* @param client The client through which to send the pointer event
|
||||
* @param x the horizontal location of the cursor
|
||||
* @param y the vertical location of the cursor
|
||||
* @param buttonMask the button mask indicating which buttons are pressed
|
||||
* @return true if the pointer event was sent successfully, false otherwise
|
||||
*/
|
||||
extern rfbBool SendPointerEvent(rfbClient* client,int x, int y, int buttonMask);
|
||||
/**
|
||||
* Sends a SetDesktopSize event to the server.
|
||||
* @param client The client through which to send the SetDesktopSize event
|
||||
* @param width The width of the update request rectangle
|
||||
* @param height The height of the update request rectangle
|
||||
* @return true if the SetDesktopSize event was send successfully, false otherwise
|
||||
*/
|
||||
extern rfbBool SendExtDesktopSize(rfbClient* client, uint16_t width, uint16_t height);
|
||||
/**
|
||||
* Sends a key event to the server. If your application is not merely a VNC
|
||||
* viewer (i.e. it controls the server), you'll want to send the keys that the
|
||||
* user presses to the server. Use this function to do that.
|
||||
* @param client The client through which to send the key event
|
||||
* @param key An rfbKeySym defined in rfb/keysym.h
|
||||
* @param down true if this was a key down event, false otherwise
|
||||
* @return true if the key event was send successfully, false otherwise
|
||||
*/
|
||||
extern rfbBool SendKeyEvent(rfbClient* client,uint32_t key, rfbBool down);
|
||||
/**
|
||||
* The same as SendKeyEvent, except a key code will be sent along with the
|
||||
* symbol if the server supports extended key events.
|
||||
* @param client The client through which to send the key event
|
||||
* @param keysym An rfbKeySym defined in rfb/keysym.h
|
||||
* @param keycode An XT key code
|
||||
* @param down true if this was a key down event, false otherwise
|
||||
* @return true if the extended key event is supported and was sent
|
||||
* successfully, false otherwise
|
||||
*/
|
||||
extern rfbBool SendExtendedKeyEvent(rfbClient* client, uint32_t keysym, uint32_t keycode, rfbBool down);
|
||||
/**
|
||||
* Places a string on the server's clipboard. Use this function if you want to
|
||||
* be able to copy and paste between the server and your application. For
|
||||
* instance, when your application is notified that the user copied some text
|
||||
* onto the clipboard, you would call this function to synchronize the server's
|
||||
* clipboard with your local clipboard.
|
||||
* @param client The client structure through which to send the client cut text
|
||||
* message
|
||||
* @param str The string to send (doesn't need to be NULL terminated)
|
||||
* @param len The length of the string
|
||||
* @return true if the client cut message was sent successfully, false otherwise
|
||||
*/
|
||||
extern rfbBool SendClientCutText(rfbClient* client,char *str, int len);
|
||||
/**
|
||||
* Handles messages from the RFB server. You must call this function
|
||||
* intermittently so LibVNCClient can parse messages from the server. For
|
||||
* example, if your app has a draw loop, you could place a call to this
|
||||
* function within that draw loop.
|
||||
* @note You must call WaitForMessage() before you call this function.
|
||||
* @param client The client which will handle the RFB server messages
|
||||
* @return true if the client was able to handle the RFB server messages, false
|
||||
* otherwise
|
||||
*/
|
||||
extern rfbBool HandleRFBServerMessage(rfbClient* client);
|
||||
|
||||
/**
|
||||
* Sends a text chat message to the server.
|
||||
* @param client The client through which to send the message
|
||||
* @param text The text to send
|
||||
* @return true if the text was sent successfully, false otherwise
|
||||
*/
|
||||
extern rfbBool TextChatSend(rfbClient* client, char *text);
|
||||
/**
|
||||
* Opens a text chat window on the server.
|
||||
* @param client The client through which to send the message
|
||||
* @return true if the window was opened successfully, false otherwise
|
||||
*/
|
||||
extern rfbBool TextChatOpen(rfbClient* client);
|
||||
/**
|
||||
* Closes the text chat window on the server.
|
||||
* @param client The client through which to send the message
|
||||
* @return true if the window was closed successfully, false otherwise
|
||||
*/
|
||||
extern rfbBool TextChatClose(rfbClient* client);
|
||||
extern rfbBool TextChatFinish(rfbClient* client);
|
||||
extern rfbBool PermitServerInput(rfbClient* client, int enabled);
|
||||
extern rfbBool SendXvpMsg(rfbClient* client, uint8_t version, uint8_t code);
|
||||
|
||||
extern void PrintPixelFormat(rfbPixelFormat *format);
|
||||
|
||||
extern rfbBool SupportsClient2Server(rfbClient* client, int messageType);
|
||||
extern rfbBool SupportsServer2Client(rfbClient* client, int messageType);
|
||||
|
||||
/* client data */
|
||||
|
||||
/**
|
||||
* Associates a client data tag with the given pointer. LibVNCClient has
|
||||
* several events to which you can associate your own handlers. These handlers
|
||||
* have the client structure as one of their parameters. Sometimes, you may want
|
||||
* to make data from elsewhere in your application available to these handlers
|
||||
* without using a global variable. To do this, you call
|
||||
* rfbClientSetClientData() and associate the data with a tag. Then, your
|
||||
* handler can call rfbClientGetClientData() and get the a pointer to the data
|
||||
* associated with that tag.
|
||||
* @param client The client in which to set the client data
|
||||
* @param tag A unique tag which identifies the data
|
||||
* @param data A pointer to the data to associate with the tag
|
||||
*/
|
||||
void rfbClientSetClientData(rfbClient* client, void* tag, void* data);
|
||||
/**
|
||||
* Returns a pointer to the client data associated with the given tag. See the
|
||||
* the documentation for rfbClientSetClientData() for a discussion of how you
|
||||
* can use client data.
|
||||
* @param client The client from which to get the client data
|
||||
* @param tag The tag which identifies the client data
|
||||
* @return a pointer to the client data
|
||||
*/
|
||||
void* rfbClientGetClientData(rfbClient* client, void* tag);
|
||||
|
||||
/* protocol extensions */
|
||||
|
||||
typedef struct _rfbClientProtocolExtension {
|
||||
int* encodings;
|
||||
/** returns TRUE if the encoding was handled */
|
||||
rfbBool (*handleEncoding)(rfbClient* cl,
|
||||
rfbFramebufferUpdateRectHeader* rect);
|
||||
/** returns TRUE if it handled the message */
|
||||
rfbBool (*handleMessage)(rfbClient* cl,
|
||||
rfbServerToClientMsg* message);
|
||||
struct _rfbClientProtocolExtension* next;
|
||||
uint32_t const* securityTypes;
|
||||
/** returns TRUE if it handled the authentication */
|
||||
rfbBool (*handleAuthentication)(rfbClient* cl, uint32_t authScheme);
|
||||
} rfbClientProtocolExtension;
|
||||
|
||||
void rfbClientRegisterExtension(rfbClientProtocolExtension* e);
|
||||
|
||||
/* sockets.c */
|
||||
|
||||
extern rfbBool errorMessageOnReadFailure;
|
||||
|
||||
extern rfbBool ReadFromRFBServer(rfbClient* client, char *out, unsigned int n);
|
||||
extern rfbBool WriteToRFBServer(rfbClient* client, const char *buf, unsigned int n);
|
||||
extern int FindFreeTcpPort(void);
|
||||
extern rfbSocket ListenAtTcpPort(int port);
|
||||
extern rfbSocket ListenAtTcpPortAndAddress(int port, const char *address);
|
||||
/**
|
||||
Tries to connect to an IPv4 host.
|
||||
@param host Binary IPv4 address
|
||||
@param port Port
|
||||
@return A blocking socket or RFB_INVALID_SOCKET if the connection failed
|
||||
*/
|
||||
extern rfbSocket ConnectClientToTcpAddr(unsigned int host, int port);
|
||||
/**
|
||||
Tries to connect to an IPv4 or IPv6 host.
|
||||
@param hostname A hostname or IP address
|
||||
@param port Port
|
||||
@return A blocking socket or RFB_INVALID_SOCKET if the connection failed
|
||||
*/
|
||||
extern rfbSocket ConnectClientToTcpAddr6(const char *hostname, int port);
|
||||
/**
|
||||
Tries to connect to a Unix socket.
|
||||
@param sockFile Path of the socket file
|
||||
@return A blocking socket or RFB_INVALID_SOCKET if the connection failed
|
||||
*/
|
||||
extern rfbSocket ConnectClientToUnixSock(const char *sockFile);
|
||||
/**
|
||||
Tries to connect to an IPv4 host using the given timeout value.
|
||||
@param host Binary IPv4 address
|
||||
@param port Port
|
||||
@param timeout The time in seconds to wait for a connection
|
||||
@return A nonblocking socket or RFB_INVALID_SOCKET if the connection failed
|
||||
*/
|
||||
extern rfbSocket ConnectClientToTcpAddrWithTimeout(unsigned int host, int port, unsigned int timeout);
|
||||
/**
|
||||
Tries to connect to an IPv4 or IPv6 host using the given timeout value.
|
||||
@param hostname A hostname or IP address
|
||||
@param port Port
|
||||
@param timeout The time in seconds to wait for a connection
|
||||
@return A nonblocking socket or RFB_INVALID_SOCKET if the connection failed
|
||||
*/
|
||||
extern rfbSocket ConnectClientToTcpAddr6WithTimeout(const char *hostname, int port, unsigned int timeout);
|
||||
/**
|
||||
Tries to connect to a Unix socket using the given timeout value.
|
||||
@param sockFile Path of the socket file
|
||||
@param timeout The time in seconds to wait for a connection
|
||||
@return A nonblocking socket or RFB_INVALID_SOCKET if the connection failed
|
||||
*/
|
||||
extern rfbSocket ConnectClientToUnixSockWithTimeout(const char *sockFile, unsigned int timeout);
|
||||
extern rfbSocket AcceptTcpConnection(rfbSocket listenSock);
|
||||
extern rfbBool SetNonBlocking(rfbSocket sock);
|
||||
extern rfbBool SetBlocking(rfbSocket sock);
|
||||
extern rfbBool SetDSCP(rfbSocket sock, int dscp);
|
||||
|
||||
extern rfbBool StringToIPAddr(const char *str, unsigned int *addr);
|
||||
extern rfbBool SameMachine(rfbSocket sock);
|
||||
/**
|
||||
* Waits for an RFB message to arrive from the server. Before handling a message
|
||||
* with HandleRFBServerMessage(), you must wait for your client to receive one.
|
||||
* This function blocks until a message is received. You may specify a timeout
|
||||
* in microseconds. Once this number of microseconds have elapsed, the function
|
||||
* will return.
|
||||
* @param client The client to cause to wait until a message is received
|
||||
* @param usecs The timeout in microseconds
|
||||
* @return the return value of the underlying select() call
|
||||
*/
|
||||
extern int WaitForMessage(rfbClient* client,unsigned int usecs);
|
||||
|
||||
/* vncviewer.c */
|
||||
/**
|
||||
* Allocates and returns a pointer to an rfbClient structure. This will probably
|
||||
* be the first LibVNCClient function your client code calls. Most libVNCClient
|
||||
* functions operate on an rfbClient structure, and this function allocates
|
||||
* memory for that structure. When you're done with the rfbClient structure
|
||||
* pointer this function returns, you should free the memory rfbGetClient()
|
||||
* allocated by calling rfbClientCleanup().
|
||||
*
|
||||
* A pixel is one dot on the screen. The number of bytes in a pixel will depend
|
||||
* on the number of samples in that pixel and the number of bits in each sample.
|
||||
* A sample represents one of the primary colors in a color model. The RGB
|
||||
* color model uses red, green, and blue samples respectively. Suppose you
|
||||
* wanted to use 16-bit RGB color: You would have three samples per pixel (one
|
||||
* for each primary color), five bits per sample (the quotient of 16 RGB bits
|
||||
* divided by three samples), and two bytes per pixel (the smallest multiple of
|
||||
* eight bits in which the 16-bit pixel will fit). If you wanted 32-bit RGB
|
||||
* color, you would have three samples per pixel again, eight bits per sample
|
||||
* (since that's how 32-bit color is defined), and four bytes per pixel (the
|
||||
* smallest multiple of eight bits in which the 32-bit pixel will fit.
|
||||
* @param bitsPerSample The number of bits in a sample
|
||||
* @param samplesPerPixel The number of samples in a pixel
|
||||
* @param bytesPerPixel The number of bytes in a pixel
|
||||
* @return a pointer to the allocated rfbClient structure
|
||||
*/
|
||||
rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,int bytesPerPixel);
|
||||
/**
|
||||
* Initializes the client. The format is {PROGRAM_NAME, [OPTIONS]..., HOST}. This
|
||||
* function does not initialize the program name if the rfbClient's program
|
||||
* name is set already. The options are as follows:
|
||||
* <table>
|
||||
* <tr><th>Option</th><th>Description</th></tr>
|
||||
* <tr><td>-listen</td><td>Listen for incoming connections.</td></tr>
|
||||
* <tr><td>-listennofork</td><td>Listen for incoming connections without forking.
|
||||
* </td></tr>
|
||||
* <tr><td>-play</td><td>Set this client to replay a previously recorded session.</td></tr>
|
||||
* <tr><td>-encodings</td><td>Set the encodings to use. The next item in the
|
||||
* argv array is the encodings string, consisting of comma separated encodings like 'tight,ultra,raw'.</td></tr>
|
||||
* <tr><td>-compress</td><td>Set the compression level. The next item in the
|
||||
* argv array is the compression level as an integer. Ranges from 0 (lowest) to 9 (highest).
|
||||
* </td></tr>
|
||||
* <tr><td>-scale</td><td>Set the scaling level. The next item in the
|
||||
* argv array is the scaling level as an integer. The screen will be scaled down by this factor.</td></tr>
|
||||
* <tr><td>-qosdscp</td><td>Set the Quality of Service Differentiated Services
|
||||
* Code Point (QoS DSCP). The next item in the argv array is the code point as
|
||||
* an integer.</td></tr>
|
||||
* <tr><td>-repeaterdest</td><td>Set a VNC repeater address. The next item in the argv array is
|
||||
* the repeater's address as a string.</td></tr>
|
||||
* </table>
|
||||
*
|
||||
* The host may include a port number (delimited by a ':').
|
||||
* @param client The client to initialize
|
||||
* @param argc The number of arguments to the initializer
|
||||
* @param argv The arguments to the initializer as an array of NULL terminated
|
||||
* strings
|
||||
* @return true if the client was initialized successfully, false otherwise.
|
||||
*/
|
||||
rfbBool rfbInitClient(rfbClient* client,int* argc,char** argv);
|
||||
/**
|
||||
* Cleans up the client structure and releases the memory allocated for it. You
|
||||
* should call this when you're done with the rfbClient structure that you
|
||||
* allocated with rfbGetClient().
|
||||
* @note rfbClientCleanup() does not touch client->frameBuffer.
|
||||
* @param client The client to clean up
|
||||
*/
|
||||
void rfbClientCleanup(rfbClient* client);
|
||||
|
||||
#if(defined __cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
@page libvncclient_doc LibVNCClient Documentation
|
||||
@section example_code Example Code
|
||||
See SDLvncviewer.c for a rather complete client example.
|
||||
*/
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,39 @@
|
|||
#ifndef RFBSASL_H
|
||||
#define RFBSASL_H
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 S. Waterman. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
|
||||
#include <rfb/rfbclient.h>
|
||||
|
||||
/*
|
||||
* Perform the SASL authentication process
|
||||
*/
|
||||
rfbBool HandleSASLAuth(rfbClient *client);
|
||||
|
||||
/*
|
||||
* Read from SASL when the SASL SSF is in use.
|
||||
*/
|
||||
int ReadFromSASL(rfbClient* client, char *out, unsigned int n);
|
||||
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
|
||||
#endif /* RFBSASL_H */
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* LibVNCServer/LibVNCClient common platform socket defines and includes.
|
||||
*
|
||||
* Copyright (C) 2020 Christian Beier <dontmind@freeshell.org>
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#ifndef _RFB_COMMON_SOCKETS_H
|
||||
#define _RFB_COMMON_SOCKETS_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#endif /* _RFB_COMMON_SOCKETS_H */
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef TLS_H
|
||||
#define TLS_H
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009 Vic Lee.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/* Handle Anonymous TLS Authentication (18) with the server.
|
||||
* After authentication, client->tlsSession will be set.
|
||||
*/
|
||||
rfbBool HandleAnonTLSAuth(rfbClient* client);
|
||||
|
||||
/* Handle VeNCrypt Authentication (19) with the server.
|
||||
* The callback function GetX509Credential will be called.
|
||||
* After authentication, client->tlsSession will be set.
|
||||
*/
|
||||
rfbBool HandleVeNCryptAuth(rfbClient* client);
|
||||
|
||||
/* Read desired bytes from TLS session.
|
||||
* It's a wrapper function over gnutls_record_recv() and return values
|
||||
* are same as read(), that is, >0 for actual bytes read, 0 for EOF,
|
||||
* or EAGAIN, EINTR.
|
||||
* This should be a non-blocking call. Blocking is handled in sockets.c.
|
||||
*/
|
||||
int ReadFromTLS(rfbClient* client, char *out, unsigned int n);
|
||||
|
||||
/* Write desired bytes to TLS session.
|
||||
* It's a wrapper function over gnutls_record_send() and it will be
|
||||
* blocking call, until all bytes are written or error returned.
|
||||
*/
|
||||
int WriteToTLS(rfbClient* client, const char *buf, unsigned int n);
|
||||
|
||||
/* Free TLS resources */
|
||||
void FreeTLS(rfbClient* client);
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
/* Get the number of bits in the current cipher */
|
||||
int GetTLSCipherBits(rfbClient* client);
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
|
||||
#endif /* TLS_H */
|
|
@ -0,0 +1,534 @@
|
|||
|
||||
/*
|
||||
* Copyright (C)2009-2012 D. R. Commander. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __TURBOJPEG_H__
|
||||
#define __TURBOJPEG_H__
|
||||
|
||||
#if defined(_WIN32) && defined(DLLDEFINE)
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLLEXPORT
|
||||
#endif
|
||||
#define DLLCALL
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4996)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @addtogroup TurboJPEG Lite
|
||||
* TurboJPEG API. This API provides an interface for generating and decoding
|
||||
* JPEG images in memory.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The number of chrominance subsampling options
|
||||
*/
|
||||
#define TJ_NUMSAMP 5
|
||||
|
||||
/**
|
||||
* Chrominance subsampling options.
|
||||
* When an image is converted from the RGB to the YCbCr colorspace as part of
|
||||
* the JPEG compression process, some of the Cb and Cr (chrominance) components
|
||||
* can be discarded or averaged together to produce a smaller image with little
|
||||
* perceptible loss of image clarity (the human eye is more sensitive to small
|
||||
* changes in brightness than small changes in color.) This is called
|
||||
* "chrominance subsampling".
|
||||
*/
|
||||
enum TJSAMP
|
||||
{
|
||||
/**
|
||||
* 4:4:4 chrominance subsampling (no chrominance subsampling). The JPEG or
|
||||
* YUV image will contain one chrominance component for every pixel in the
|
||||
* source image.
|
||||
*/
|
||||
TJSAMP_444=0,
|
||||
/**
|
||||
* 4:2:2 chrominance subsampling. The JPEG or YUV image will contain one
|
||||
* chrominance component for every 2x1 block of pixels in the source image.
|
||||
*/
|
||||
TJSAMP_422,
|
||||
/**
|
||||
* 4:2:0 chrominance subsampling. The JPEG or YUV image will contain one
|
||||
* chrominance component for every 2x2 block of pixels in the source image.
|
||||
*/
|
||||
TJSAMP_420,
|
||||
/**
|
||||
* Grayscale. The JPEG or YUV image will contain no chrominance components.
|
||||
*/
|
||||
TJSAMP_GRAY,
|
||||
/**
|
||||
* 4:4:0 chrominance subsampling. The JPEG or YUV image will contain one
|
||||
* chrominance component for every 1x2 block of pixels in the source image.
|
||||
*/
|
||||
TJSAMP_440
|
||||
};
|
||||
|
||||
/**
|
||||
* MCU block width (in pixels) for a given level of chrominance subsampling.
|
||||
* MCU block sizes:
|
||||
* - 8x8 for no subsampling or grayscale
|
||||
* - 16x8 for 4:2:2
|
||||
* - 8x16 for 4:4:0
|
||||
* - 16x16 for 4:2:0
|
||||
*/
|
||||
static const int tjMCUWidth[TJ_NUMSAMP] = {8, 16, 16, 8, 8};
|
||||
|
||||
/**
|
||||
* MCU block height (in pixels) for a given level of chrominance subsampling.
|
||||
* MCU block sizes:
|
||||
* - 8x8 for no subsampling or grayscale
|
||||
* - 16x8 for 4:2:2
|
||||
* - 8x16 for 4:4:0
|
||||
* - 16x16 for 4:2:0
|
||||
*/
|
||||
static const int tjMCUHeight[TJ_NUMSAMP] = {8, 8, 16, 8, 16};
|
||||
|
||||
|
||||
/**
|
||||
* The number of pixel formats
|
||||
*/
|
||||
#define TJ_NUMPF 11
|
||||
|
||||
/**
|
||||
* Pixel formats
|
||||
*/
|
||||
enum TJPF
|
||||
{
|
||||
/**
|
||||
* RGB pixel format. The red, green, and blue components in the image are
|
||||
* stored in 3-byte pixels in the order R, G, B from lowest to highest byte
|
||||
* address within each pixel.
|
||||
*/
|
||||
TJPF_RGB=0,
|
||||
/**
|
||||
* BGR pixel format. The red, green, and blue components in the image are
|
||||
* stored in 3-byte pixels in the order B, G, R from lowest to highest byte
|
||||
* address within each pixel.
|
||||
*/
|
||||
TJPF_BGR,
|
||||
/**
|
||||
* RGBX pixel format. The red, green, and blue components in the image are
|
||||
* stored in 4-byte pixels in the order R, G, B from lowest to highest byte
|
||||
* address within each pixel. The X component is ignored when compressing
|
||||
* and undefined when decompressing.
|
||||
*/
|
||||
TJPF_RGBX,
|
||||
/**
|
||||
* BGRX pixel format. The red, green, and blue components in the image are
|
||||
* stored in 4-byte pixels in the order B, G, R from lowest to highest byte
|
||||
* address within each pixel. The X component is ignored when compressing
|
||||
* and undefined when decompressing.
|
||||
*/
|
||||
TJPF_BGRX,
|
||||
/**
|
||||
* XBGR pixel format. The red, green, and blue components in the image are
|
||||
* stored in 4-byte pixels in the order R, G, B from highest to lowest byte
|
||||
* address within each pixel. The X component is ignored when compressing
|
||||
* and undefined when decompressing.
|
||||
*/
|
||||
TJPF_XBGR,
|
||||
/**
|
||||
* XRGB pixel format. The red, green, and blue components in the image are
|
||||
* stored in 4-byte pixels in the order B, G, R from highest to lowest byte
|
||||
* address within each pixel. The X component is ignored when compressing
|
||||
* and undefined when decompressing.
|
||||
*/
|
||||
TJPF_XRGB,
|
||||
/**
|
||||
* Grayscale pixel format. Each 1-byte pixel represents a luminance
|
||||
* (brightness) level from 0 to 255.
|
||||
*/
|
||||
TJPF_GRAY,
|
||||
/**
|
||||
* RGBA pixel format. This is the same as @ref TJPF_RGBX, except that when
|
||||
* decompressing, the X component is guaranteed to be 0xFF, which can be
|
||||
* interpreted as an opaque alpha channel.
|
||||
*/
|
||||
TJPF_RGBA,
|
||||
/**
|
||||
* BGRA pixel format. This is the same as @ref TJPF_BGRX, except that when
|
||||
* decompressing, the X component is guaranteed to be 0xFF, which can be
|
||||
* interpreted as an opaque alpha channel.
|
||||
*/
|
||||
TJPF_BGRA,
|
||||
/**
|
||||
* ABGR pixel format. This is the same as @ref TJPF_XBGR, except that when
|
||||
* decompressing, the X component is guaranteed to be 0xFF, which can be
|
||||
* interpreted as an opaque alpha channel.
|
||||
*/
|
||||
TJPF_ABGR,
|
||||
/**
|
||||
* ARGB pixel format. This is the same as @ref TJPF_XRGB, except that when
|
||||
* decompressing, the X component is guaranteed to be 0xFF, which can be
|
||||
* interpreted as an opaque alpha channel.
|
||||
*/
|
||||
TJPF_ARGB
|
||||
};
|
||||
|
||||
/**
|
||||
* Red offset (in bytes) for a given pixel format. This specifies the number
|
||||
* of bytes that the red component is offset from the start of the pixel. For
|
||||
* instance, if a pixel of format TJ_BGRX is stored in <tt>char pixel[]</tt>,
|
||||
* then the red component will be <tt>pixel[tjRedOffset[TJ_BGRX]]</tt>.
|
||||
*/
|
||||
static const int tjRedOffset[TJ_NUMPF] = {0, 2, 0, 2, 3, 1, 0, 0, 2, 3, 1};
|
||||
/**
|
||||
* Green offset (in bytes) for a given pixel format. This specifies the number
|
||||
* of bytes that the green component is offset from the start of the pixel.
|
||||
* For instance, if a pixel of format TJ_BGRX is stored in
|
||||
* <tt>char pixel[]</tt>, then the green component will be
|
||||
* <tt>pixel[tjGreenOffset[TJ_BGRX]]</tt>.
|
||||
*/
|
||||
static const int tjGreenOffset[TJ_NUMPF] = {1, 1, 1, 1, 2, 2, 0, 1, 1, 2, 2};
|
||||
/**
|
||||
* Blue offset (in bytes) for a given pixel format. This specifies the number
|
||||
* of bytes that the Blue component is offset from the start of the pixel. For
|
||||
* instance, if a pixel of format TJ_BGRX is stored in <tt>char pixel[]</tt>,
|
||||
* then the blue component will be <tt>pixel[tjBlueOffset[TJ_BGRX]]</tt>.
|
||||
*/
|
||||
static const int tjBlueOffset[TJ_NUMPF] = {2, 0, 2, 0, 1, 3, 0, 2, 0, 1, 3};
|
||||
|
||||
/**
|
||||
* Pixel size (in bytes) for a given pixel format.
|
||||
*/
|
||||
static const int tjPixelSize[TJ_NUMPF] = {3, 3, 4, 4, 4, 4, 1, 4, 4, 4, 4};
|
||||
|
||||
|
||||
/**
|
||||
* The uncompressed source/destination image is stored in bottom-up (Windows,
|
||||
* OpenGL) order, not top-down (X11) order.
|
||||
*/
|
||||
#define TJFLAG_BOTTOMUP 2
|
||||
/**
|
||||
* Turn off CPU auto-detection and force TurboJPEG to use MMX code (IPP and
|
||||
* 32-bit libjpeg-turbo versions only.)
|
||||
*/
|
||||
#define TJFLAG_FORCEMMX 8
|
||||
/**
|
||||
* Turn off CPU auto-detection and force TurboJPEG to use SSE code (32-bit IPP
|
||||
* and 32-bit libjpeg-turbo versions only)
|
||||
*/
|
||||
#define TJFLAG_FORCESSE 16
|
||||
/**
|
||||
* Turn off CPU auto-detection and force TurboJPEG to use SSE2 code (32-bit IPP
|
||||
* and 32-bit libjpeg-turbo versions only)
|
||||
*/
|
||||
#define TJFLAG_FORCESSE2 32
|
||||
/**
|
||||
* Turn off CPU auto-detection and force TurboJPEG to use SSE3 code (64-bit IPP
|
||||
* version only)
|
||||
*/
|
||||
#define TJFLAG_FORCESSE3 128
|
||||
/**
|
||||
* Use fast, inaccurate chrominance upsampling routines in the JPEG
|
||||
* decompressor (libjpeg and libjpeg-turbo versions only)
|
||||
*/
|
||||
#define TJFLAG_FASTUPSAMPLE 256
|
||||
|
||||
|
||||
/**
|
||||
* Scaling factor
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/**
|
||||
* Numerator
|
||||
*/
|
||||
int num;
|
||||
/**
|
||||
* Denominator
|
||||
*/
|
||||
int denom;
|
||||
} tjscalingfactor;
|
||||
|
||||
|
||||
/**
|
||||
* TurboJPEG instance handle
|
||||
*/
|
||||
typedef void* tjhandle;
|
||||
|
||||
|
||||
/**
|
||||
* Pad the given width to the nearest 32-bit boundary
|
||||
*/
|
||||
#define TJPAD(width) (((width)+3)&(~3))
|
||||
|
||||
/**
|
||||
* Compute the scaled value of <tt>dimension</tt> using the given scaling
|
||||
* factor. This macro performs the integer equivalent of <tt>ceil(dimension *
|
||||
* scalingFactor)</tt>.
|
||||
*/
|
||||
#define TJSCALED(dimension, scalingFactor) ((dimension * scalingFactor.num \
|
||||
+ scalingFactor.denom - 1) / scalingFactor.denom)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Create a TurboJPEG compressor instance.
|
||||
*
|
||||
* @return a handle to the newly-created instance, or NULL if an error
|
||||
* occurred (see #tjGetErrorStr().)
|
||||
*/
|
||||
DLLEXPORT tjhandle DLLCALL tjInitCompress(void);
|
||||
|
||||
|
||||
/**
|
||||
* Compress an RGB or grayscale image into a JPEG image.
|
||||
*
|
||||
* @param handle a handle to a TurboJPEG compressor or transformer instance
|
||||
* @param srcBuf pointer to an image buffer containing RGB or grayscale pixels
|
||||
* to be compressed
|
||||
* @param width width (in pixels) of the source image
|
||||
* @param pitch bytes per line of the source image. Normally, this should be
|
||||
* <tt>width * #tjPixelSize[pixelFormat]</tt> if the image is unpadded,
|
||||
* or <tt>#TJPAD(width * #tjPixelSize[pixelFormat])</tt> if each line of
|
||||
* the image is padded to the nearest 32-bit boundary, as is the case
|
||||
* for Windows bitmaps. You can also be clever and use this parameter
|
||||
* to skip lines, etc. Setting this parameter to 0 is the equivalent of
|
||||
* setting it to <tt>width * #tjPixelSize[pixelFormat]</tt>.
|
||||
* @param height height (in pixels) of the source image
|
||||
* @param pixelFormat pixel format of the source image (see @ref TJPF
|
||||
* "Pixel formats".)
|
||||
* @param jpegBuf address of a pointer to an image buffer that will receive the
|
||||
* JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer
|
||||
* to accommodate the size of the JPEG image. Thus, you can choose to:
|
||||
* -# pre-allocate the JPEG buffer with an arbitrary size using
|
||||
* #tjAlloc() and let TurboJPEG grow the buffer as needed,
|
||||
* -# set <tt>*jpegBuf</tt> to NULL to tell TurboJPEG to allocate the
|
||||
* buffer for you, or
|
||||
* -# pre-allocate the buffer to a "worst case" size determined by
|
||||
* calling #tjBufSize(). This should ensure that the buffer never has
|
||||
* to be re-allocated (setting #TJFLAG_NOREALLOC guarantees this.)
|
||||
* .
|
||||
* If you choose option 1, <tt>*jpegSize</tt> should be set to the
|
||||
* size of your pre-allocated buffer. In any case, unless you have
|
||||
* set #TJFLAG_NOREALLOC, you should always check <tt>*jpegBuf</tt> upon
|
||||
* return from this function, as it may have changed.
|
||||
* @param jpegSize pointer to an unsigned long variable that holds the size of
|
||||
* the JPEG image buffer. If <tt>*jpegBuf</tt> points to a
|
||||
* pre-allocated buffer, then <tt>*jpegSize</tt> should be set to the
|
||||
* size of the buffer. Upon return, <tt>*jpegSize</tt> will contain the
|
||||
* size of the JPEG image (in bytes.)
|
||||
* @param jpegSubsamp the level of chrominance subsampling to be used when
|
||||
* generating the JPEG image (see @ref TJSAMP
|
||||
* "Chrominance subsampling options".)
|
||||
* @param jpegQual the image quality of the generated JPEG image (1 = worst,
|
||||
100 = best)
|
||||
* @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP
|
||||
* "flags".
|
||||
*
|
||||
* @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().)
|
||||
*/
|
||||
DLLEXPORT int DLLCALL tjCompress2(tjhandle handle, unsigned char *srcBuf,
|
||||
int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf,
|
||||
unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags);
|
||||
|
||||
|
||||
/**
|
||||
* The maximum size of the buffer (in bytes) required to hold a JPEG image with
|
||||
* the given parameters. The number of bytes returned by this function is
|
||||
* larger than the size of the uncompressed source image. The reason for this
|
||||
* is that the JPEG format uses 16-bit coefficients, and it is thus possible
|
||||
* for a very high-quality JPEG image with very high frequency content to
|
||||
* expand rather than compress when converted to the JPEG format. Such images
|
||||
* represent a very rare corner case, but since there is no way to predict the
|
||||
* size of a JPEG image prior to compression, the corner case has to be
|
||||
* handled.
|
||||
*
|
||||
* @param width width of the image (in pixels)
|
||||
* @param height height of the image (in pixels)
|
||||
* @param jpegSubsamp the level of chrominance subsampling to be used when
|
||||
* generating the JPEG image (see @ref TJSAMP
|
||||
* "Chrominance subsampling options".)
|
||||
*
|
||||
* @return the maximum size of the buffer (in bytes) required to hold the
|
||||
* image, or -1 if the arguments are out of bounds.
|
||||
*/
|
||||
DLLEXPORT unsigned long DLLCALL tjBufSize(int width, int height,
|
||||
int jpegSubsamp);
|
||||
|
||||
|
||||
/**
|
||||
* Create a TurboJPEG decompressor instance.
|
||||
*
|
||||
* @return a handle to the newly-created instance, or NULL if an error
|
||||
* occurred (see #tjGetErrorStr().)
|
||||
*/
|
||||
DLLEXPORT tjhandle DLLCALL tjInitDecompress(void);
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve information about a JPEG image without decompressing it.
|
||||
*
|
||||
* @param handle a handle to a TurboJPEG decompressor or transformer instance
|
||||
* @param jpegBuf pointer to a buffer containing a JPEG image
|
||||
* @param jpegSize size of the JPEG image (in bytes)
|
||||
* @param width pointer to an integer variable that will receive the width (in
|
||||
* pixels) of the JPEG image
|
||||
* @param height pointer to an integer variable that will receive the height
|
||||
* (in pixels) of the JPEG image
|
||||
* @param jpegSubsamp pointer to an integer variable that will receive the
|
||||
* level of chrominance subsampling used when compressing the JPEG image
|
||||
* (see @ref TJSAMP "Chrominance subsampling options".)
|
||||
*
|
||||
* @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().)
|
||||
*/
|
||||
DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle handle,
|
||||
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height,
|
||||
int *jpegSubsamp);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of fractional scaling factors that the JPEG decompressor in
|
||||
* this implementation of TurboJPEG supports.
|
||||
*
|
||||
* @param numscalingfactors pointer to an integer variable that will receive
|
||||
* the number of elements in the list
|
||||
*
|
||||
* @return a pointer to a list of fractional scaling factors, or NULL if an
|
||||
* error is encountered (see #tjGetErrorStr().)
|
||||
*/
|
||||
DLLEXPORT tjscalingfactor* DLLCALL tjGetScalingFactors(int *numscalingfactors);
|
||||
|
||||
|
||||
/**
|
||||
* Decompress a JPEG image to an RGB or grayscale image.
|
||||
*
|
||||
* @param handle a handle to a TurboJPEG decompressor or transformer instance
|
||||
* @param jpegBuf pointer to a buffer containing the JPEG image to decompress
|
||||
* @param jpegSize size of the JPEG image (in bytes)
|
||||
* @param dstBuf pointer to an image buffer that will receive the decompressed
|
||||
* image. This buffer should normally be <tt>pitch * scaledHeight</tt>
|
||||
* bytes in size, where <tt>scaledHeight</tt> can be determined by
|
||||
* calling #TJSCALED() with the JPEG image height and one of the scaling
|
||||
* factors returned by #tjGetScalingFactors(). The dstBuf pointer may
|
||||
* also be used to decompress into a specific region of a larger buffer.
|
||||
* @param width desired width (in pixels) of the destination image. If this is
|
||||
* smaller than the width of the JPEG image being decompressed, then
|
||||
* TurboJPEG will use scaling in the JPEG decompressor to generate the
|
||||
* largest possible image that will fit within the desired width. If
|
||||
* width is set to 0, then only the height will be considered when
|
||||
* determining the scaled image size.
|
||||
* @param pitch bytes per line of the destination image. Normally, this is
|
||||
* <tt>scaledWidth * #tjPixelSize[pixelFormat]</tt> if the decompressed
|
||||
* image is unpadded, else <tt>#TJPAD(scaledWidth *
|
||||
* #tjPixelSize[pixelFormat])</tt> if each line of the decompressed
|
||||
* image is padded to the nearest 32-bit boundary, as is the case for
|
||||
* Windows bitmaps. (NOTE: <tt>scaledWidth</tt> can be determined by
|
||||
* calling #TJSCALED() with the JPEG image width and one of the scaling
|
||||
* factors returned by #tjGetScalingFactors().) You can also be clever
|
||||
* and use the pitch parameter to skip lines, etc. Setting this
|
||||
* parameter to 0 is the equivalent of setting it to <tt>scaledWidth
|
||||
* * #tjPixelSize[pixelFormat]</tt>.
|
||||
* @param height desired height (in pixels) of the destination image. If this
|
||||
* is smaller than the height of the JPEG image being decompressed, then
|
||||
* TurboJPEG will use scaling in the JPEG decompressor to generate the
|
||||
* largest possible image that will fit within the desired height. If
|
||||
* height is set to 0, then only the width will be considered when
|
||||
* determining the scaled image size.
|
||||
* @param pixelFormat pixel format of the destination image (see @ref
|
||||
* TJPF "Pixel formats".)
|
||||
* @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP
|
||||
* "flags".
|
||||
*
|
||||
* @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().)
|
||||
*/
|
||||
DLLEXPORT int DLLCALL tjDecompress2(tjhandle handle,
|
||||
unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf,
|
||||
int width, int pitch, int height, int pixelFormat, int flags);
|
||||
|
||||
|
||||
/**
|
||||
* Destroy a TurboJPEG compressor, decompressor, or transformer instance.
|
||||
*
|
||||
* @param handle a handle to a TurboJPEG compressor, decompressor or
|
||||
* transformer instance
|
||||
*
|
||||
* @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().)
|
||||
*/
|
||||
DLLEXPORT int DLLCALL tjDestroy(tjhandle handle);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a descriptive error message explaining why the last command failed.
|
||||
*
|
||||
* @return a descriptive error message explaining why the last command failed.
|
||||
*/
|
||||
DLLEXPORT char* DLLCALL tjGetErrorStr(void);
|
||||
|
||||
|
||||
/* Backward compatibility functions and macros (nothing to see here) */
|
||||
#define NUMSUBOPT TJ_NUMSAMP
|
||||
#define TJ_444 TJSAMP_444
|
||||
#define TJ_422 TJSAMP_422
|
||||
#define TJ_420 TJSAMP_420
|
||||
#define TJ_411 TJSAMP_420
|
||||
#define TJ_GRAYSCALE TJSAMP_GRAY
|
||||
|
||||
#define TJ_BGR 1
|
||||
#define TJ_BOTTOMUP TJFLAG_BOTTOMUP
|
||||
#define TJ_FORCEMMX TJFLAG_FORCEMMX
|
||||
#define TJ_FORCESSE TJFLAG_FORCESSE
|
||||
#define TJ_FORCESSE2 TJFLAG_FORCESSE2
|
||||
#define TJ_ALPHAFIRST 64
|
||||
#define TJ_FORCESSE3 TJFLAG_FORCESSE3
|
||||
#define TJ_FASTUPSAMPLE TJFLAG_FASTUPSAMPLE
|
||||
|
||||
DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height);
|
||||
|
||||
DLLEXPORT int DLLCALL tjCompress(tjhandle handle, unsigned char *srcBuf,
|
||||
int width, int pitch, int height, int pixelSize, unsigned char *dstBuf,
|
||||
unsigned long *compressedSize, int jpegSubsamp, int jpegQual, int flags);
|
||||
|
||||
DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle handle,
|
||||
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height);
|
||||
|
||||
DLLEXPORT int DLLCALL tjDecompress(tjhandle handle,
|
||||
unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf,
|
||||
int width, int pitch, int height, int pixelSize, int flags);
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
85
meson.build
85
meson.build
|
@ -28,6 +28,7 @@ cc = meson.get_compiler('c')
|
|||
|
||||
libm = cc.find_library('m', required: false)
|
||||
librt = cc.find_library('rt', required: false)
|
||||
pthread = cc.find_library('pthread', required: false)
|
||||
|
||||
xkbcommon = dependency('xkbcommon')
|
||||
pixman = dependency('pixman-1')
|
||||
|
@ -39,20 +40,14 @@ egl = dependency('egl')
|
|||
glesv2 = dependency('glesv2')
|
||||
lavc = dependency('libavcodec')
|
||||
lavu = dependency('libavutil')
|
||||
|
||||
libvncserver_opt = cmake.subproject_options()
|
||||
libvncserver_opt.add_cmake_defines({
|
||||
'BUILD_SHARED_LIBS': false,
|
||||
'LIBVNCSERVER_INSTALL': false,
|
||||
})
|
||||
|
||||
libvncserver_project = cmake.subproject('libvncserver', required: false,
|
||||
options: libvncserver_opt)
|
||||
if libvncserver_project.found()
|
||||
libvncclient = libvncserver_project.dependency('vncclient')
|
||||
else
|
||||
libvncclient = dependency('libvncclient')
|
||||
endif
|
||||
gcrypt = dependency('libgcrypt', required: false)
|
||||
openssl = dependency('openssl', required: false)
|
||||
gnutls = dependency('gnutls', required: false)
|
||||
sasl = dependency('libsasl2', required: false)
|
||||
libjpeg = dependency('libjpeg', required: false)
|
||||
libpng = dependency('libpng', required: false)
|
||||
lzo = dependency('lzo2', required: false)
|
||||
libz = dependency('zlib', required: false)
|
||||
|
||||
aml_project = subproject('aml', required: false,
|
||||
default_options: ['default_library=static'])
|
||||
|
@ -62,7 +57,7 @@ else
|
|||
aml = dependency('aml')
|
||||
endif
|
||||
|
||||
inc = include_directories('include')
|
||||
inc = include_directories('include', 'src/encodings')
|
||||
|
||||
subdir('protocols')
|
||||
|
||||
|
@ -82,6 +77,12 @@ sources = [
|
|||
'src/renderer-egl.c',
|
||||
'src/buffer.c',
|
||||
'src/open-h264.c',
|
||||
'src/cursor.c',
|
||||
'src/listen.c',
|
||||
'src/rfbproto.c',
|
||||
'src/sockets.c',
|
||||
'src/vncviewer.c',
|
||||
'src/turbojpeg.c',
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
|
@ -98,7 +99,6 @@ dependencies = [
|
|||
glesv2,
|
||||
lavc,
|
||||
lavu,
|
||||
libvncclient,
|
||||
client_protos,
|
||||
]
|
||||
|
||||
|
@ -106,6 +106,59 @@ config = configuration_data()
|
|||
|
||||
config.set('PREFIX', '"' + prefix + '"')
|
||||
|
||||
if gcrypt.found()
|
||||
sources += 'src/crypto_libgcrypt.c'
|
||||
dependencies += gcrypt
|
||||
elif openssl.found()
|
||||
sources += 'src/crypto_openssl.c'
|
||||
dependencies += openssl
|
||||
else
|
||||
sources += 'src/crypto_included.c'
|
||||
endif
|
||||
|
||||
if gnutls.found()
|
||||
sources += 'src/tls_gnutls.c'
|
||||
dependencies += gnutls
|
||||
config.set('LIBVNCSERVER_HAVE_GNUTLS', true)
|
||||
elif openssl.found()
|
||||
sources += 'src/tls_openssl.c'
|
||||
dependencies += openssl
|
||||
config.set('LIBVNCSERVER_HAVE_LIBSSL', true)
|
||||
else
|
||||
sources += 'src/tls_none.c'
|
||||
endif
|
||||
|
||||
if sasl.found()
|
||||
dependencies += sasl
|
||||
sources += 'src/sasl.c'
|
||||
config.set('LIBVNCSERVER_HAVE_SASL', true)
|
||||
endif
|
||||
|
||||
if libjpeg.found()
|
||||
dependencies += libjpeg
|
||||
config.set('LIBVNCSERVER_HAVE_LIBJPEG', true)
|
||||
endif
|
||||
|
||||
if libpng.found()
|
||||
dependencies += libpng
|
||||
config.set('LIBVNCSERVER_HAVE_LIBPNG', true)
|
||||
endif
|
||||
|
||||
if pthread.found()
|
||||
dependencies += pthread
|
||||
config.set('LIBVNCSERVER_HAVE_PTHREAD', true)
|
||||
endif
|
||||
|
||||
if libz.found()
|
||||
dependencies += libz
|
||||
config.set('LIBVNCSERVER_HAVE_LIBZ', true)
|
||||
endif
|
||||
|
||||
if lzo.found()
|
||||
dependencies += lzo
|
||||
config.set('LIBVNCSERVER_HAVE_LZO', true)
|
||||
endif
|
||||
|
||||
if host_system == 'linux' and cc.has_header('sys/sdt.h')
|
||||
config.set('HAVE_USDT', true)
|
||||
endif
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* crypto_included.c - Crypto wrapper (included version)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011 Gernot Tenchio
|
||||
* Copyright (C) 2019 Christian Beier
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "sha.h"
|
||||
#include "d3des.h"
|
||||
#include "crypto.h"
|
||||
|
||||
|
||||
int hash_md5(void *out, const void *in, const size_t in_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hash_sha1(void *out, const void *in, const size_t in_len)
|
||||
{
|
||||
SHA1Context sha1;
|
||||
if(SHA1Reset(&sha1) != shaSuccess)
|
||||
return 0;
|
||||
if(SHA1Input(&sha1, in, in_len) != shaSuccess)
|
||||
return 0;
|
||||
if(SHA1Result(&sha1, out) != shaSuccess)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void random_bytes(void *out, size_t len)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int encrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len)
|
||||
{
|
||||
int eightbyteblocks = in_len/8;
|
||||
int i;
|
||||
rfbDesKey((unsigned char*)key, EN0);
|
||||
for(i = 0; i < eightbyteblocks; ++i)
|
||||
rfbDes((unsigned char*)in + i*8, (unsigned char*)out + i*8);
|
||||
|
||||
*out_len = in_len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int decrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len)
|
||||
{
|
||||
int eightbyteblocks = in_len/8;
|
||||
int i;
|
||||
rfbDesKey((unsigned char*)key, DE1);
|
||||
for(i = 0; i < eightbyteblocks; ++i)
|
||||
rfbDes((unsigned char*)in + i*8, (unsigned char*)out + i*8);
|
||||
|
||||
*out_len = in_len;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int encrypt_aes128ecb(void *out, int *out_len, const unsigned char key[16], const void *in, const size_t in_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dh_generate_keypair(uint8_t *priv_out, uint8_t *pub_out, const uint8_t *gen, const size_t gen_len, const uint8_t *prime, const size_t keylen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dh_compute_shared_key(uint8_t *shared_out, const uint8_t *priv, const uint8_t *pub, const uint8_t *prime, const size_t keylen)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* crypto_gnutls.c - Crypto wrapper (libgcrypt version)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011 Gernot Tenchio
|
||||
* Copyright (C) 2019 Christian Beier
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <gcrypt.h>
|
||||
#include "crypto.h"
|
||||
|
||||
static int mpiToBytes(const gcry_mpi_t value, uint8_t *result, size_t size)
|
||||
{
|
||||
gcry_error_t error;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
error = gcry_mpi_print(GCRYMPI_FMT_USG, result, size, &len, value);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
return 0;
|
||||
for (i=size-1;i>(int)size-1-(int)len;--i)
|
||||
result[i] = result[i-size+len];
|
||||
for (;i>=0;--i)
|
||||
result[i] = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned char reverseByte(unsigned char b) {
|
||||
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
|
||||
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
|
||||
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
|
||||
return b;
|
||||
}
|
||||
|
||||
int hash_md5(void *out, const void *in, const size_t in_len)
|
||||
{
|
||||
int result = 0;
|
||||
gcry_error_t error;
|
||||
gcry_md_hd_t md5 = NULL;
|
||||
void *digest;
|
||||
|
||||
error = gcry_md_open(&md5, GCRY_MD_MD5, 0);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
gcry_md_write(md5, in, in_len);
|
||||
|
||||
if(!(digest = gcry_md_read(md5, GCRY_MD_MD5)))
|
||||
goto out;
|
||||
|
||||
memcpy(out, digest, gcry_md_get_algo_dlen(GCRY_MD_MD5));
|
||||
|
||||
result = 1;
|
||||
|
||||
out:
|
||||
gcry_md_close(md5);
|
||||
return result;
|
||||
}
|
||||
|
||||
int hash_sha1(void *out, const void *in, const size_t in_len)
|
||||
{
|
||||
int result = 0;
|
||||
gcry_error_t error;
|
||||
gcry_md_hd_t sha1 = NULL;
|
||||
void *digest;
|
||||
|
||||
error = gcry_md_open(&sha1, GCRY_MD_SHA1, 0);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
gcry_md_write(sha1, in, in_len);
|
||||
|
||||
if(!(digest = gcry_md_read(sha1, GCRY_MD_SHA1)))
|
||||
goto out;
|
||||
|
||||
memcpy(out, digest, gcry_md_get_algo_dlen(GCRY_MD_SHA1));
|
||||
|
||||
result = 1;
|
||||
|
||||
out:
|
||||
gcry_md_close(sha1);
|
||||
return result;
|
||||
}
|
||||
|
||||
void random_bytes(void *out, size_t len)
|
||||
{
|
||||
gcry_randomize(out, len, GCRY_STRONG_RANDOM);
|
||||
}
|
||||
|
||||
int encrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len)
|
||||
{
|
||||
int result = 0;
|
||||
gcry_error_t error;
|
||||
gcry_cipher_hd_t des = NULL;
|
||||
unsigned char mungedkey[8];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
mungedkey[i] = reverseByte(key[i]);
|
||||
|
||||
error = gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
error = gcry_cipher_setkey(des, mungedkey, 8);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
error = gcry_cipher_encrypt(des, out, in_len, in, in_len);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
*out_len = in_len;
|
||||
|
||||
result = 1;
|
||||
|
||||
out:
|
||||
gcry_cipher_close(des);
|
||||
return result;
|
||||
}
|
||||
|
||||
int decrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len)
|
||||
{
|
||||
int result = 0;
|
||||
gcry_error_t error;
|
||||
gcry_cipher_hd_t des = NULL;
|
||||
unsigned char mungedkey[8];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
mungedkey[i] = reverseByte(key[i]);
|
||||
|
||||
error = gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
error = gcry_cipher_setkey(des, mungedkey, 8);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
error = gcry_cipher_decrypt(des, out, in_len, in, in_len);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
*out_len = in_len;
|
||||
|
||||
result = 1;
|
||||
|
||||
out:
|
||||
gcry_cipher_close(des);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int encrypt_aes128ecb(void *out, int *out_len, const unsigned char key[16], const void *in, const size_t in_len)
|
||||
{
|
||||
int result = 0;
|
||||
gcry_error_t error;
|
||||
gcry_cipher_hd_t aes = NULL;
|
||||
|
||||
error = gcry_cipher_open(&aes, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB, 0);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
error = gcry_cipher_setkey(aes, key, 16);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
error = gcry_cipher_encrypt(aes, out, in_len, in, in_len);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
*out_len = in_len;
|
||||
|
||||
result = 1;
|
||||
|
||||
out:
|
||||
gcry_cipher_close(aes);
|
||||
return result;
|
||||
}
|
||||
|
||||
int dh_generate_keypair(uint8_t *priv_out, uint8_t *pub_out, const uint8_t *gen, const size_t gen_len, const uint8_t *prime, const size_t keylen)
|
||||
{
|
||||
int result = 0;
|
||||
gcry_error_t error;
|
||||
gcry_mpi_t genmpi = NULL, modmpi = NULL, privmpi = NULL, pubmpi = NULL;
|
||||
|
||||
error = gcry_mpi_scan(&genmpi, GCRYMPI_FMT_USG, gen, gen_len, NULL);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
error = gcry_mpi_scan(&modmpi, GCRYMPI_FMT_USG, prime, keylen, NULL);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
privmpi = gcry_mpi_new(keylen);
|
||||
if (!privmpi)
|
||||
goto out;
|
||||
gcry_mpi_randomize(privmpi, (keylen/8)*8, GCRY_STRONG_RANDOM);
|
||||
|
||||
pubmpi = gcry_mpi_new(keylen);
|
||||
if (!pubmpi)
|
||||
goto out;
|
||||
|
||||
gcry_mpi_powm(pubmpi, genmpi, privmpi, modmpi);
|
||||
|
||||
if (!mpiToBytes(pubmpi, pub_out, keylen))
|
||||
goto out;
|
||||
if (!mpiToBytes(privmpi, priv_out, keylen))
|
||||
goto out;
|
||||
|
||||
result = 1;
|
||||
|
||||
out:
|
||||
gcry_mpi_release(genmpi);
|
||||
gcry_mpi_release(modmpi);
|
||||
gcry_mpi_release(privmpi);
|
||||
gcry_mpi_release(pubmpi);
|
||||
return result;
|
||||
}
|
||||
|
||||
int dh_compute_shared_key(uint8_t *shared_out, const uint8_t *priv, const uint8_t *pub, const uint8_t *prime, const size_t keylen)
|
||||
{
|
||||
int result = 1;
|
||||
gcry_error_t error;
|
||||
gcry_mpi_t keympi = NULL, modmpi = NULL, privmpi = NULL, pubmpi = NULL;
|
||||
|
||||
error = gcry_mpi_scan(&privmpi, GCRYMPI_FMT_USG, priv, keylen, NULL);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
error = gcry_mpi_scan(&pubmpi, GCRYMPI_FMT_USG, pub, keylen, NULL);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
error = gcry_mpi_scan(&modmpi, GCRYMPI_FMT_USG, prime, keylen, NULL);
|
||||
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
keympi = gcry_mpi_new(keylen);
|
||||
if (!keympi)
|
||||
goto out;
|
||||
|
||||
gcry_mpi_powm(keympi, pubmpi, privmpi, modmpi);
|
||||
|
||||
if (!mpiToBytes(keympi, shared_out, keylen))
|
||||
goto out;
|
||||
|
||||
result = 1;
|
||||
|
||||
out:
|
||||
gcry_mpi_release(keympi);
|
||||
gcry_mpi_release(modmpi);
|
||||
gcry_mpi_release(privmpi);
|
||||
gcry_mpi_release(pubmpi);
|
||||
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* crypto_openssl.c - Crypto wrapper (openssl version)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011 Gernot Tenchio
|
||||
* Copyright (C) 2019 Christian Beier
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/md5.h>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
#include "crypto.h"
|
||||
|
||||
static unsigned char reverseByte(unsigned char b) {
|
||||
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
|
||||
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
|
||||
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
|
||||
return b;
|
||||
}
|
||||
|
||||
int hash_md5(void *out, const void *in, const size_t in_len)
|
||||
{
|
||||
MD5_CTX md5;
|
||||
if(!MD5_Init(&md5))
|
||||
return 0;
|
||||
if(!MD5_Update(&md5, in, in_len))
|
||||
return 0;
|
||||
if(!MD5_Final(out, &md5))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int hash_sha1(void *out, const void *in, const size_t in_len)
|
||||
{
|
||||
SHA_CTX sha1;
|
||||
if(!SHA1_Init(&sha1))
|
||||
return 0;
|
||||
if(!SHA1_Update(&sha1, in, in_len))
|
||||
return 0;
|
||||
if(!SHA1_Final(out, &sha1))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void random_bytes(void *out, size_t len)
|
||||
{
|
||||
RAND_bytes(out, len);
|
||||
}
|
||||
|
||||
int encrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len)
|
||||
{
|
||||
int result = 0;
|
||||
EVP_CIPHER_CTX *des;
|
||||
unsigned char mungedkey[8];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
mungedkey[i] = reverseByte(key[i]);
|
||||
|
||||
if(!(des = EVP_CIPHER_CTX_new()))
|
||||
goto out;
|
||||
if(!EVP_EncryptInit_ex(des, EVP_des_ecb(), NULL, mungedkey, NULL))
|
||||
goto out;
|
||||
if(!EVP_EncryptUpdate(des, out, out_len, in, in_len))
|
||||
goto out;
|
||||
|
||||
result = 1;
|
||||
|
||||
out:
|
||||
EVP_CIPHER_CTX_free(des);
|
||||
return result;
|
||||
}
|
||||
|
||||
int decrypt_rfbdes(void *out, int *out_len, const unsigned char key[8], const void *in, const size_t in_len)
|
||||
{
|
||||
int result = 0;
|
||||
EVP_CIPHER_CTX *des;
|
||||
unsigned char mungedkey[8];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
mungedkey[i] = reverseByte(key[i]);
|
||||
|
||||
if(!(des = EVP_CIPHER_CTX_new()))
|
||||
goto out;
|
||||
if(!EVP_DecryptInit_ex(des, EVP_des_ecb(), NULL, mungedkey, NULL))
|
||||
goto out;
|
||||
if(!EVP_DecryptUpdate(des, out, out_len, in, in_len))
|
||||
goto out;
|
||||
|
||||
result = 1;
|
||||
|
||||
out:
|
||||
EVP_CIPHER_CTX_free(des);
|
||||
return result;
|
||||
}
|
||||
|
||||
int encrypt_aes128ecb(void *out, int *out_len, const unsigned char key[16], const void *in, const size_t in_len)
|
||||
{
|
||||
int result = 0;
|
||||
EVP_CIPHER_CTX *aes;
|
||||
|
||||
if(!(aes = EVP_CIPHER_CTX_new()))
|
||||
goto out;
|
||||
EVP_CIPHER_CTX_set_padding(aes, 0);
|
||||
if(!EVP_EncryptInit_ex(aes, EVP_aes_128_ecb(), NULL, key, NULL))
|
||||
goto out;
|
||||
if(!EVP_EncryptUpdate(aes, out, out_len, in, in_len))
|
||||
goto out;
|
||||
|
||||
result = 1;
|
||||
|
||||
out:
|
||||
EVP_CIPHER_CTX_free(aes);
|
||||
return result;
|
||||
}
|
||||
|
||||
int dh_generate_keypair(uint8_t *priv_out, uint8_t *pub_out, const uint8_t *gen, const size_t gen_len, const uint8_t *prime, const size_t keylen)
|
||||
{
|
||||
int result = 0;
|
||||
DH *dh;
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L || \
|
||||
(defined (LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x30500000)
|
||||
const BIGNUM *pub_key = NULL;
|
||||
const BIGNUM *priv_key = NULL;
|
||||
#endif
|
||||
|
||||
if(!(dh = DH_new()))
|
||||
goto out;
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
|
||||
(defined (LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x30500000)
|
||||
dh->p = BN_bin2bn(prime, keylen, NULL);
|
||||
dh->g = BN_bin2bn(gen, gen_len, NULL);
|
||||
#else
|
||||
if(!DH_set0_pqg(dh, BN_bin2bn(prime, keylen, NULL), NULL, BN_bin2bn(gen, gen_len, NULL)))
|
||||
goto out;
|
||||
#endif
|
||||
if(!DH_generate_key(dh))
|
||||
goto out;
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
|
||||
(defined (LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x30500000)
|
||||
if(BN_bn2bin(dh->priv_key, priv_out) == 0)
|
||||
goto out;
|
||||
if(BN_bn2bin(dh->pub_key, pub_out) == 0)
|
||||
goto out;
|
||||
#else
|
||||
DH_get0_key(dh, &pub_key, &priv_key);
|
||||
if(BN_bn2binpad(priv_key, priv_out, keylen) == -1)
|
||||
goto out;
|
||||
if(BN_bn2binpad(pub_key, pub_out, keylen) == -1)
|
||||
goto out;
|
||||
#endif
|
||||
|
||||
result = 1;
|
||||
|
||||
out:
|
||||
DH_free(dh);
|
||||
return result;
|
||||
}
|
||||
|
||||
int dh_compute_shared_key(uint8_t *shared_out, const uint8_t *priv, const uint8_t *pub, const uint8_t *prime, const size_t keylen)
|
||||
{
|
||||
int result = 0;
|
||||
DH *dh;
|
||||
|
||||
if(!(dh = DH_new()))
|
||||
goto out;
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
|
||||
(defined LIBRESSL_VERSION_NUMBER && LIBRESSL_VERSION_NUMBER < 0x30500000)
|
||||
dh->p = BN_bin2bn(prime, keylen, NULL);
|
||||
dh->priv_key = BN_bin2bn(priv, keylen, NULL);
|
||||
#else
|
||||
if(!DH_set0_pqg(dh, BN_bin2bn(prime, keylen, NULL), NULL, BN_new()))
|
||||
goto out;
|
||||
if(!DH_set0_key(dh, NULL, BN_bin2bn(priv, keylen, NULL)))
|
||||
goto out;
|
||||
#endif
|
||||
if(DH_compute_key(shared_out, BN_bin2bn(pub, keylen, NULL), dh) == -1)
|
||||
goto out;
|
||||
|
||||
result = 1;
|
||||
|
||||
out:
|
||||
DH_free(dh);
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Copyright (C) 2001,2002 Constantin Kaplinsky. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* cursor.c - code to support cursor shape updates (XCursor and
|
||||
* RichCursor preudo-encodings).
|
||||
*/
|
||||
|
||||
#include <rfb/rfbclient.h>
|
||||
|
||||
|
||||
#define OPER_SAVE 0
|
||||
#define OPER_RESTORE 1
|
||||
|
||||
#define MAX_CURSOR_SIZE 1024
|
||||
|
||||
#define RGB24_TO_PIXEL(bpp,r,g,b) \
|
||||
((((uint##bpp##_t)(r) & 0xFF) * client->format.redMax + 127) / 255 \
|
||||
<< client->format.redShift | \
|
||||
(((uint##bpp##_t)(g) & 0xFF) * client->format.greenMax + 127) / 255 \
|
||||
<< client->format.greenShift | \
|
||||
(((uint##bpp##_t)(b) & 0xFF) * client->format.blueMax + 127) / 255 \
|
||||
<< client->format.blueShift)
|
||||
|
||||
|
||||
rfbBool HandleCursorShape(rfbClient* client,int xhot, int yhot, int width, int height, uint32_t enc)
|
||||
{
|
||||
int bytesPerPixel;
|
||||
size_t bytesPerRow, bytesMaskData;
|
||||
rfbXCursorColors rgb;
|
||||
uint32_t colors[2];
|
||||
char *buf;
|
||||
uint8_t *ptr;
|
||||
int x, y, b;
|
||||
|
||||
bytesPerPixel = client->format.bitsPerPixel / 8;
|
||||
bytesPerRow = (width + 7) / 8;
|
||||
bytesMaskData = bytesPerRow * height;
|
||||
|
||||
if (width * height == 0)
|
||||
return TRUE;
|
||||
|
||||
if (width >= MAX_CURSOR_SIZE || height >= MAX_CURSOR_SIZE)
|
||||
return FALSE;
|
||||
|
||||
/* Allocate memory for pixel data and temporary mask data. */
|
||||
if(client->rcSource)
|
||||
free(client->rcSource);
|
||||
|
||||
client->rcSource = malloc((size_t)width * height * bytesPerPixel);
|
||||
if (client->rcSource == NULL)
|
||||
return FALSE;
|
||||
|
||||
buf = malloc(bytesMaskData);
|
||||
if (buf == NULL) {
|
||||
free(client->rcSource);
|
||||
client->rcSource = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Read and decode cursor pixel data, depending on the encoding type. */
|
||||
|
||||
if (enc == rfbEncodingXCursor) {
|
||||
/* Read and convert background and foreground colors. */
|
||||
if (!ReadFromRFBServer(client, (char *)&rgb, sz_rfbXCursorColors)) {
|
||||
free(client->rcSource);
|
||||
client->rcSource = NULL;
|
||||
free(buf);
|
||||
return FALSE;
|
||||
}
|
||||
colors[0] = RGB24_TO_PIXEL(32, rgb.backRed, rgb.backGreen, rgb.backBlue);
|
||||
colors[1] = RGB24_TO_PIXEL(32, rgb.foreRed, rgb.foreGreen, rgb.foreBlue);
|
||||
|
||||
/* Read 1bpp pixel data into a temporary buffer. */
|
||||
if (!ReadFromRFBServer(client, buf, bytesMaskData)) {
|
||||
free(client->rcSource);
|
||||
client->rcSource = NULL;
|
||||
free(buf);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Convert 1bpp data to byte-wide color indices. */
|
||||
ptr = client->rcSource;
|
||||
for (y = 0; y < height; y++) {
|
||||
for (x = 0; x < width / 8; x++) {
|
||||
for (b = 7; b >= 0; b--) {
|
||||
*ptr = buf[y * bytesPerRow + x] >> b & 1;
|
||||
ptr += bytesPerPixel;
|
||||
}
|
||||
}
|
||||
for (b = 7; b > 7 - width % 8; b--) {
|
||||
*ptr = buf[y * bytesPerRow + x] >> b & 1;
|
||||
ptr += bytesPerPixel;
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert indices into the actual pixel values. */
|
||||
switch (bytesPerPixel) {
|
||||
case 1:
|
||||
for (x = 0; x < width * height; x++)
|
||||
client->rcSource[x] = (uint8_t)colors[client->rcSource[x]];
|
||||
break;
|
||||
case 2:
|
||||
for (x = 0; x < width * height; x++)
|
||||
((uint16_t *)client->rcSource)[x] = (uint16_t)colors[client->rcSource[x * 2]];
|
||||
break;
|
||||
case 4:
|
||||
for (x = 0; x < width * height; x++)
|
||||
((uint32_t *)client->rcSource)[x] = colors[client->rcSource[x * 4]];
|
||||
break;
|
||||
}
|
||||
|
||||
} else { /* enc == rfbEncodingRichCursor */
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)client->rcSource, width * height * bytesPerPixel)) {
|
||||
free(client->rcSource);
|
||||
client->rcSource = NULL;
|
||||
free(buf);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Read and decode mask data. */
|
||||
|
||||
if (!ReadFromRFBServer(client, buf, bytesMaskData)) {
|
||||
free(client->rcSource);
|
||||
client->rcSource = NULL;
|
||||
free(buf);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
client->rcMask = malloc((size_t)width * height);
|
||||
if (client->rcMask == NULL) {
|
||||
free(client->rcSource);
|
||||
client->rcSource = NULL;
|
||||
free(buf);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ptr = client->rcMask;
|
||||
for (y = 0; y < height; y++) {
|
||||
for (x = 0; x < width / 8; x++) {
|
||||
for (b = 7; b >= 0; b--) {
|
||||
*ptr++ = buf[y * bytesPerRow + x] >> b & 1;
|
||||
}
|
||||
}
|
||||
for (b = 7; b > 7 - width % 8; b--) {
|
||||
*ptr++ = buf[y * bytesPerRow + x] >> b & 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (client->GotCursorShape != NULL) {
|
||||
client->GotCursorShape(client, xhot, yhot, width, height, bytesPerPixel);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* corre.c - handle CoRRE encoding.
|
||||
*
|
||||
* This file shouldn't be compiled directly. It is included multiple times by
|
||||
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||
* each value of BPP, this file defines a function which handles a CoRRE
|
||||
* encoded rectangle with BPP bits per pixel.
|
||||
*/
|
||||
|
||||
#define HandleCoRREBPP CONCAT2E(HandleCoRRE,BPP)
|
||||
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||
|
||||
static rfbBool
|
||||
HandleCoRREBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||
{
|
||||
rfbRREHeader hdr;
|
||||
int i;
|
||||
CARDBPP pix;
|
||||
uint8_t *ptr;
|
||||
int x, y, w, h;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbRREHeader))
|
||||
return FALSE;
|
||||
|
||||
hdr.nSubrects = rfbClientSwap32IfLE(hdr.nSubrects);
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix)))
|
||||
return FALSE;
|
||||
|
||||
client->GotFillRect(client, rx, ry, rw, rh, pix);
|
||||
|
||||
if (hdr.nSubrects > RFB_BUFFER_SIZE / (4 + (BPP / 8)) || !ReadFromRFBServer(client, client->buffer, hdr.nSubrects * (4 + (BPP / 8))))
|
||||
return FALSE;
|
||||
|
||||
ptr = (uint8_t *)client->buffer;
|
||||
|
||||
for (i = 0; i < hdr.nSubrects; i++) {
|
||||
pix = *(CARDBPP *)ptr;
|
||||
ptr += BPP/8;
|
||||
x = *ptr++;
|
||||
y = *ptr++;
|
||||
w = *ptr++;
|
||||
h = *ptr++;
|
||||
|
||||
client->GotFillRect(client, rx+x, ry+y, w, h, pix);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#undef CARDBPP
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Copyright (C) 2001,2002 Constantin Kaplinsky. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* cursor.c - code to support cursor shape updates (XCursor and
|
||||
* RichCursor preudo-encodings).
|
||||
*/
|
||||
|
||||
#include <rfb/rfbclient.h>
|
||||
|
||||
|
||||
#define OPER_SAVE 0
|
||||
#define OPER_RESTORE 1
|
||||
|
||||
#define MAX_CURSOR_SIZE 1024
|
||||
|
||||
#define RGB24_TO_PIXEL(bpp,r,g,b) \
|
||||
((((uint##bpp##_t)(r) & 0xFF) * client->format.redMax + 127) / 255 \
|
||||
<< client->format.redShift | \
|
||||
(((uint##bpp##_t)(g) & 0xFF) * client->format.greenMax + 127) / 255 \
|
||||
<< client->format.greenShift | \
|
||||
(((uint##bpp##_t)(b) & 0xFF) * client->format.blueMax + 127) / 255 \
|
||||
<< client->format.blueShift)
|
||||
|
||||
|
||||
rfbBool HandleCursorShape(rfbClient* client,int xhot, int yhot, int width, int height, uint32_t enc)
|
||||
{
|
||||
int bytesPerPixel;
|
||||
size_t bytesPerRow, bytesMaskData;
|
||||
rfbXCursorColors rgb;
|
||||
uint32_t colors[2];
|
||||
char *buf;
|
||||
uint8_t *ptr;
|
||||
int x, y, b;
|
||||
|
||||
bytesPerPixel = client->format.bitsPerPixel / 8;
|
||||
bytesPerRow = (width + 7) / 8;
|
||||
bytesMaskData = bytesPerRow * height;
|
||||
|
||||
if (width * height == 0)
|
||||
return TRUE;
|
||||
|
||||
if (width >= MAX_CURSOR_SIZE || height >= MAX_CURSOR_SIZE)
|
||||
return FALSE;
|
||||
|
||||
/* Allocate memory for pixel data and temporary mask data. */
|
||||
if(client->rcSource)
|
||||
free(client->rcSource);
|
||||
|
||||
client->rcSource = malloc((size_t)width * height * bytesPerPixel);
|
||||
if (client->rcSource == NULL)
|
||||
return FALSE;
|
||||
|
||||
buf = malloc(bytesMaskData);
|
||||
if (buf == NULL) {
|
||||
free(client->rcSource);
|
||||
client->rcSource = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Read and decode cursor pixel data, depending on the encoding type. */
|
||||
|
||||
if (enc == rfbEncodingXCursor) {
|
||||
/* Read and convert background and foreground colors. */
|
||||
if (!ReadFromRFBServer(client, (char *)&rgb, sz_rfbXCursorColors)) {
|
||||
free(client->rcSource);
|
||||
client->rcSource = NULL;
|
||||
free(buf);
|
||||
return FALSE;
|
||||
}
|
||||
colors[0] = RGB24_TO_PIXEL(32, rgb.backRed, rgb.backGreen, rgb.backBlue);
|
||||
colors[1] = RGB24_TO_PIXEL(32, rgb.foreRed, rgb.foreGreen, rgb.foreBlue);
|
||||
|
||||
/* Read 1bpp pixel data into a temporary buffer. */
|
||||
if (!ReadFromRFBServer(client, buf, bytesMaskData)) {
|
||||
free(client->rcSource);
|
||||
client->rcSource = NULL;
|
||||
free(buf);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Convert 1bpp data to byte-wide color indices. */
|
||||
ptr = client->rcSource;
|
||||
for (y = 0; y < height; y++) {
|
||||
for (x = 0; x < width / 8; x++) {
|
||||
for (b = 7; b >= 0; b--) {
|
||||
*ptr = buf[y * bytesPerRow + x] >> b & 1;
|
||||
ptr += bytesPerPixel;
|
||||
}
|
||||
}
|
||||
for (b = 7; b > 7 - width % 8; b--) {
|
||||
*ptr = buf[y * bytesPerRow + x] >> b & 1;
|
||||
ptr += bytesPerPixel;
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert indices into the actual pixel values. */
|
||||
switch (bytesPerPixel) {
|
||||
case 1:
|
||||
for (x = 0; x < width * height; x++)
|
||||
client->rcSource[x] = (uint8_t)colors[client->rcSource[x]];
|
||||
break;
|
||||
case 2:
|
||||
for (x = 0; x < width * height; x++)
|
||||
((uint16_t *)client->rcSource)[x] = (uint16_t)colors[client->rcSource[x * 2]];
|
||||
break;
|
||||
case 4:
|
||||
for (x = 0; x < width * height; x++)
|
||||
((uint32_t *)client->rcSource)[x] = colors[client->rcSource[x * 4]];
|
||||
break;
|
||||
}
|
||||
|
||||
} else { /* enc == rfbEncodingRichCursor */
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)client->rcSource, width * height * bytesPerPixel)) {
|
||||
free(client->rcSource);
|
||||
client->rcSource = NULL;
|
||||
free(buf);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Read and decode mask data. */
|
||||
|
||||
if (!ReadFromRFBServer(client, buf, bytesMaskData)) {
|
||||
free(client->rcSource);
|
||||
client->rcSource = NULL;
|
||||
free(buf);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
client->rcMask = malloc((size_t)width * height);
|
||||
if (client->rcMask == NULL) {
|
||||
free(client->rcSource);
|
||||
client->rcSource = NULL;
|
||||
free(buf);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ptr = client->rcMask;
|
||||
for (y = 0; y < height; y++) {
|
||||
for (x = 0; x < width / 8; x++) {
|
||||
for (b = 7; b >= 0; b--) {
|
||||
*ptr++ = buf[y * bytesPerRow + x] >> b & 1;
|
||||
}
|
||||
}
|
||||
for (b = 7; b > 7 - width % 8; b--) {
|
||||
*ptr++ = buf[y * bytesPerRow + x] >> b & 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (client->GotCursorShape != NULL) {
|
||||
client->GotCursorShape(client, xhot, yhot, width, height, bytesPerPixel);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* hextile.c - handle hextile encoding.
|
||||
*
|
||||
* This file shouldn't be compiled directly. It is included multiple times by
|
||||
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||
* each value of BPP, this file defines a function which handles a hextile
|
||||
* encoded rectangle with BPP bits per pixel.
|
||||
*/
|
||||
|
||||
#define HandleHextileBPP CONCAT2E(HandleHextile,BPP)
|
||||
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||
|
||||
static rfbBool
|
||||
HandleHextileBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||
{
|
||||
CARDBPP bg = 0, fg;
|
||||
int i;
|
||||
uint8_t *ptr;
|
||||
int x, y, w, h;
|
||||
int sx, sy, sw, sh;
|
||||
uint8_t subencoding;
|
||||
uint8_t nSubrects;
|
||||
|
||||
for (y = ry; y < ry+rh; y += 16) {
|
||||
for (x = rx; x < rx+rw; x += 16) {
|
||||
w = h = 16;
|
||||
if (rx+rw - x < 16)
|
||||
w = rx+rw - x;
|
||||
if (ry+rh - y < 16)
|
||||
h = ry+rh - y;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&subencoding, 1))
|
||||
return FALSE;
|
||||
|
||||
if (subencoding & rfbHextileRaw) {
|
||||
if (!ReadFromRFBServer(client, client->buffer, w * h * (BPP / 8)))
|
||||
return FALSE;
|
||||
|
||||
client->GotBitmap(client, (uint8_t *)client->buffer, x, y, w, h);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (subencoding & rfbHextileBackgroundSpecified)
|
||||
if (!ReadFromRFBServer(client, (char *)&bg, sizeof(bg)))
|
||||
return FALSE;
|
||||
|
||||
client->GotFillRect(client, x, y, w, h, bg);
|
||||
|
||||
if (subencoding & rfbHextileForegroundSpecified)
|
||||
if (!ReadFromRFBServer(client, (char *)&fg, sizeof(fg)))
|
||||
return FALSE;
|
||||
|
||||
if (!(subencoding & rfbHextileAnySubrects)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&nSubrects, 1))
|
||||
return FALSE;
|
||||
|
||||
ptr = (uint8_t*)client->buffer;
|
||||
|
||||
if (subencoding & rfbHextileSubrectsColoured) {
|
||||
if (!ReadFromRFBServer(client, client->buffer, nSubrects * (2 + (BPP / 8))))
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < nSubrects; i++) {
|
||||
#if BPP==8
|
||||
GET_PIXEL8(fg, ptr);
|
||||
#elif BPP==16
|
||||
GET_PIXEL16(fg, ptr);
|
||||
#elif BPP==32
|
||||
GET_PIXEL32(fg, ptr);
|
||||
#else
|
||||
#error "Invalid BPP"
|
||||
#endif
|
||||
sx = rfbHextileExtractX(*ptr);
|
||||
sy = rfbHextileExtractY(*ptr);
|
||||
ptr++;
|
||||
sw = rfbHextileExtractW(*ptr);
|
||||
sh = rfbHextileExtractH(*ptr);
|
||||
ptr++;
|
||||
|
||||
client->GotFillRect(client, x+sx, y+sy, sw, sh, fg);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!ReadFromRFBServer(client, client->buffer, nSubrects * 2))
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < nSubrects; i++) {
|
||||
sx = rfbHextileExtractX(*ptr);
|
||||
sy = rfbHextileExtractY(*ptr);
|
||||
ptr++;
|
||||
sw = rfbHextileExtractW(*ptr);
|
||||
sh = rfbHextileExtractH(*ptr);
|
||||
ptr++;
|
||||
|
||||
client->GotFillRect(client, x+sx, y+sy, sw, sh, fg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#undef CARDBPP
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* rre.c - handle RRE encoding.
|
||||
*
|
||||
* This file shouldn't be compiled directly. It is included multiple times by
|
||||
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||
* each value of BPP, this file defines a function which handles an RRE
|
||||
* encoded rectangle with BPP bits per pixel.
|
||||
*/
|
||||
|
||||
#define HandleRREBPP CONCAT2E(HandleRRE,BPP)
|
||||
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||
|
||||
static rfbBool
|
||||
HandleRREBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||
{
|
||||
rfbRREHeader hdr;
|
||||
int i;
|
||||
CARDBPP pix;
|
||||
rfbRectangle subrect;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbRREHeader))
|
||||
return FALSE;
|
||||
|
||||
hdr.nSubrects = rfbClientSwap32IfLE(hdr.nSubrects);
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix)))
|
||||
return FALSE;
|
||||
|
||||
client->GotFillRect(client, rx, ry, rw, rh, pix);
|
||||
|
||||
for (i = 0; i < hdr.nSubrects; i++) {
|
||||
if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix)))
|
||||
return FALSE;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&subrect, sz_rfbRectangle))
|
||||
return FALSE;
|
||||
|
||||
subrect.x = rfbClientSwap16IfLE(subrect.x);
|
||||
subrect.y = rfbClientSwap16IfLE(subrect.y);
|
||||
subrect.w = rfbClientSwap16IfLE(subrect.w);
|
||||
subrect.h = rfbClientSwap16IfLE(subrect.h);
|
||||
|
||||
client->GotFillRect(client, rx+subrect.x, ry+subrect.y, subrect.w, subrect.h, pix);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#undef CARDBPP
|
|
@ -0,0 +1,670 @@
|
|||
/*
|
||||
* Copyright (C) 2017, 2019 D. R. Commander. All Rights Reserved.
|
||||
* Copyright (C) 2004-2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* Copyright (C) 2004 Landmark Graphics Corporation. All Rights Reserved.
|
||||
* Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
||||
|
||||
#include "turbojpeg.h"
|
||||
|
||||
/*
|
||||
* tight.c - handle ``tight'' encoding.
|
||||
*
|
||||
* This file shouldn't be compiled directly. It is included multiple
|
||||
* times by rfbproto.c, each time with a different definition of the
|
||||
* macro BPP. For each value of BPP, this file defines a function
|
||||
* which handles a tight-encoded rectangle with BPP bits per pixel.
|
||||
*
|
||||
*/
|
||||
|
||||
#define TIGHT_MIN_TO_COMPRESS 12
|
||||
|
||||
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||
#define filterPtrBPP CONCAT2E(filterPtr,BPP)
|
||||
|
||||
#define HandleTightBPP CONCAT2E(HandleTight,BPP)
|
||||
#define InitFilterCopyBPP CONCAT2E(InitFilterCopy,BPP)
|
||||
#define InitFilterPaletteBPP CONCAT2E(InitFilterPalette,BPP)
|
||||
#define InitFilterGradientBPP CONCAT2E(InitFilterGradient,BPP)
|
||||
#define FilterCopyBPP CONCAT2E(FilterCopy,BPP)
|
||||
#define FilterPaletteBPP CONCAT2E(FilterPalette,BPP)
|
||||
#define FilterGradientBPP CONCAT2E(FilterGradient,BPP)
|
||||
|
||||
#if BPP != 8
|
||||
#define DecompressJpegRectBPP CONCAT2E(DecompressJpegRect,BPP)
|
||||
#endif
|
||||
|
||||
#ifndef RGB_TO_PIXEL
|
||||
|
||||
#define RGB_TO_PIXEL(bpp,r,g,b) \
|
||||
(((CARD##bpp)(r) & client->format.redMax) << client->format.redShift | \
|
||||
((CARD##bpp)(g) & client->format.greenMax) << client->format.greenShift | \
|
||||
((CARD##bpp)(b) & client->format.blueMax) << client->format.blueShift)
|
||||
|
||||
#define RGB24_TO_PIXEL(bpp,r,g,b) \
|
||||
((((CARD##bpp)(r) & 0xFF) * client->format.redMax + 127) / 255 \
|
||||
<< client->format.redShift | \
|
||||
(((CARD##bpp)(g) & 0xFF) * client->format.greenMax + 127) / 255 \
|
||||
<< client->format.greenShift | \
|
||||
(((CARD##bpp)(b) & 0xFF) * client->format.blueMax + 127) / 255 \
|
||||
<< client->format.blueShift)
|
||||
|
||||
#define RGB24_TO_PIXEL32(r,g,b) \
|
||||
(((uint32_t)(r) & 0xFF) << client->format.redShift | \
|
||||
((uint32_t)(g) & 0xFF) << client->format.greenShift | \
|
||||
((uint32_t)(b) & 0xFF) << client->format.blueShift)
|
||||
|
||||
#endif
|
||||
|
||||
/* Type declarations */
|
||||
|
||||
typedef void (*filterPtrBPP)(rfbClient* client, int, int, int);
|
||||
|
||||
/* Prototypes */
|
||||
|
||||
static int InitFilterCopyBPP (rfbClient* client, int rw, int rh);
|
||||
static int InitFilterPaletteBPP (rfbClient* client, int rw, int rh);
|
||||
static int InitFilterGradientBPP (rfbClient* client, int rw, int rh);
|
||||
static void FilterCopyBPP (rfbClient* client, int srcx, int srcy, int numRows);
|
||||
static void FilterPaletteBPP (rfbClient* client, int srcx, int srcy, int numRows);
|
||||
static void FilterGradientBPP (rfbClient* client, int srcx, int srcy, int numRows);
|
||||
|
||||
#if BPP != 8
|
||||
static rfbBool DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h);
|
||||
#endif
|
||||
|
||||
/* Definitions */
|
||||
|
||||
static rfbBool
|
||||
HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||
{
|
||||
CARDBPP fill_colour;
|
||||
uint8_t comp_ctl;
|
||||
uint8_t filter_id;
|
||||
filterPtrBPP filterFn;
|
||||
z_streamp zs;
|
||||
int err, stream_id, compressedLen, bitsPixel;
|
||||
int bufferSize, rowSize, numRows, portionLen, rowsProcessed, extraBytes;
|
||||
rfbBool readUncompressed = FALSE;
|
||||
|
||||
if (client->frameBuffer == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (rx + rw > client->width || ry + rh > client->height) {
|
||||
rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", rx, ry, rw, rh);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&comp_ctl, 1))
|
||||
return FALSE;
|
||||
|
||||
/* Flush zlib streams if we are told by the server to do so. */
|
||||
for (stream_id = 0; stream_id < 4; stream_id++) {
|
||||
if ((comp_ctl & 1) && client->zlibStreamActive[stream_id]) {
|
||||
if (inflateEnd (&client->zlibStream[stream_id]) != Z_OK &&
|
||||
client->zlibStream[stream_id].msg != NULL)
|
||||
rfbClientLog("inflateEnd: %s\n", client->zlibStream[stream_id].msg);
|
||||
client->zlibStreamActive[stream_id] = FALSE;
|
||||
}
|
||||
comp_ctl >>= 1;
|
||||
}
|
||||
|
||||
if ((comp_ctl & rfbTightNoZlib) == rfbTightNoZlib) {
|
||||
comp_ctl &= ~(rfbTightNoZlib);
|
||||
readUncompressed = TRUE;
|
||||
}
|
||||
|
||||
/* Handle solid rectangles. */
|
||||
if (comp_ctl == rfbTightFill) {
|
||||
#if BPP == 32
|
||||
if (client->format.depth == 24 && client->format.redMax == 0xFF &&
|
||||
client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) {
|
||||
if (!ReadFromRFBServer(client, client->buffer, 3))
|
||||
return FALSE;
|
||||
fill_colour = RGB24_TO_PIXEL32(client->buffer[0], client->buffer[1], client->buffer[2]);
|
||||
} else {
|
||||
if (!ReadFromRFBServer(client, (char*)&fill_colour, sizeof(fill_colour)))
|
||||
return FALSE;
|
||||
}
|
||||
#else
|
||||
if (!ReadFromRFBServer(client, (char*)&fill_colour, sizeof(fill_colour)))
|
||||
return FALSE;
|
||||
#endif
|
||||
|
||||
client->GotFillRect(client, rx, ry, rw, rh, fill_colour);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#if BPP == 8
|
||||
if (comp_ctl == rfbTightJpeg) {
|
||||
rfbClientLog("Tight encoding: JPEG is not supported in 8 bpp mode.\n");
|
||||
return FALSE;
|
||||
}
|
||||
#else
|
||||
if (comp_ctl == rfbTightJpeg) {
|
||||
return DecompressJpegRectBPP(client, rx, ry, rw, rh);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Quit on unsupported subencoding value. */
|
||||
if (comp_ctl > rfbTightMaxSubencoding) {
|
||||
rfbClientLog("Tight encoding: bad subencoding value received.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Here primary compression mode handling begins.
|
||||
* Data was processed with optional filter + zlib compression.
|
||||
*/
|
||||
|
||||
/* First, we should identify a filter to use. */
|
||||
if ((comp_ctl & rfbTightExplicitFilter) != 0) {
|
||||
if (!ReadFromRFBServer(client, (char*)&filter_id, 1))
|
||||
return FALSE;
|
||||
|
||||
switch (filter_id) {
|
||||
case rfbTightFilterCopy:
|
||||
filterFn = FilterCopyBPP;
|
||||
bitsPixel = InitFilterCopyBPP(client, rw, rh);
|
||||
break;
|
||||
case rfbTightFilterPalette:
|
||||
filterFn = FilterPaletteBPP;
|
||||
bitsPixel = InitFilterPaletteBPP(client, rw, rh);
|
||||
break;
|
||||
case rfbTightFilterGradient:
|
||||
filterFn = FilterGradientBPP;
|
||||
bitsPixel = InitFilterGradientBPP(client, rw, rh);
|
||||
break;
|
||||
default:
|
||||
rfbClientLog("Tight encoding: unknown filter code received.\n");
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
filterFn = FilterCopyBPP;
|
||||
bitsPixel = InitFilterCopyBPP(client, rw, rh);
|
||||
}
|
||||
if (bitsPixel == 0) {
|
||||
rfbClientLog("Tight encoding: error receiving palette.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Determine if the data should be decompressed or just copied. */
|
||||
rowSize = (rw * bitsPixel + 7) / 8;
|
||||
if (rh * rowSize < TIGHT_MIN_TO_COMPRESS) {
|
||||
if (!ReadFromRFBServer(client, (char*)client->buffer, rh * rowSize))
|
||||
return FALSE;
|
||||
|
||||
filterFn(client, rx, ry, rh);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Read the length (1..3 bytes) of compressed data following. */
|
||||
compressedLen = (int)ReadCompactLen(client);
|
||||
if (compressedLen <= 0) {
|
||||
rfbClientLog("Incorrect data received from the server.\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (readUncompressed) {
|
||||
if (compressedLen > RFB_BUFFER_SIZE) {
|
||||
rfbClientErr("Received uncompressed byte count exceeds our buffer size.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ReadFromRFBServer(client, (char*)client->buffer, compressedLen))
|
||||
return FALSE;
|
||||
|
||||
filterFn(client, rx, ry, rh);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Now let's initialize compression stream if needed. */
|
||||
stream_id = comp_ctl & 0x03;
|
||||
zs = &client->zlibStream[stream_id];
|
||||
if (!client->zlibStreamActive[stream_id]) {
|
||||
zs->zalloc = Z_NULL;
|
||||
zs->zfree = Z_NULL;
|
||||
zs->opaque = Z_NULL;
|
||||
err = inflateInit(zs);
|
||||
if (err != Z_OK) {
|
||||
if (zs->msg != NULL)
|
||||
rfbClientLog("InflateInit error: %s.\n", zs->msg);
|
||||
return FALSE;
|
||||
}
|
||||
client->zlibStreamActive[stream_id] = TRUE;
|
||||
}
|
||||
|
||||
/* Read, decode and draw actual pixel data in a loop. */
|
||||
|
||||
bufferSize = RFB_BUFFER_SIZE * bitsPixel / (bitsPixel + BPP) & 0xFFFFFFFC;
|
||||
if (rowSize > bufferSize) {
|
||||
/* Should be impossible when RFB_BUFFER_SIZE >= 16384 */
|
||||
rfbClientLog("Internal error: incorrect buffer size.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rowsProcessed = 0;
|
||||
extraBytes = 0;
|
||||
|
||||
while (compressedLen > 0) {
|
||||
if (compressedLen > ZLIB_BUFFER_SIZE)
|
||||
portionLen = ZLIB_BUFFER_SIZE;
|
||||
else
|
||||
portionLen = compressedLen;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char*)client->zlib_buffer, portionLen))
|
||||
return FALSE;
|
||||
|
||||
compressedLen -= portionLen;
|
||||
|
||||
zs->next_in = (Bytef *)client->zlib_buffer;
|
||||
zs->avail_in = portionLen;
|
||||
|
||||
do {
|
||||
zs->next_out = (Bytef *)&client->buffer[extraBytes];
|
||||
zs->avail_out = bufferSize - extraBytes;
|
||||
|
||||
err = inflate(zs, Z_SYNC_FLUSH);
|
||||
if (err == Z_BUF_ERROR) /* Input exhausted -- no problem. */
|
||||
break;
|
||||
if (err != Z_OK && err != Z_STREAM_END) {
|
||||
if (zs->msg != NULL) {
|
||||
rfbClientLog("Inflate error: %s.\n", zs->msg);
|
||||
} else {
|
||||
rfbClientLog("Inflate error: %d.\n", err);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
numRows = (bufferSize - zs->avail_out) / rowSize;
|
||||
|
||||
filterFn(client, rx, ry+rowsProcessed, numRows);
|
||||
|
||||
extraBytes = bufferSize - zs->avail_out - numRows * rowSize;
|
||||
if (extraBytes > 0)
|
||||
memcpy(client->buffer, &client->buffer[numRows * rowSize], extraBytes);
|
||||
|
||||
rowsProcessed += numRows;
|
||||
}
|
||||
while (zs->avail_out == 0);
|
||||
}
|
||||
|
||||
if (rowsProcessed != rh) {
|
||||
rfbClientLog("Incorrect number of scan lines after decompression.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
*
|
||||
* Filter stuff.
|
||||
*
|
||||
*/
|
||||
|
||||
static int
|
||||
InitFilterCopyBPP (rfbClient* client, int rw, int rh)
|
||||
{
|
||||
client->rectWidth = rw;
|
||||
|
||||
#if BPP == 32
|
||||
if (client->format.depth == 24 && client->format.redMax == 0xFF &&
|
||||
client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) {
|
||||
client->cutZeros = TRUE;
|
||||
return 24;
|
||||
} else {
|
||||
client->cutZeros = FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return BPP;
|
||||
}
|
||||
|
||||
static void
|
||||
FilterCopyBPP (rfbClient* client, int srcx, int srcy, int numRows)
|
||||
{
|
||||
CARDBPP *dst =
|
||||
(CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8];
|
||||
int y;
|
||||
|
||||
#if BPP == 32
|
||||
int x;
|
||||
|
||||
if (client->cutZeros) {
|
||||
for (y = 0; y < numRows; y++) {
|
||||
for (x = 0; x < client->rectWidth; x++) {
|
||||
dst[y*client->width+x] =
|
||||
RGB24_TO_PIXEL32(client->buffer[(y*client->rectWidth+x)*3],
|
||||
client->buffer[(y*client->rectWidth+x)*3+1],
|
||||
client->buffer[(y*client->rectWidth+x)*3+2]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (y = 0; y < numRows; y++)
|
||||
memcpy (&dst[y*client->width],
|
||||
&client->buffer[y * client->rectWidth * (BPP / 8)],
|
||||
client->rectWidth * (BPP / 8));
|
||||
}
|
||||
|
||||
static int
|
||||
InitFilterGradientBPP (rfbClient* client, int rw, int rh)
|
||||
{
|
||||
int bits;
|
||||
|
||||
bits = InitFilterCopyBPP(client, rw, rh);
|
||||
if (client->cutZeros)
|
||||
memset(client->tightPrevRow, 0, rw * 3);
|
||||
else
|
||||
memset(client->tightPrevRow, 0, rw * 3 * sizeof(uint16_t));
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
#if BPP == 32
|
||||
|
||||
static void
|
||||
FilterGradient24 (rfbClient* client, int srcx, int srcy, int numRows)
|
||||
{
|
||||
CARDBPP *dst =
|
||||
(CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8];
|
||||
int x, y, c;
|
||||
uint8_t thisRow[2048*3];
|
||||
uint8_t pix[3];
|
||||
int est[3];
|
||||
|
||||
for (y = 0; y < numRows; y++) {
|
||||
|
||||
/* First pixel in a row */
|
||||
for (c = 0; c < 3; c++) {
|
||||
pix[c] = client->tightPrevRow[c] + client->buffer[y*client->rectWidth*3+c];
|
||||
thisRow[c] = pix[c];
|
||||
}
|
||||
dst[y*client->width] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
|
||||
|
||||
/* Remaining pixels of a row */
|
||||
for (x = 1; x < client->rectWidth; x++) {
|
||||
for (c = 0; c < 3; c++) {
|
||||
est[c] = (int)client->tightPrevRow[x*3+c] + (int)pix[c] -
|
||||
(int)client->tightPrevRow[(x-1)*3+c];
|
||||
if (est[c] > 0xFF) {
|
||||
est[c] = 0xFF;
|
||||
} else if (est[c] < 0x00) {
|
||||
est[c] = 0x00;
|
||||
}
|
||||
pix[c] = (uint8_t)est[c] + client->buffer[(y*client->rectWidth+x)*3+c];
|
||||
thisRow[x*3+c] = pix[c];
|
||||
}
|
||||
dst[y*client->width+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
|
||||
}
|
||||
|
||||
memcpy(client->tightPrevRow, thisRow, client->rectWidth * 3);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
FilterGradientBPP (rfbClient* client, int srcx, int srcy, int numRows)
|
||||
{
|
||||
CARDBPP *dst =
|
||||
(CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8];
|
||||
int x, y, c;
|
||||
CARDBPP *src = (CARDBPP *)client->buffer;
|
||||
uint16_t *thatRow = (uint16_t *)client->tightPrevRow;
|
||||
uint16_t thisRow[2048*3];
|
||||
uint16_t pix[3];
|
||||
uint16_t max[3];
|
||||
int shift[3];
|
||||
int est[3];
|
||||
|
||||
#if BPP == 32
|
||||
if (client->cutZeros) {
|
||||
FilterGradient24(client, srcx, srcy, numRows);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
max[0] = client->format.redMax;
|
||||
max[1] = client->format.greenMax;
|
||||
max[2] = client->format.blueMax;
|
||||
|
||||
shift[0] = client->format.redShift;
|
||||
shift[1] = client->format.greenShift;
|
||||
shift[2] = client->format.blueShift;
|
||||
|
||||
for (y = 0; y < numRows; y++) {
|
||||
|
||||
/* First pixel in a row */
|
||||
for (c = 0; c < 3; c++) {
|
||||
pix[c] = (uint16_t)(((src[y*client->rectWidth] >> shift[c]) + thatRow[c]) & max[c]);
|
||||
thisRow[c] = pix[c];
|
||||
}
|
||||
dst[y*client->width] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
|
||||
|
||||
/* Remaining pixels of a row */
|
||||
for (x = 1; x < client->rectWidth; x++) {
|
||||
for (c = 0; c < 3; c++) {
|
||||
est[c] = (int)thatRow[x*3+c] + (int)pix[c] - (int)thatRow[(x-1)*3+c];
|
||||
if (est[c] > (int)max[c]) {
|
||||
est[c] = (int)max[c];
|
||||
} else if (est[c] < 0) {
|
||||
est[c] = 0;
|
||||
}
|
||||
pix[c] = (uint16_t)(((src[y*client->rectWidth+x] >> shift[c]) + est[c]) & max[c]);
|
||||
thisRow[x*3+c] = pix[c];
|
||||
}
|
||||
dst[y*client->width+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
|
||||
}
|
||||
memcpy(thatRow, thisRow, client->rectWidth * 3 * sizeof(uint16_t));
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
InitFilterPaletteBPP (rfbClient* client, int rw, int rh)
|
||||
{
|
||||
uint8_t numColors;
|
||||
#if BPP == 32
|
||||
int i;
|
||||
CARDBPP *palette = (CARDBPP *)client->tightPalette;
|
||||
#endif
|
||||
|
||||
client->rectWidth = rw;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char*)&numColors, 1))
|
||||
return 0;
|
||||
|
||||
client->rectColors = (int)numColors;
|
||||
if (++client->rectColors < 2)
|
||||
return 0;
|
||||
|
||||
#if BPP == 32
|
||||
if (client->format.depth == 24 && client->format.redMax == 0xFF &&
|
||||
client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) {
|
||||
if (!ReadFromRFBServer(client, (char*)&client->tightPalette, client->rectColors * 3))
|
||||
return 0;
|
||||
for (i = client->rectColors - 1; i >= 0; i--) {
|
||||
palette[i] = RGB24_TO_PIXEL32(client->tightPalette[i*3],
|
||||
client->tightPalette[i*3+1],
|
||||
client->tightPalette[i*3+2]);
|
||||
}
|
||||
return (client->rectColors == 2) ? 1 : 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ReadFromRFBServer(client, (char*)&client->tightPalette, client->rectColors * (BPP / 8)))
|
||||
return 0;
|
||||
|
||||
return (client->rectColors == 2) ? 1 : 8;
|
||||
}
|
||||
|
||||
static void
|
||||
FilterPaletteBPP (rfbClient* client, int srcx, int srcy, int numRows)
|
||||
{
|
||||
int x, y, b, w;
|
||||
CARDBPP *dst =
|
||||
(CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8];
|
||||
uint8_t *src = (uint8_t *)client->buffer;
|
||||
CARDBPP *palette = (CARDBPP *)client->tightPalette;
|
||||
|
||||
if (client->rectColors == 2) {
|
||||
w = (client->rectWidth + 7) / 8;
|
||||
for (y = 0; y < numRows; y++) {
|
||||
for (x = 0; x < client->rectWidth / 8; x++) {
|
||||
for (b = 7; b >= 0; b--)
|
||||
dst[y*client->width+x*8+7-b] = palette[src[y*w+x] >> b & 1];
|
||||
}
|
||||
for (b = 7; b >= 8 - client->rectWidth % 8; b--) {
|
||||
dst[y*client->width+x*8+7-b] = palette[src[y*w+x] >> b & 1];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < numRows; y++)
|
||||
for (x = 0; x < client->rectWidth; x++)
|
||||
dst[y*client->width+x] = palette[(int)src[y*client->rectWidth+x]];
|
||||
}
|
||||
}
|
||||
|
||||
#if BPP != 8
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
*
|
||||
* JPEG decompression.
|
||||
*
|
||||
*/
|
||||
|
||||
static rfbBool
|
||||
DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h)
|
||||
{
|
||||
int compressedLen;
|
||||
uint8_t *compressedData, *dst;
|
||||
int pixelSize, pitch, flags = 0;
|
||||
|
||||
compressedLen = (int)ReadCompactLen(client);
|
||||
if (compressedLen <= 0) {
|
||||
rfbClientLog("Incorrect data received from the server.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
compressedData = malloc(compressedLen);
|
||||
if (compressedData == NULL) {
|
||||
rfbClientLog("Memory allocation error.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ReadFromRFBServer(client, (char*)compressedData, compressedLen)) {
|
||||
free(compressedData);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(client->GotJpeg != NULL)
|
||||
return client->GotJpeg(client, compressedData, compressedLen, x, y, w, h);
|
||||
|
||||
if (!client->tjhnd) {
|
||||
if ((client->tjhnd = tjInitDecompress()) == NULL) {
|
||||
rfbClientLog("TurboJPEG error: %s\n", tjGetErrorStr());
|
||||
free(compressedData);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
#if BPP == 16
|
||||
flags = 0;
|
||||
pixelSize = 3;
|
||||
pitch = w * pixelSize;
|
||||
dst = (uint8_t *)client->buffer;
|
||||
#else
|
||||
if (client->format.bigEndian) flags |= TJ_ALPHAFIRST;
|
||||
if (client->format.redShift == 16 && client->format.blueShift == 0)
|
||||
flags |= TJ_BGR;
|
||||
if (client->format.bigEndian) flags ^= TJ_BGR;
|
||||
pixelSize = BPP / 8;
|
||||
pitch = client->width * pixelSize;
|
||||
dst = &client->frameBuffer[y * pitch + x * pixelSize];
|
||||
#endif
|
||||
|
||||
if (tjDecompress(client->tjhnd, compressedData, (unsigned long)compressedLen,
|
||||
dst, w, pitch, h, pixelSize, flags)==-1) {
|
||||
rfbClientLog("TurboJPEG error: %s\n", tjGetErrorStr());
|
||||
free(compressedData);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
free(compressedData);
|
||||
|
||||
#if BPP == 16
|
||||
pixelSize = BPP / 8;
|
||||
pitch = client->width * pixelSize;
|
||||
dst = &client->frameBuffer[y * pitch + x * pixelSize];
|
||||
{
|
||||
CARDBPP *dst16=(CARDBPP *)dst, *dst2;
|
||||
char *src = client->buffer;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < h; j++) {
|
||||
for (i = 0, dst2 = dst16; i < w; i++, dst2++, src += 3) {
|
||||
*dst2 = RGB24_TO_PIXEL(BPP, src[0], src[1], src[2]);
|
||||
}
|
||||
dst16 += client->width;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static long
|
||||
ReadCompactLen (rfbClient* client)
|
||||
{
|
||||
long len;
|
||||
uint8_t b;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&b, 1))
|
||||
return -1;
|
||||
len = (int)b & 0x7F;
|
||||
if (b & 0x80) {
|
||||
if (!ReadFromRFBServer(client, (char *)&b, 1))
|
||||
return -1;
|
||||
len |= ((int)b & 0x7F) << 7;
|
||||
if (b & 0x80) {
|
||||
if (!ReadFromRFBServer(client, (char *)&b, 1))
|
||||
return -1;
|
||||
len |= ((int)b & 0xFF) << 14;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#undef CARDBPP
|
||||
|
||||
/* LIBVNCSERVER_HAVE_LIBZ and LIBVNCSERVER_HAVE_LIBJPEG */
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Wiki Wang <wikiwang@live.com>. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* trle.c - handle trle encoding.
|
||||
*
|
||||
* This file shouldn't be compiled directly. It is included multiple times by
|
||||
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||
* each value of BPP, this file defines a function which handles a trle
|
||||
* encoded rectangle with BPP bits per pixel.
|
||||
*/
|
||||
|
||||
#ifndef REALBPP
|
||||
#define REALBPP BPP
|
||||
#endif
|
||||
|
||||
#if !defined(UNCOMP) || UNCOMP == 0
|
||||
#define HandleTRLE CONCAT2E(HandleTRLE, REALBPP)
|
||||
#elif UNCOMP > 0
|
||||
#define HandleTRLE CONCAT3E(HandleTRLE, REALBPP, Down)
|
||||
#else
|
||||
#define HandleTRLE CONCAT3E(HandleTRLE, REALBPP, Up)
|
||||
#endif
|
||||
#define CARDBPP CONCAT3E(uint, BPP, _t)
|
||||
#define CARDREALBPP CONCAT3E(uint, REALBPP, _t)
|
||||
|
||||
#if REALBPP != BPP && defined(UNCOMP) && UNCOMP != 0
|
||||
#if UNCOMP > 0
|
||||
#define UncompressCPixel(pointer) ((*(CARDBPP *)pointer) >> UNCOMP)
|
||||
#else
|
||||
#define UncompressCPixel(pointer) ((*(CARDBPP *)pointer) << (-(UNCOMP)))
|
||||
#endif
|
||||
#else
|
||||
#define UncompressCPixel(pointer) (*(CARDBPP *)pointer)
|
||||
#endif
|
||||
|
||||
static rfbBool HandleTRLE(rfbClient *client, int rx, int ry, int rw, int rh) {
|
||||
int x, y, w, h;
|
||||
uint8_t type, last_type = 0;
|
||||
int min_buffer_size = 16 * 16 * (REALBPP / 8) * 2;
|
||||
uint8_t *buffer;
|
||||
CARDBPP palette[128];
|
||||
int bpp = 0, mask = 0, divider = 0;
|
||||
CARDBPP color = 0;
|
||||
|
||||
/* First make sure we have a large enough raw buffer to hold the
|
||||
* decompressed data. In practice, with a fixed REALBPP, fixed frame
|
||||
* buffer size and the first update containing the entire frame
|
||||
* buffer, this buffer allocation should only happen once, on the
|
||||
* first update.
|
||||
*/
|
||||
if (client->raw_buffer_size < min_buffer_size) {
|
||||
|
||||
if (client->raw_buffer != NULL) {
|
||||
|
||||
free(client->raw_buffer);
|
||||
}
|
||||
|
||||
client->raw_buffer_size = min_buffer_size;
|
||||
client->raw_buffer = (char *)malloc(client->raw_buffer_size);
|
||||
}
|
||||
|
||||
rfbClientLog("Update %d %d %d %d\n", rx, ry, rw, rh);
|
||||
|
||||
for (y = ry; y < ry + rh; y += 16) {
|
||||
for (x = rx; x < rx + rw; x += 16) {
|
||||
w = h = 16;
|
||||
if (rx + rw - x < 16)
|
||||
w = rx + rw - x;
|
||||
if (ry + rh - y < 16)
|
||||
h = ry + rh - y;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)(&type), 1))
|
||||
return FALSE;
|
||||
|
||||
buffer = (uint8_t*)(client->raw_buffer);
|
||||
|
||||
switch (type) {
|
||||
case 0: {
|
||||
if (!ReadFromRFBServer(client, (char *)buffer, w * h * REALBPP / 8))
|
||||
return FALSE;
|
||||
#if REALBPP != BPP
|
||||
int i, j;
|
||||
|
||||
for (j = y * client->width; j < (y + h) * client->width;
|
||||
j += client->width)
|
||||
for (i = x; i < x + w; i++, buffer += REALBPP / 8)
|
||||
((CARDBPP *)client->frameBuffer)[j + i] = UncompressCPixel(buffer);
|
||||
#else
|
||||
client->GotBitmap(client, buffer, x, y, w, h);
|
||||
#endif
|
||||
type = last_type;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if (!ReadFromRFBServer(client, (char *)buffer, REALBPP / 8))
|
||||
return FALSE;
|
||||
|
||||
color = UncompressCPixel(buffer);
|
||||
|
||||
client->GotFillRect(client, x, y, w, h, color);
|
||||
|
||||
last_type = type;
|
||||
break;
|
||||
}
|
||||
case_127:
|
||||
case 127:
|
||||
switch (last_type) {
|
||||
case 0:
|
||||
return FALSE;
|
||||
case 1:
|
||||
client->GotFillRect(client, x, y, w, h, color);
|
||||
type = last_type;
|
||||
break;
|
||||
case 128:
|
||||
return FALSE;
|
||||
default:
|
||||
if (last_type >= 130) {
|
||||
last_type = last_type & 0x7f;
|
||||
|
||||
bpp = (last_type > 4 ? (last_type > 16 ? 8 : 4)
|
||||
: (last_type > 2 ? 2 : 1)),
|
||||
mask = (1 << bpp) - 1, divider = (8 / bpp);
|
||||
}
|
||||
if (last_type <= 16) {
|
||||
int i, j, shift;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char*)buffer,
|
||||
(w + divider - 1) / divider * h))
|
||||
return FALSE;
|
||||
|
||||
/* read palettized pixels */
|
||||
for (j = y * client->width; j < (y + h) * client->width;
|
||||
j += client->width) {
|
||||
for (i = x, shift = 8 - bpp; i < x + w; i++) {
|
||||
((CARDBPP *)client->frameBuffer)[j + i] =
|
||||
palette[((*buffer) >> shift) & mask];
|
||||
shift -= bpp;
|
||||
if (shift < 0) {
|
||||
shift = 8 - bpp;
|
||||
buffer++;
|
||||
}
|
||||
}
|
||||
if (shift < 8 - bpp)
|
||||
buffer++;
|
||||
|
||||
type = last_type;
|
||||
}
|
||||
} else
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case 128: {
|
||||
int i = 0, j = 0;
|
||||
while (j < h) {
|
||||
int color, length, buffer_pos = 0;
|
||||
/* read color */
|
||||
if (!ReadFromRFBServer(client, (char*)buffer, REALBPP / 8 + 1))
|
||||
return FALSE;
|
||||
color = UncompressCPixel(buffer);
|
||||
buffer += REALBPP / 8;
|
||||
buffer_pos += REALBPP / 8;
|
||||
/* read run length */
|
||||
length = 1;
|
||||
while (*buffer == 0xff && buffer_pos < client->raw_buffer_size-1) {
|
||||
if (!ReadFromRFBServer(client, (char*)buffer + 1, 1))
|
||||
return FALSE;
|
||||
length += *buffer;
|
||||
buffer++;
|
||||
buffer_pos++;
|
||||
}
|
||||
length += *buffer;
|
||||
buffer++;
|
||||
while (j < h && length > 0) {
|
||||
((CARDBPP *)client->frameBuffer)[(y + j) * client->width + x + i] =
|
||||
color;
|
||||
length--;
|
||||
i++;
|
||||
if (i >= w) {
|
||||
i = 0;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if (length > 0)
|
||||
rfbClientLog("Warning: possible TRLE corruption\n");
|
||||
}
|
||||
|
||||
type = last_type;
|
||||
|
||||
break;
|
||||
}
|
||||
case_129:
|
||||
case 129: {
|
||||
int i, j;
|
||||
/* read palettized pixels */
|
||||
i = j = 0;
|
||||
while (j < h) {
|
||||
int color, length, buffer_pos = 0;
|
||||
/* read color */
|
||||
if (!ReadFromRFBServer(client, (char *)buffer, 1))
|
||||
return FALSE;
|
||||
color = palette[(*buffer) & 0x7f];
|
||||
length = 1;
|
||||
if (*buffer & 0x80) {
|
||||
if (!ReadFromRFBServer(client, (char *)buffer + 1, 1))
|
||||
return FALSE;
|
||||
buffer++;
|
||||
buffer_pos++;
|
||||
/* read run length */
|
||||
while (*buffer == 0xff && buffer_pos < client->raw_buffer_size-1) {
|
||||
if (!ReadFromRFBServer(client, (char *)buffer + 1, 1))
|
||||
return FALSE;
|
||||
length += *buffer;
|
||||
buffer++;
|
||||
buffer_pos++;
|
||||
}
|
||||
length += *buffer;
|
||||
}
|
||||
buffer++;
|
||||
while (j < h && length > 0) {
|
||||
((CARDBPP *)client->frameBuffer)[(y + j) * client->width + x + i] =
|
||||
color;
|
||||
length--;
|
||||
i++;
|
||||
if (i >= w) {
|
||||
i = 0;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if (length > 0)
|
||||
rfbClientLog("Warning: possible TRLE corruption\n");
|
||||
}
|
||||
|
||||
if (type == 129) {
|
||||
type = last_type;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (type <= 16) {
|
||||
int i;
|
||||
|
||||
bpp = (type > 4 ? 4 : (type > 2 ? 2 : 1)),
|
||||
mask = (1 << bpp) - 1, divider = (8 / bpp);
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)buffer, type * REALBPP / 8))
|
||||
return FALSE;
|
||||
|
||||
/* read palette */
|
||||
for (i = 0; i < type; i++, buffer += REALBPP / 8)
|
||||
palette[i] = UncompressCPixel(buffer);
|
||||
|
||||
last_type = type;
|
||||
goto case_127;
|
||||
} else if (type >= 130) {
|
||||
int i;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)buffer, (type - 128) * REALBPP / 8))
|
||||
return FALSE;
|
||||
|
||||
/* read palette */
|
||||
for (i = 0; i < type - 128; i++, buffer += REALBPP / 8)
|
||||
palette[i] = UncompressCPixel(buffer);
|
||||
|
||||
last_type = type;
|
||||
goto case_129;
|
||||
} else
|
||||
return FALSE;
|
||||
}
|
||||
last_type = type;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#undef CARDBPP
|
||||
#undef CARDREALBPP
|
||||
#undef HandleTRLE
|
||||
#undef UncompressCPixel
|
||||
#undef REALBPP
|
||||
#undef UNCOMP
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
|
||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ultrazip.c - handle ultrazip encoding.
|
||||
*
|
||||
* This file shouldn't be compiled directly. It is included multiple times by
|
||||
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||
* each value of BPP, this file defines a function which handles an zlib
|
||||
* encoded rectangle with BPP bits per pixel.
|
||||
*/
|
||||
|
||||
#define HandleUltraZipBPP CONCAT2E(HandleUltraZip,BPP)
|
||||
#define HandleUltraBPP CONCAT2E(HandleUltra,BPP)
|
||||
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||
|
||||
static rfbBool
|
||||
HandleUltraBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||
{
|
||||
rfbZlibHeader hdr;
|
||||
int toRead=0;
|
||||
int inflateResult=0;
|
||||
lzo_uint uncompressedBytes = (( rw * rh ) * ( BPP / 8 ));
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbZlibHeader))
|
||||
return FALSE;
|
||||
|
||||
toRead = rfbClientSwap32IfLE(hdr.nBytes);
|
||||
if (toRead==0) return TRUE;
|
||||
|
||||
if (toRead < 0) {
|
||||
rfbClientErr("ultra error: remote sent negative payload size\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (uncompressedBytes==0)
|
||||
{
|
||||
rfbClientLog("ultra error: rectangle has 0 uncomressed bytes ((%dw * %dh) * (%d / 8))\n", rw, rh, BPP);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* First make sure we have a large enough raw buffer to hold the
|
||||
* decompressed data. In practice, with a fixed BPP, fixed frame
|
||||
* buffer size and the first update containing the entire frame
|
||||
* buffer, this buffer allocation should only happen once, on the
|
||||
* first update.
|
||||
*/
|
||||
if ( client->raw_buffer_size < (int)uncompressedBytes) {
|
||||
if ( client->raw_buffer != NULL ) {
|
||||
free( client->raw_buffer );
|
||||
}
|
||||
client->raw_buffer_size = uncompressedBytes;
|
||||
/* buffer needs to be aligned on 4-byte boundaries */
|
||||
if ((client->raw_buffer_size % 4)!=0)
|
||||
client->raw_buffer_size += (4-(client->raw_buffer_size % 4));
|
||||
client->raw_buffer = (char*) malloc( client->raw_buffer_size );
|
||||
if(client->raw_buffer == NULL)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* allocate enough space to store the incoming compressed packet */
|
||||
if ( client->ultra_buffer_size < toRead ) {
|
||||
if ( client->ultra_buffer != NULL ) {
|
||||
free( client->ultra_buffer );
|
||||
}
|
||||
client->ultra_buffer_size = toRead;
|
||||
/* buffer needs to be aligned on 4-byte boundaries */
|
||||
if ((client->ultra_buffer_size % 4)!=0)
|
||||
client->ultra_buffer_size += (4-(client->ultra_buffer_size % 4));
|
||||
client->ultra_buffer = (char*) malloc( client->ultra_buffer_size );
|
||||
}
|
||||
|
||||
/* Fill the buffer, obtaining data from the server. */
|
||||
if (!ReadFromRFBServer(client, client->ultra_buffer, toRead))
|
||||
return FALSE;
|
||||
|
||||
/* uncompress the data */
|
||||
uncompressedBytes = client->raw_buffer_size;
|
||||
inflateResult = lzo1x_decompress_safe(
|
||||
(lzo_byte *)client->ultra_buffer, toRead,
|
||||
(lzo_byte *)client->raw_buffer, (lzo_uintp) &uncompressedBytes,
|
||||
NULL);
|
||||
|
||||
/* Note that uncompressedBytes will be 0 on output overrun */
|
||||
if ((rw * rh * (BPP / 8)) != uncompressedBytes)
|
||||
rfbClientLog("Ultra decompressed unexpected amount of data (%d != %d)\n", (rw * rh * (BPP / 8)), uncompressedBytes);
|
||||
|
||||
/* Put the uncompressed contents of the update on the screen. */
|
||||
if ( inflateResult == LZO_E_OK )
|
||||
{
|
||||
client->GotBitmap(client, (unsigned char *)client->raw_buffer, rx, ry, rw, rh);
|
||||
}
|
||||
else
|
||||
{
|
||||
rfbClientLog("ultra decompress returned error: %d\n",
|
||||
inflateResult);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* UltraZip is like rre in that it is composed of subrects */
|
||||
static rfbBool
|
||||
HandleUltraZipBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||
{
|
||||
rfbZlibHeader hdr;
|
||||
int i=0;
|
||||
int toRead=0;
|
||||
int inflateResult=0;
|
||||
unsigned char *ptr=NULL;
|
||||
lzo_uint uncompressedBytes = ry + (rw * 65535);
|
||||
unsigned int numCacheRects = rx;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbZlibHeader))
|
||||
return FALSE;
|
||||
|
||||
toRead = rfbClientSwap32IfLE(hdr.nBytes);
|
||||
|
||||
if (toRead==0) return TRUE;
|
||||
|
||||
if (toRead < 0) {
|
||||
rfbClientErr("ultrazip error: remote sent negative payload size\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (uncompressedBytes==0)
|
||||
{
|
||||
rfbClientLog("ultrazip error: rectangle has 0 uncomressed bytes (%dy + (%dw * 65535)) (%d rectangles)\n", ry, rw, rx);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* First make sure we have a large enough raw buffer to hold the
|
||||
* decompressed data. In practice, with a fixed BPP, fixed frame
|
||||
* buffer size and the first update containing the entire frame
|
||||
* buffer, this buffer allocation should only happen once, on the
|
||||
* first update.
|
||||
*/
|
||||
if ( client->raw_buffer_size < (int)(uncompressedBytes + 500)) {
|
||||
if ( client->raw_buffer != NULL ) {
|
||||
free( client->raw_buffer );
|
||||
}
|
||||
client->raw_buffer_size = uncompressedBytes + 500;
|
||||
/* buffer needs to be aligned on 4-byte boundaries */
|
||||
if ((client->raw_buffer_size % 4)!=0)
|
||||
client->raw_buffer_size += (4-(client->raw_buffer_size % 4));
|
||||
client->raw_buffer = (char*) malloc( client->raw_buffer_size );
|
||||
if(client->raw_buffer == NULL)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/* allocate enough space to store the incoming compressed packet */
|
||||
if ( client->ultra_buffer_size < toRead ) {
|
||||
if ( client->ultra_buffer != NULL ) {
|
||||
free( client->ultra_buffer );
|
||||
}
|
||||
client->ultra_buffer_size = toRead;
|
||||
client->ultra_buffer = (char*) malloc( client->ultra_buffer_size );
|
||||
}
|
||||
|
||||
/* Fill the buffer, obtaining data from the server. */
|
||||
if (!ReadFromRFBServer(client, client->ultra_buffer, toRead))
|
||||
return FALSE;
|
||||
|
||||
/* uncompress the data */
|
||||
uncompressedBytes = client->raw_buffer_size;
|
||||
inflateResult = lzo1x_decompress_safe(
|
||||
(lzo_byte *)client->ultra_buffer, toRead,
|
||||
(lzo_byte *)client->raw_buffer, &uncompressedBytes, NULL);
|
||||
if ( inflateResult != LZO_E_OK )
|
||||
{
|
||||
rfbClientLog("ultra decompress returned error: %d\n",
|
||||
inflateResult);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Put the uncompressed contents of the update on the screen. */
|
||||
ptr = (unsigned char *)client->raw_buffer;
|
||||
for (i=0; i<numCacheRects; i++)
|
||||
{
|
||||
unsigned short sx, sy, sw, sh;
|
||||
unsigned int se;
|
||||
|
||||
memcpy((char *)&sx, ptr, 2); ptr += 2;
|
||||
memcpy((char *)&sy, ptr, 2); ptr += 2;
|
||||
memcpy((char *)&sw, ptr, 2); ptr += 2;
|
||||
memcpy((char *)&sh, ptr, 2); ptr += 2;
|
||||
memcpy((char *)&se, ptr, 4); ptr += 4;
|
||||
|
||||
sx = rfbClientSwap16IfLE(sx);
|
||||
sy = rfbClientSwap16IfLE(sy);
|
||||
sw = rfbClientSwap16IfLE(sw);
|
||||
sh = rfbClientSwap16IfLE(sh);
|
||||
se = rfbClientSwap32IfLE(se);
|
||||
|
||||
if (se == rfbEncodingRaw)
|
||||
{
|
||||
client->GotBitmap(client, (unsigned char *)ptr, sx, sy, sw, sh);
|
||||
ptr += ((sw * sh) * (BPP / 8));
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#undef CARDBPP
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* vncauth.c - Functions for VNC password management and authentication.
|
||||
*/
|
||||
|
||||
#include <rfb/rfbproto.h>
|
||||
#ifdef __STRICT_ANSI__
|
||||
#define _BSD_SOURCE
|
||||
#define _POSIX_SOURCE
|
||||
#define _XOPEN_SOURCE 600
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include "crypto.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
/* libvncclient does not need this */
|
||||
#ifndef rfbEncryptBytes
|
||||
|
||||
/*
|
||||
* We use a fixed key to store passwords, since we assume that our local
|
||||
* file system is secure but nonetheless don't want to store passwords
|
||||
* as plaintext.
|
||||
*/
|
||||
|
||||
static unsigned char fixedkey[8] = {23,82,107,6,35,78,88,7};
|
||||
|
||||
|
||||
/*
|
||||
* Encrypt a password and store it in a file. Returns 0 if successful,
|
||||
* 1 if the file could not be written.
|
||||
*/
|
||||
|
||||
int
|
||||
rfbEncryptAndStorePasswd(char *passwd, char *fname)
|
||||
{
|
||||
FILE *fp;
|
||||
unsigned int i;
|
||||
unsigned char encryptedPasswd[8];
|
||||
int out_len;
|
||||
|
||||
if ((fp = fopen(fname,"w")) == NULL) return 1;
|
||||
|
||||
fchmod(fileno(fp), S_IRUSR|S_IWUSR);
|
||||
|
||||
/* pad password with nulls */
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (i < strlen(passwd)) {
|
||||
encryptedPasswd[i] = passwd[i];
|
||||
} else {
|
||||
encryptedPasswd[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do encryption in-place - this way we overwrite our copy of the plaintext
|
||||
password */
|
||||
encrypt_rfbdes(encryptedPasswd, &out_len, fixedkey, encryptedPasswd, sizeof(encryptedPasswd));
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
putc(encryptedPasswd[i], fp);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Decrypt a password from a file. Returns a pointer to a newly allocated
|
||||
* string containing the password or a null pointer if the password could
|
||||
* not be retrieved for some reason.
|
||||
*/
|
||||
|
||||
char *
|
||||
rfbDecryptPasswdFromFile(char *fname)
|
||||
{
|
||||
FILE *fp;
|
||||
int i, ch;
|
||||
unsigned char *passwd = (unsigned char *)malloc(9);
|
||||
int out_len;
|
||||
|
||||
if (!passwd || (fp = fopen(fname,"r")) == NULL) {
|
||||
free(passwd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
ch = getc(fp);
|
||||
if (ch == EOF) {
|
||||
fclose(fp);
|
||||
free(passwd);
|
||||
return NULL;
|
||||
}
|
||||
passwd[i] = ch;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if(!decrypt_rfbdes(passwd, &out_len, fixedkey, passwd, 8))
|
||||
return NULL;
|
||||
|
||||
passwd[8] = 0;
|
||||
|
||||
return (char *)passwd;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generate CHALLENGESIZE random bytes for use in challenge-response
|
||||
* authentication.
|
||||
*/
|
||||
|
||||
void
|
||||
rfbRandomBytes(unsigned char *bytes)
|
||||
{
|
||||
int i;
|
||||
static rfbBool s_srandom_called = FALSE;
|
||||
|
||||
if (!s_srandom_called) {
|
||||
srandom((unsigned int)time(NULL) ^ (unsigned int)getpid());
|
||||
s_srandom_called = TRUE;
|
||||
}
|
||||
|
||||
for (i = 0; i < CHALLENGESIZE; i++) {
|
||||
bytes[i] = (unsigned char)(random() & 255);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Encrypt CHALLENGESIZE bytes in memory using a password.
|
||||
*/
|
||||
|
||||
void
|
||||
rfbEncryptBytes(unsigned char *bytes, char *passwd)
|
||||
{
|
||||
unsigned char key[8];
|
||||
unsigned int i;
|
||||
int out_len;
|
||||
|
||||
/* key is simply password padded with nulls */
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (i < strlen(passwd)) {
|
||||
key[i] = passwd[i];
|
||||
} else {
|
||||
key[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
encrypt_rfbdes(bytes, &out_len, key, bytes, CHALLENGESIZE);
|
||||
}
|
||||
|
||||
void
|
||||
rfbEncryptBytes2(unsigned char *where, const int length, unsigned char *key) {
|
||||
int i, j, out_len;
|
||||
for (i = 0; i< 8; i++)
|
||||
where[i] ^= key[i];
|
||||
encrypt_rfbdes(where, &out_len, key, where, 8);
|
||||
for (i = 8; i < length; i += 8) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
where[i + j] ^= where[i + j - 8];
|
||||
}
|
||||
encrypt_rfbdes(where + i, &out_len, key, where + i, 8);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
|
||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||
|
||||
/*
|
||||
* zlib.c - handle zlib encoding.
|
||||
*
|
||||
* This file shouldn't be compiled directly. It is included multiple times by
|
||||
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||
* each value of BPP, this file defines a function which handles an zlib
|
||||
* encoded rectangle with BPP bits per pixel.
|
||||
*/
|
||||
|
||||
#define HandleZlibBPP CONCAT2E(HandleZlib,BPP)
|
||||
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||
|
||||
static rfbBool
|
||||
HandleZlibBPP (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||
{
|
||||
rfbZlibHeader hdr;
|
||||
int remaining;
|
||||
int inflateResult;
|
||||
int toRead;
|
||||
|
||||
/* First make sure we have a large enough raw buffer to hold the
|
||||
* decompressed data. In practice, with a fixed BPP, fixed frame
|
||||
* buffer size and the first update containing the entire frame
|
||||
* buffer, this buffer allocation should only happen once, on the
|
||||
* first update.
|
||||
*/
|
||||
if ( client->raw_buffer_size < (( rw * rh ) * ( BPP / 8 ))) {
|
||||
|
||||
if ( client->raw_buffer != NULL ) {
|
||||
|
||||
free( client->raw_buffer );
|
||||
|
||||
}
|
||||
|
||||
client->raw_buffer_size = (( rw * rh ) * ( BPP / 8 ));
|
||||
client->raw_buffer = (char*) malloc( client->raw_buffer_size );
|
||||
|
||||
}
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbZlibHeader))
|
||||
return FALSE;
|
||||
|
||||
remaining = rfbClientSwap32IfLE(hdr.nBytes);
|
||||
|
||||
/* Need to initialize the decompressor state. */
|
||||
client->decompStream.next_in = ( Bytef * )client->buffer;
|
||||
client->decompStream.avail_in = 0;
|
||||
client->decompStream.next_out = ( Bytef * )client->raw_buffer;
|
||||
client->decompStream.avail_out = client->raw_buffer_size;
|
||||
client->decompStream.data_type = Z_BINARY;
|
||||
|
||||
/* Initialize the decompression stream structures on the first invocation. */
|
||||
if ( client->decompStreamInited == FALSE ) {
|
||||
|
||||
inflateResult = inflateInit( &client->decompStream );
|
||||
|
||||
if ( inflateResult != Z_OK ) {
|
||||
rfbClientLog(
|
||||
"inflateInit returned error: %d, msg: %s\n",
|
||||
inflateResult,
|
||||
client->decompStream.msg);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
client->decompStreamInited = TRUE;
|
||||
|
||||
}
|
||||
|
||||
inflateResult = Z_OK;
|
||||
|
||||
/* Process buffer full of data until no more to process, or
|
||||
* some type of inflater error, or Z_STREAM_END.
|
||||
*/
|
||||
while (( remaining > 0 ) &&
|
||||
( inflateResult == Z_OK )) {
|
||||
|
||||
if ( remaining > RFB_BUFFER_SIZE ) {
|
||||
toRead = RFB_BUFFER_SIZE;
|
||||
}
|
||||
else {
|
||||
toRead = remaining;
|
||||
}
|
||||
|
||||
/* Fill the buffer, obtaining data from the server. */
|
||||
if (!ReadFromRFBServer(client, client->buffer,toRead))
|
||||
return FALSE;
|
||||
|
||||
client->decompStream.next_in = ( Bytef * )client->buffer;
|
||||
client->decompStream.avail_in = toRead;
|
||||
|
||||
/* Need to uncompress buffer full. */
|
||||
inflateResult = inflate( &client->decompStream, Z_SYNC_FLUSH );
|
||||
|
||||
/* We never supply a dictionary for compression. */
|
||||
if ( inflateResult == Z_NEED_DICT ) {
|
||||
rfbClientLog("zlib inflate needs a dictionary!\n");
|
||||
return FALSE;
|
||||
}
|
||||
if ( inflateResult < 0 ) {
|
||||
rfbClientLog(
|
||||
"zlib inflate returned error: %d, msg: %s\n",
|
||||
inflateResult,
|
||||
client->decompStream.msg);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Result buffer allocated to be at least large enough. We should
|
||||
* never run out of space!
|
||||
*/
|
||||
if (( client->decompStream.avail_in > 0 ) &&
|
||||
( client->decompStream.avail_out <= 0 )) {
|
||||
rfbClientLog("zlib inflate ran out of space!\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
remaining -= toRead;
|
||||
|
||||
} /* while ( remaining > 0 ) */
|
||||
|
||||
if ( inflateResult == Z_OK ) {
|
||||
|
||||
/* Put the uncompressed contents of the update on the screen. */
|
||||
client->GotBitmap(client, (uint8_t *)client->raw_buffer, rx, ry, rw, rh);
|
||||
}
|
||||
else {
|
||||
|
||||
rfbClientLog(
|
||||
"zlib inflate returned error: %d, msg: %s\n",
|
||||
inflateResult,
|
||||
client->decompStream.msg);
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#undef CARDBPP
|
||||
|
||||
#endif
|
|
@ -0,0 +1,427 @@
|
|||
/*
|
||||
* Copyright (C) 2005 Johannes E. Schindelin. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||
|
||||
/*
|
||||
* zrle.c - handle zrle encoding.
|
||||
*
|
||||
* This file shouldn't be compiled directly. It is included multiple times by
|
||||
* rfbproto.c, each time with a different definition of the macro BPP. For
|
||||
* each value of BPP, this file defines a function which handles an zrle
|
||||
* encoded rectangle with BPP bits per pixel.
|
||||
*/
|
||||
|
||||
#ifndef REALBPP
|
||||
#define REALBPP BPP
|
||||
#endif
|
||||
|
||||
#if !defined(UNCOMP) || UNCOMP==0
|
||||
#define HandleZRLE CONCAT2E(HandleZRLE,REALBPP)
|
||||
#define HandleZRLETile CONCAT2E(HandleZRLETile,REALBPP)
|
||||
#elif UNCOMP>0
|
||||
#define HandleZRLE CONCAT3E(HandleZRLE,REALBPP,Down)
|
||||
#define HandleZRLETile CONCAT3E(HandleZRLETile,REALBPP,Down)
|
||||
#else
|
||||
#define HandleZRLE CONCAT3E(HandleZRLE,REALBPP,Up)
|
||||
#define HandleZRLETile CONCAT3E(HandleZRLETile,REALBPP,Up)
|
||||
#endif
|
||||
#define CARDBPP CONCAT3E(uint,BPP,_t)
|
||||
#define CARDREALBPP CONCAT3E(uint,REALBPP,_t)
|
||||
|
||||
#define ENDIAN_LITTLE 0
|
||||
#define ENDIAN_BIG 1
|
||||
#define ENDIAN_NO 2
|
||||
#define ZYWRLE_ENDIAN ENDIAN_LITTLE
|
||||
#undef END_FIX
|
||||
#if ZYWRLE_ENDIAN == ENDIAN_LITTLE
|
||||
# define END_FIX LE
|
||||
#elif ZYWRLE_ENDIAN == ENDIAN_BIG
|
||||
# define END_FIX BE
|
||||
#else
|
||||
# define END_FIX NE
|
||||
#endif
|
||||
#define __RFB_CONCAT3E(a,b,c) CONCAT3E(a,b,c)
|
||||
#define __RFB_CONCAT2E(a,b) CONCAT2E(a,b)
|
||||
#undef CPIXEL
|
||||
#if REALBPP != BPP
|
||||
#if UNCOMP == 0
|
||||
#define CPIXEL REALBPP
|
||||
#elif UNCOMP>0
|
||||
#define CPIXEL CONCAT2E(REALBPP,Down)
|
||||
#else
|
||||
#define CPIXEL CONCAT2E(REALBPP,Up)
|
||||
#endif
|
||||
#endif
|
||||
#define PIXEL_T __RFB_CONCAT3E(uint,BPP,_t)
|
||||
#if BPP!=8
|
||||
#define ZYWRLE_DECODE 1
|
||||
#include "zywrletemplate.c"
|
||||
#endif
|
||||
#undef CPIXEL
|
||||
|
||||
static int HandleZRLETile(rfbClient* client,
|
||||
uint8_t* buffer,size_t buffer_length,
|
||||
int x,int y,int w,int h);
|
||||
|
||||
static rfbBool
|
||||
HandleZRLE (rfbClient* client, int rx, int ry, int rw, int rh)
|
||||
{
|
||||
rfbZRLEHeader header;
|
||||
int remaining;
|
||||
int inflateResult;
|
||||
int toRead;
|
||||
int min_buffer_size = rw * rh * (REALBPP / 8) * 2;
|
||||
|
||||
/* First make sure we have a large enough raw buffer to hold the
|
||||
* decompressed data. In practice, with a fixed REALBPP, fixed frame
|
||||
* buffer size and the first update containing the entire frame
|
||||
* buffer, this buffer allocation should only happen once, on the
|
||||
* first update.
|
||||
*/
|
||||
if ( client->raw_buffer_size < min_buffer_size) {
|
||||
|
||||
if ( client->raw_buffer != NULL ) {
|
||||
|
||||
free( client->raw_buffer );
|
||||
|
||||
}
|
||||
|
||||
client->raw_buffer_size = min_buffer_size;
|
||||
client->raw_buffer = (char*) malloc( client->raw_buffer_size );
|
||||
|
||||
}
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&header, sz_rfbZRLEHeader))
|
||||
return FALSE;
|
||||
|
||||
remaining = rfbClientSwap32IfLE(header.length);
|
||||
|
||||
/* Need to initialize the decompressor state. */
|
||||
client->decompStream.next_in = ( Bytef * )client->buffer;
|
||||
client->decompStream.avail_in = 0;
|
||||
client->decompStream.next_out = ( Bytef * )client->raw_buffer;
|
||||
client->decompStream.avail_out = client->raw_buffer_size;
|
||||
client->decompStream.data_type = Z_BINARY;
|
||||
|
||||
/* Initialize the decompression stream structures on the first invocation. */
|
||||
if ( client->decompStreamInited == FALSE ) {
|
||||
|
||||
inflateResult = inflateInit( &client->decompStream );
|
||||
|
||||
if ( inflateResult != Z_OK ) {
|
||||
rfbClientLog(
|
||||
"inflateInit returned error: %d, msg: %s\n",
|
||||
inflateResult,
|
||||
client->decompStream.msg);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
client->decompStreamInited = TRUE;
|
||||
|
||||
}
|
||||
|
||||
inflateResult = Z_OK;
|
||||
|
||||
/* Process buffer full of data until no more to process, or
|
||||
* some type of inflater error, or Z_STREAM_END.
|
||||
*/
|
||||
while (( remaining > 0 ) &&
|
||||
( inflateResult == Z_OK )) {
|
||||
|
||||
if ( remaining > RFB_BUFFER_SIZE ) {
|
||||
toRead = RFB_BUFFER_SIZE;
|
||||
}
|
||||
else {
|
||||
toRead = remaining;
|
||||
}
|
||||
|
||||
/* Fill the buffer, obtaining data from the server. */
|
||||
if (!ReadFromRFBServer(client, client->buffer,toRead))
|
||||
return FALSE;
|
||||
|
||||
client->decompStream.next_in = ( Bytef * )client->buffer;
|
||||
client->decompStream.avail_in = toRead;
|
||||
|
||||
/* Need to uncompress buffer full. */
|
||||
inflateResult = inflate( &client->decompStream, Z_SYNC_FLUSH );
|
||||
|
||||
/* We never supply a dictionary for compression. */
|
||||
if ( inflateResult == Z_NEED_DICT ) {
|
||||
rfbClientLog("zlib inflate needs a dictionary!\n");
|
||||
return FALSE;
|
||||
}
|
||||
if ( inflateResult < 0 ) {
|
||||
rfbClientLog(
|
||||
"zlib inflate returned error: %d, msg: %s\n",
|
||||
inflateResult,
|
||||
client->decompStream.msg);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Result buffer allocated to be at least large enough. We should
|
||||
* never run out of space!
|
||||
*/
|
||||
if (( client->decompStream.avail_in > 0 ) &&
|
||||
( client->decompStream.avail_out <= 0 )) {
|
||||
rfbClientLog("zlib inflate ran out of space!\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
remaining -= toRead;
|
||||
|
||||
} /* while ( remaining > 0 ) */
|
||||
|
||||
if ( inflateResult == Z_OK ) {
|
||||
char* buf=client->raw_buffer;
|
||||
int i,j;
|
||||
|
||||
remaining = client->raw_buffer_size-client->decompStream.avail_out;
|
||||
|
||||
for(j=0; j<rh; j+=rfbZRLETileHeight)
|
||||
for(i=0; i<rw; i+=rfbZRLETileWidth) {
|
||||
int subWidth=(i+rfbZRLETileWidth>rw)?rw-i:rfbZRLETileWidth;
|
||||
int subHeight=(j+rfbZRLETileHeight>rh)?rh-j:rfbZRLETileHeight;
|
||||
int result=HandleZRLETile(client,(uint8_t *)buf,remaining,rx+i,ry+j,subWidth,subHeight);
|
||||
|
||||
if(result<0) {
|
||||
rfbClientLog("ZRLE decoding failed (%d)\n",result);
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
buf+=result;
|
||||
remaining-=result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
rfbClientLog(
|
||||
"zlib inflate returned error: %d, msg: %s\n",
|
||||
inflateResult,
|
||||
client->decompStream.msg);
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#if REALBPP!=BPP && defined(UNCOMP) && UNCOMP!=0
|
||||
#if UNCOMP>0
|
||||
#define UncompressCPixel(pointer) ((*(CARDBPP*)pointer)>>UNCOMP)
|
||||
#else
|
||||
#define UncompressCPixel(pointer) ((*(CARDBPP*)pointer)<<(-(UNCOMP)))
|
||||
#endif
|
||||
#else
|
||||
#define UncompressCPixel(pointer) (*(CARDBPP*)pointer)
|
||||
#endif
|
||||
|
||||
static int HandleZRLETile(rfbClient* client,
|
||||
uint8_t* buffer,size_t buffer_length,
|
||||
int x,int y,int w,int h) {
|
||||
uint8_t* buffer_copy = buffer;
|
||||
uint8_t* buffer_end = buffer+buffer_length;
|
||||
uint8_t type;
|
||||
#if BPP!=8
|
||||
uint8_t zywrle_level = (client->appData.qualityLevel & 0x80) ?
|
||||
0 : (3 - client->appData.qualityLevel / 3);
|
||||
#endif
|
||||
|
||||
if(buffer_length<1)
|
||||
return -2;
|
||||
|
||||
type = *buffer;
|
||||
buffer++;
|
||||
{
|
||||
if( type == 0 ) /* raw */
|
||||
#if BPP!=8
|
||||
if( zywrle_level > 0 ){
|
||||
CARDBPP* pFrame = (CARDBPP*)client->frameBuffer + y*client->width+x;
|
||||
int ret;
|
||||
client->appData.qualityLevel |= 0x80;
|
||||
ret = HandleZRLETile(client, buffer, buffer_end-buffer, x, y, w, h);
|
||||
client->appData.qualityLevel &= 0x7F;
|
||||
if( ret < 0 ){
|
||||
return ret;
|
||||
}
|
||||
ZYWRLE_SYNTHESIZE( pFrame, pFrame, w, h, client->width, zywrle_level, (int*)client->zlib_buffer );
|
||||
buffer += ret;
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
#if REALBPP!=BPP
|
||||
int i,j;
|
||||
|
||||
if(1+w*h*REALBPP/8>buffer_length) {
|
||||
rfbClientLog("expected %d bytes, got only %d (%dx%d)\n",1+w*h*REALBPP/8,buffer_length,w,h);
|
||||
return -3;
|
||||
}
|
||||
|
||||
for(j=y*client->width; j<(y+h)*client->width; j+=client->width)
|
||||
for(i=x; i<x+w; i++,buffer+=REALBPP/8)
|
||||
((CARDBPP*)client->frameBuffer)[j+i] = UncompressCPixel(buffer);
|
||||
#else
|
||||
client->GotBitmap(client, buffer, x, y, w, h);
|
||||
buffer+=w*h*REALBPP/8;
|
||||
#endif
|
||||
}
|
||||
else if( type == 1 ) /* solid */
|
||||
{
|
||||
CARDBPP color = UncompressCPixel(buffer);
|
||||
|
||||
if(1+REALBPP/8>buffer_length)
|
||||
return -4;
|
||||
|
||||
client->GotFillRect(client, x, y, w, h, color);
|
||||
|
||||
buffer+=REALBPP/8;
|
||||
|
||||
}
|
||||
else if( type <= 127 ) /* packed Palette */
|
||||
{
|
||||
CARDBPP palette[128];
|
||||
int i,j,shift,
|
||||
bpp=(type>4?(type>16?8:4):(type>2?2:1)),
|
||||
mask=(1<<bpp)-1,
|
||||
divider=(8/bpp);
|
||||
|
||||
if(1+type*REALBPP/8+((w+divider-1)/divider)*h>buffer_length)
|
||||
return -5;
|
||||
|
||||
/* read palette */
|
||||
for(i=0; i<type; i++,buffer+=REALBPP/8)
|
||||
palette[i] = UncompressCPixel(buffer);
|
||||
|
||||
/* read palettized pixels */
|
||||
for(j=y*client->width; j<(y+h)*client->width; j+=client->width) {
|
||||
for(i=x,shift=8-bpp; i<x+w; i++) {
|
||||
((CARDBPP*)client->frameBuffer)[j+i] = palette[((*buffer)>>shift)&mask];
|
||||
shift-=bpp;
|
||||
if(shift<0) {
|
||||
shift=8-bpp;
|
||||
buffer++;
|
||||
}
|
||||
}
|
||||
if(shift<8-bpp)
|
||||
buffer++;
|
||||
}
|
||||
|
||||
}
|
||||
/* case 17 ... 127: not used, but valid */
|
||||
else if( type == 128 ) /* plain RLE */
|
||||
{
|
||||
int i=0,j=0;
|
||||
while(j<h) {
|
||||
int color,length;
|
||||
/* read color */
|
||||
if(buffer+REALBPP/8+1>buffer_end)
|
||||
return -7;
|
||||
color = UncompressCPixel(buffer);
|
||||
buffer+=REALBPP/8;
|
||||
/* read run length */
|
||||
length=1;
|
||||
while(*buffer==0xff) {
|
||||
if(buffer+1>=buffer_end)
|
||||
return -8;
|
||||
length+=*buffer;
|
||||
buffer++;
|
||||
}
|
||||
length+=*buffer;
|
||||
buffer++;
|
||||
while(j<h && length>0) {
|
||||
((CARDBPP*)client->frameBuffer)[(y+j)*client->width+x+i] = color;
|
||||
length--;
|
||||
i++;
|
||||
if(i>=w) {
|
||||
i=0;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if(length>0)
|
||||
rfbClientLog("Warning: possible ZRLE corruption\n");
|
||||
}
|
||||
|
||||
}
|
||||
else if( type == 129 ) /* unused */
|
||||
{
|
||||
return -8;
|
||||
}
|
||||
else if( type >= 130 ) /* palette RLE */
|
||||
{
|
||||
CARDBPP palette[128];
|
||||
int i,j;
|
||||
|
||||
if(2+(type-128)*REALBPP/8>buffer_length)
|
||||
return -9;
|
||||
|
||||
/* read palette */
|
||||
for(i=0; i<type-128; i++,buffer+=REALBPP/8)
|
||||
palette[i] = UncompressCPixel(buffer);
|
||||
/* read palettized pixels */
|
||||
i=j=0;
|
||||
while(j<h) {
|
||||
int color,length;
|
||||
/* read color */
|
||||
if(buffer>=buffer_end)
|
||||
return -10;
|
||||
color = palette[(*buffer)&0x7f];
|
||||
length=1;
|
||||
if(*buffer&0x80) {
|
||||
if(buffer+1>=buffer_end)
|
||||
return -11;
|
||||
buffer++;
|
||||
/* read run length */
|
||||
while(*buffer==0xff) {
|
||||
if(buffer+1>=buffer_end)
|
||||
return -8;
|
||||
length+=*buffer;
|
||||
buffer++;
|
||||
}
|
||||
length+=*buffer;
|
||||
}
|
||||
buffer++;
|
||||
while(j<h && length>0) {
|
||||
((CARDBPP*)client->frameBuffer)[(y+j)*client->width+x+i] = color;
|
||||
length--;
|
||||
i++;
|
||||
if(i>=w) {
|
||||
i=0;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if(length>0)
|
||||
rfbClientLog("Warning: possible ZRLE corruption\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer-buffer_copy;
|
||||
}
|
||||
|
||||
#undef CARDBPP
|
||||
#undef CARDREALBPP
|
||||
#undef HandleZRLE
|
||||
#undef HandleZRLETile
|
||||
#undef UncompressCPixel
|
||||
|
||||
#endif
|
||||
|
||||
#undef UNCOMP
|
||||
#undef REALBPP
|
|
@ -0,0 +1,824 @@
|
|||
|
||||
/********************************************************************
|
||||
* *
|
||||
* THIS FILE IS PART OF THE 'ZYWRLE' VNC CODEC SOURCE CODE. *
|
||||
* *
|
||||
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
|
||||
* GOVERNED BY A FOLLOWING BSD-STYLE SOURCE LICENSE. *
|
||||
* PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
|
||||
* *
|
||||
* THE 'ZYWRLE' VNC CODEC SOURCE CODE IS (C) COPYRIGHT 2006 *
|
||||
* BY Hitachi Systems & Services, Ltd. *
|
||||
* (Noriaki Yamazaki, Research & Development Center) * *
|
||||
* *
|
||||
********************************************************************
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of the Hitachi Systems & Services, Ltd. nor
|
||||
the names of its contributors may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
********************************************************************/
|
||||
|
||||
/* Change Log:
|
||||
V0.02 : 2008/02/04 : Fix mis encode/decode when width != scanline
|
||||
(Thanks Johannes Schindelin, author of LibVNC
|
||||
Server/Client)
|
||||
V0.01 : 2007/02/06 : Initial release
|
||||
*/
|
||||
|
||||
/* #define ZYWRLE_ENCODE */
|
||||
/* #define ZYWRLE_DECODE */
|
||||
#define ZYWRLE_QUANTIZE
|
||||
|
||||
/*
|
||||
[References]
|
||||
PLHarr:
|
||||
Senecal, J. G., P. Lindstrom, M. A. Duchaineau, and K. I. Joy, "An Improved N-Bit to N-Bit Reversible Haar-Like Transform," Pacific Graphics 2004, October 2004, pp. 371-380.
|
||||
EZW:
|
||||
Shapiro, JM: Embedded Image Coding Using Zerotrees of Wavelet Coefficients, IEEE Trans. Signal. Process., Vol.41, pp.3445-3462 (1993).
|
||||
*/
|
||||
|
||||
|
||||
/* Template Macro stuffs. */
|
||||
#undef ZYWRLE_ANALYZE
|
||||
#undef ZYWRLE_SYNTHESIZE
|
||||
#define ZYWRLE_ANALYZE __RFB_CONCAT3E(zywrleAnalyze,BPP,END_FIX)
|
||||
#define ZYWRLE_SYNTHESIZE __RFB_CONCAT3E(zywrleSynthesize,BPP,END_FIX)
|
||||
|
||||
#define ZYWRLE_RGBYUV __RFB_CONCAT3E(zywrleRGBYUV,BPP,END_FIX)
|
||||
#define ZYWRLE_YUVRGB __RFB_CONCAT3E(zywrleYUVRGB,BPP,END_FIX)
|
||||
#define ZYWRLE_YMASK __RFB_CONCAT2E(ZYWRLE_YMASK,BPP)
|
||||
#define ZYWRLE_UVMASK __RFB_CONCAT2E(ZYWRLE_UVMASK,BPP)
|
||||
#define ZYWRLE_LOAD_PIXEL __RFB_CONCAT2E(ZYWRLE_LOAD_PIXEL,BPP)
|
||||
#define ZYWRLE_SAVE_PIXEL __RFB_CONCAT2E(ZYWRLE_SAVE_PIXEL,BPP)
|
||||
|
||||
/* Packing/Unpacking pixel stuffs.
|
||||
Endian conversion stuffs. */
|
||||
#undef S_0
|
||||
#undef S_1
|
||||
#undef L_0
|
||||
#undef L_1
|
||||
#undef L_2
|
||||
#if ZYWRLE_ENDIAN == ENDIAN_BIG
|
||||
# define S_0 1
|
||||
# define S_1 0
|
||||
# define L_0 3
|
||||
# define L_1 2
|
||||
# define L_2 1
|
||||
#else
|
||||
# define S_0 0
|
||||
# define S_1 1
|
||||
# define L_0 0
|
||||
# define L_1 1
|
||||
# define L_2 2
|
||||
#endif
|
||||
|
||||
/* Load/Save pixel stuffs. */
|
||||
#define ZYWRLE_YMASK15 0xFFFFFFF8
|
||||
#define ZYWRLE_UVMASK15 0xFFFFFFF8
|
||||
#define ZYWRLE_LOAD_PIXEL15(pSrc,R,G,B) { \
|
||||
R = (((unsigned char*)pSrc)[S_1]<< 1)& 0xF8; \
|
||||
G = ((((unsigned char*)pSrc)[S_1]<< 6)|(((unsigned char*)pSrc)[S_0]>> 2))& 0xF8; \
|
||||
B = (((unsigned char*)pSrc)[S_0]<< 3)& 0xF8; \
|
||||
}
|
||||
#define ZYWRLE_SAVE_PIXEL15(pDst,R,G,B) { \
|
||||
R &= 0xF8; \
|
||||
G &= 0xF8; \
|
||||
B &= 0xF8; \
|
||||
((unsigned char*)pDst)[S_1] = (unsigned char)( (R>>1)|(G>>6) ); \
|
||||
((unsigned char*)pDst)[S_0] = (unsigned char)(((B>>3)|(G<<2))& 0xFF); \
|
||||
}
|
||||
#define ZYWRLE_YMASK16 0xFFFFFFFC
|
||||
#define ZYWRLE_UVMASK16 0xFFFFFFF8
|
||||
#define ZYWRLE_LOAD_PIXEL16(pSrc,R,G,B) { \
|
||||
R = ((unsigned char*)pSrc)[S_1] & 0xF8; \
|
||||
G = ((((unsigned char*)pSrc)[S_1]<< 5)|(((unsigned char*)pSrc)[S_0]>> 3))& 0xFC; \
|
||||
B = (((unsigned char*)pSrc)[S_0]<< 3)& 0xF8; \
|
||||
}
|
||||
#define ZYWRLE_SAVE_PIXEL16(pDst,R,G,B) { \
|
||||
R &= 0xF8; \
|
||||
G &= 0xFC; \
|
||||
B &= 0xF8; \
|
||||
((unsigned char*)pDst)[S_1] = (unsigned char)( R |(G>>5) ); \
|
||||
((unsigned char*)pDst)[S_0] = (unsigned char)(((B>>3)|(G<<3))& 0xFF); \
|
||||
}
|
||||
#define ZYWRLE_YMASK32 0xFFFFFFFF
|
||||
#define ZYWRLE_UVMASK32 0xFFFFFFFF
|
||||
#define ZYWRLE_LOAD_PIXEL32(pSrc,R,G,B) { \
|
||||
R = ((unsigned char*)pSrc)[L_2]; \
|
||||
G = ((unsigned char*)pSrc)[L_1]; \
|
||||
B = ((unsigned char*)pSrc)[L_0]; \
|
||||
}
|
||||
#define ZYWRLE_SAVE_PIXEL32(pDst,R,G,B) { \
|
||||
((unsigned char*)pDst)[L_2] = (unsigned char)R; \
|
||||
((unsigned char*)pDst)[L_1] = (unsigned char)G; \
|
||||
((unsigned char*)pDst)[L_0] = (unsigned char)B; \
|
||||
}
|
||||
|
||||
#ifndef ZYWRLE_ONCE
|
||||
#define ZYWRLE_ONCE
|
||||
|
||||
#ifndef __STRICT_ANSI__
|
||||
# define InlineX inline
|
||||
#else
|
||||
# define InlineX
|
||||
#endif
|
||||
|
||||
#ifdef ZYWRLE_ENCODE
|
||||
/* Tables for Coefficients filtering. */
|
||||
# ifndef ZYWRLE_QUANTIZE
|
||||
/* Type A:lower bit omitting of EZW style. */
|
||||
const static unsigned int zywrleParam[3][3]={
|
||||
{0x0000F000,0x00000000,0x00000000},
|
||||
{0x0000C000,0x00F0F0F0,0x00000000},
|
||||
{0x0000C000,0x00C0C0C0,0x00F0F0F0},
|
||||
/* {0x0000FF00,0x00000000,0x00000000},
|
||||
{0x0000FF00,0x00FFFFFF,0x00000000},
|
||||
{0x0000FF00,0x00FFFFFF,0x00FFFFFF}, */
|
||||
};
|
||||
# else
|
||||
/* Type B:Non liner quantization filter. */
|
||||
static const signed char zywrleConv[4][256]={
|
||||
{ /* bi=5, bo=5 r=0.0:PSNR=24.849 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
},
|
||||
{ /* bi=5, bo=5 r=2.0:PSNR=74.031 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 32,
|
||||
32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, 32, 32, 32, 32, 32,
|
||||
48, 48, 48, 48, 48, 48, 48, 48,
|
||||
48, 48, 48, 56, 56, 56, 56, 56,
|
||||
56, 56, 56, 56, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 72, 72, 72, 72,
|
||||
72, 72, 72, 72, 80, 80, 80, 80,
|
||||
80, 80, 88, 88, 88, 88, 88, 88,
|
||||
88, 88, 88, 88, 88, 88, 96, 96,
|
||||
96, 96, 96, 104, 104, 104, 104, 104,
|
||||
104, 104, 104, 104, 104, 112, 112, 112,
|
||||
112, 112, 112, 112, 112, 112, 120, 120,
|
||||
120, 120, 120, 120, 120, 120, 120, 120,
|
||||
0, -120, -120, -120, -120, -120, -120, -120,
|
||||
-120, -120, -120, -112, -112, -112, -112, -112,
|
||||
-112, -112, -112, -112, -104, -104, -104, -104,
|
||||
-104, -104, -104, -104, -104, -104, -96, -96,
|
||||
-96, -96, -96, -88, -88, -88, -88, -88,
|
||||
-88, -88, -88, -88, -88, -88, -88, -80,
|
||||
-80, -80, -80, -80, -80, -72, -72, -72,
|
||||
-72, -72, -72, -72, -72, -64, -64, -64,
|
||||
-64, -64, -64, -64, -64, -56, -56, -56,
|
||||
-56, -56, -56, -56, -56, -56, -48, -48,
|
||||
-48, -48, -48, -48, -48, -48, -48, -48,
|
||||
-48, -32, -32, -32, -32, -32, -32, -32,
|
||||
-32, -32, -32, -32, -32, -32, -32, -32,
|
||||
-32, -32, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
},
|
||||
{ /* bi=5, bo=4 r=2.0:PSNR=64.441 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
48, 48, 48, 48, 48, 48, 48, 48,
|
||||
48, 48, 48, 48, 48, 48, 48, 48,
|
||||
48, 48, 48, 48, 48, 48, 48, 48,
|
||||
64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64,
|
||||
80, 80, 80, 80, 80, 80, 80, 80,
|
||||
80, 80, 80, 80, 80, 88, 88, 88,
|
||||
88, 88, 88, 88, 88, 88, 88, 88,
|
||||
104, 104, 104, 104, 104, 104, 104, 104,
|
||||
104, 104, 104, 112, 112, 112, 112, 112,
|
||||
112, 112, 112, 112, 120, 120, 120, 120,
|
||||
120, 120, 120, 120, 120, 120, 120, 120,
|
||||
0, -120, -120, -120, -120, -120, -120, -120,
|
||||
-120, -120, -120, -120, -120, -112, -112, -112,
|
||||
-112, -112, -112, -112, -112, -112, -104, -104,
|
||||
-104, -104, -104, -104, -104, -104, -104, -104,
|
||||
-104, -88, -88, -88, -88, -88, -88, -88,
|
||||
-88, -88, -88, -88, -80, -80, -80, -80,
|
||||
-80, -80, -80, -80, -80, -80, -80, -80,
|
||||
-80, -64, -64, -64, -64, -64, -64, -64,
|
||||
-64, -64, -64, -64, -64, -64, -64, -64,
|
||||
-64, -48, -48, -48, -48, -48, -48, -48,
|
||||
-48, -48, -48, -48, -48, -48, -48, -48,
|
||||
-48, -48, -48, -48, -48, -48, -48, -48,
|
||||
-48, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
},
|
||||
{ /* bi=5, bo=2 r=2.0:PSNR=43.175 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
88, 88, 88, 88, 88, 88, 88, 88,
|
||||
88, 88, 88, 88, 88, 88, 88, 88,
|
||||
88, 88, 88, 88, 88, 88, 88, 88,
|
||||
88, 88, 88, 88, 88, 88, 88, 88,
|
||||
88, 88, 88, 88, 88, 88, 88, 88,
|
||||
88, 88, 88, 88, 88, 88, 88, 88,
|
||||
88, 88, 88, 88, 88, 88, 88, 88,
|
||||
88, 88, 88, 88, 88, 88, 88, 88,
|
||||
0, -88, -88, -88, -88, -88, -88, -88,
|
||||
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||
-88, -88, -88, -88, -88, -88, -88, -88,
|
||||
-88, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
};
|
||||
const static signed char* zywrleParam[3][3][3]={
|
||||
{{zywrleConv[0],zywrleConv[2],zywrleConv[0]},{zywrleConv[0],zywrleConv[0],zywrleConv[0]},{zywrleConv[0],zywrleConv[0],zywrleConv[0]}},
|
||||
{{zywrleConv[0],zywrleConv[3],zywrleConv[0]},{zywrleConv[1],zywrleConv[1],zywrleConv[1]},{zywrleConv[0],zywrleConv[0],zywrleConv[0]}},
|
||||
{{zywrleConv[0],zywrleConv[3],zywrleConv[0]},{zywrleConv[2],zywrleConv[2],zywrleConv[2]},{zywrleConv[1],zywrleConv[1],zywrleConv[1]}},
|
||||
};
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static InlineX void Harr(signed char* pX0, signed char* pX1)
|
||||
{
|
||||
/* Piecewise-Linear Harr(PLHarr) */
|
||||
int X0 = (int)*pX0, X1 = (int)*pX1;
|
||||
int orgX0 = X0, orgX1 = X1;
|
||||
if ((X0 ^ X1) & 0x80) {
|
||||
/* differ sign */
|
||||
X1 += X0;
|
||||
if (((X1^orgX1)&0x80)==0) {
|
||||
/* |X1| > |X0| */
|
||||
X0 -= X1; /* H = -B */
|
||||
}
|
||||
} else {
|
||||
/* same sign */
|
||||
X0 -= X1;
|
||||
if (((X0 ^ orgX0) & 0x80) == 0) {
|
||||
/* |X0| > |X1| */
|
||||
X1 += X0; /* L = A */
|
||||
}
|
||||
}
|
||||
*pX0 = (signed char)X1;
|
||||
*pX1 = (signed char)X0;
|
||||
}
|
||||
/*
|
||||
1D-Wavelet transform.
|
||||
|
||||
In coefficients array, the famous 'pyramid' decomposition is well used.
|
||||
|
||||
1D Model:
|
||||
|L0L0L0L0|L0L0L0L0|H0H0H0H0|H0H0H0H0| : level 0
|
||||
|L1L1L1L1|H1H1H1H1|H0H0H0H0|H0H0H0H0| : level 1
|
||||
|
||||
But this method needs line buffer because H/L is different position from X0/X1.
|
||||
So, I used 'interleave' decomposition instead of it.
|
||||
|
||||
1D Model:
|
||||
|L0H0L0H0|L0H0L0H0|L0H0L0H0|L0H0L0H0| : level 0
|
||||
|L1H0H1H0|L1H0H1H0|L1H0H1H0|L1H0H1H0| : level 1
|
||||
|
||||
In this method, H/L and X0/X1 is always same position.
|
||||
This lead us to more speed and less memory.
|
||||
Of cause, the result of both method is quite same
|
||||
because its only difference is that coefficient position.
|
||||
*/
|
||||
static InlineX void WaveletLevel(int* data, int size, int l, int SkipPixel)
|
||||
{
|
||||
int s, ofs;
|
||||
signed char* pX0;
|
||||
signed char* end;
|
||||
|
||||
pX0 = (signed char*)data;
|
||||
s = (8<<l)*SkipPixel;
|
||||
end = pX0+(size>>(l+1))*s;
|
||||
s -= 2;
|
||||
ofs = (4<<l)*SkipPixel;
|
||||
while (pX0 < end) {
|
||||
Harr(pX0, pX0+ofs);
|
||||
pX0++;
|
||||
Harr(pX0, pX0+ofs);
|
||||
pX0++;
|
||||
Harr(pX0, pX0+ofs);
|
||||
pX0 += s;
|
||||
}
|
||||
}
|
||||
#define InvWaveletLevel(d,s,l,pix) WaveletLevel(d,s,l,pix)
|
||||
|
||||
#ifdef ZYWRLE_ENCODE
|
||||
# ifndef ZYWRLE_QUANTIZE
|
||||
/* Type A:lower bit omitting of EZW style. */
|
||||
static InlineX void FilterWaveletSquare(int* pBuf, int width, int height, int level, int l)
|
||||
{
|
||||
int r, s;
|
||||
int x, y;
|
||||
int* pH;
|
||||
const unsigned int* pM;
|
||||
|
||||
pM = &(zywrleParam[level-1][l]);
|
||||
s = 2<<l;
|
||||
for (r = 1; r < 4; r++) {
|
||||
pH = pBuf;
|
||||
if (r & 0x01)
|
||||
pH += s>>1;
|
||||
if (r & 0x02)
|
||||
pH += (s>>1)*width;
|
||||
for (y = 0; y < height / s; y++) {
|
||||
for (x = 0; x < width / s; x++) {
|
||||
/*
|
||||
these are same following code.
|
||||
pH[x] = pH[x] / (~pM[x]+1) * (~pM[x]+1);
|
||||
( round pH[x] with pM[x] bit )
|
||||
'&' operator isn't 'round' but is 'floor'.
|
||||
So, we must offset when pH[x] is negative.
|
||||
*/
|
||||
if (((signed char*)pH)[0] & 0x80)
|
||||
((signed char*)pH)[0] += ~((signed char*)pM)[0];
|
||||
if (((signed char*)pH)[1] & 0x80)
|
||||
((signed char*)pH)[1] += ~((signed char*)pM)[1];
|
||||
if (((signed char*)pH)[2] & 0x80)
|
||||
((signed char*)pH)[2] += ~((signed char*)pM)[2];
|
||||
*pH &= *pM;
|
||||
pH += s;
|
||||
}
|
||||
pH += (s-1)*width;
|
||||
}
|
||||
}
|
||||
}
|
||||
# else
|
||||
/*
|
||||
Type B:Non liner quantization filter.
|
||||
|
||||
Coefficients have Gaussian curve and smaller value which is
|
||||
large part of coefficients isn't more important than larger value.
|
||||
So, I use filter of Non liner quantize/dequantize table.
|
||||
In general, Non liner quantize formula is explained as following.
|
||||
|
||||
y=f(x) = sign(x)*round( ((abs(x)/(2^7))^ r )* 2^(bo-1) )*2^(8-bo)
|
||||
x=f-1(y) = sign(y)*round( ((abs(y)/(2^7))^(1/r))* 2^(bi-1) )*2^(8-bi)
|
||||
( r:power coefficient bi:effective MSB in input bo:effective MSB in output )
|
||||
|
||||
r < 1.0 : Smaller value is more important than larger value.
|
||||
r > 1.0 : Larger value is more important than smaller value.
|
||||
r = 1.0 : Liner quantization which is same with EZW style.
|
||||
|
||||
r = 0.75 is famous non liner quantization used in MP3 audio codec.
|
||||
In contrast to audio data, larger value is important in wavelet coefficients.
|
||||
So, I select r = 2.0 table( quantize is x^2, dequantize sqrt(x) ).
|
||||
|
||||
As compared with EZW style liner quantization, this filter tended to be
|
||||
more sharp edge and be more compression rate but be more blocking noise and be less quality.
|
||||
Especially, the surface of graphic objects has distinguishable noise in middle quality mode.
|
||||
|
||||
We need only quantized-dequantized(filtered) value rather than quantized value itself
|
||||
because all values are packed or palette-lized in later ZRLE section.
|
||||
This lead us not to need to modify client decoder when we change
|
||||
the filtering procedure in future.
|
||||
Client only decodes coefficients given by encoder.
|
||||
*/
|
||||
static InlineX void FilterWaveletSquare(int* pBuf, int width, int height, int level, int l)
|
||||
{
|
||||
int r, s;
|
||||
int x, y;
|
||||
int* pH;
|
||||
const signed char** pM;
|
||||
|
||||
pM = zywrleParam[level-1][l];
|
||||
s = 2<<l;
|
||||
for (r = 1; r < 4; r++) {
|
||||
pH = pBuf;
|
||||
if (r & 0x01)
|
||||
pH += s>>1;
|
||||
if (r & 0x02)
|
||||
pH += (s>>1)*width;
|
||||
for (y = 0; y < height / s; y++) {
|
||||
for (x = 0; x < width / s; x++) {
|
||||
((signed char*)pH)[0] = pM[0][((unsigned char*)pH)[0]];
|
||||
((signed char*)pH)[1] = pM[1][((unsigned char*)pH)[1]];
|
||||
((signed char*)pH)[2] = pM[2][((unsigned char*)pH)[2]];
|
||||
pH += s;
|
||||
}
|
||||
pH += (s-1)*width;
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
static InlineX void Wavelet(int* pBuf, int width, int height, int level)
|
||||
{
|
||||
int l, s;
|
||||
int* pTop;
|
||||
int* pEnd;
|
||||
|
||||
for (l = 0; l < level; l++) {
|
||||
pTop = pBuf;
|
||||
pEnd = pBuf+height*width;
|
||||
s = width<<l;
|
||||
while (pTop < pEnd) {
|
||||
WaveletLevel(pTop, width, l, 1);
|
||||
pTop += s;
|
||||
}
|
||||
pTop = pBuf;
|
||||
pEnd = pBuf+width;
|
||||
s = 1<<l;
|
||||
while (pTop < pEnd) {
|
||||
WaveletLevel(pTop, height,l, width);
|
||||
pTop += s;
|
||||
}
|
||||
FilterWaveletSquare(pBuf, width, height, level, l);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef ZYWRLE_DECODE
|
||||
static InlineX void InvWavelet(int* pBuf, int width, int height, int level)
|
||||
{
|
||||
int l, s;
|
||||
int* pTop;
|
||||
int* pEnd;
|
||||
|
||||
for (l = level - 1; l >= 0; l--) {
|
||||
pTop = pBuf;
|
||||
pEnd = pBuf+width;
|
||||
s = 1<<l;
|
||||
while (pTop < pEnd) {
|
||||
InvWaveletLevel(pTop, height,l, width);
|
||||
pTop += s;
|
||||
}
|
||||
pTop = pBuf;
|
||||
pEnd = pBuf+height*width;
|
||||
s = width<<l;
|
||||
while (pTop < pEnd) {
|
||||
InvWaveletLevel(pTop, width, l, 1);
|
||||
pTop += s;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Load/Save coefficients stuffs.
|
||||
Coefficients manages as 24 bits little-endian pixel. */
|
||||
#define ZYWRLE_LOAD_COEFF(pSrc,R,G,B) { \
|
||||
R = ((signed char*)pSrc)[2]; \
|
||||
G = ((signed char*)pSrc)[1]; \
|
||||
B = ((signed char*)pSrc)[0]; \
|
||||
}
|
||||
#define ZYWRLE_SAVE_COEFF(pDst,R,G,B) { \
|
||||
((signed char*)pDst)[2] = (signed char)R; \
|
||||
((signed char*)pDst)[1] = (signed char)G; \
|
||||
((signed char*)pDst)[0] = (signed char)B; \
|
||||
}
|
||||
|
||||
/*
|
||||
RGB <=> YUV conversion stuffs.
|
||||
YUV coversion is explained as following formula in strict meaning:
|
||||
Y = 0.299R + 0.587G + 0.114B ( 0<=Y<=255)
|
||||
U = -0.169R - 0.331G + 0.500B (-128<=U<=127)
|
||||
V = 0.500R - 0.419G - 0.081B (-128<=V<=127)
|
||||
|
||||
I use simple conversion RCT(reversible color transform) which is described
|
||||
in JPEG-2000 specification.
|
||||
Y = (R + 2G + B)/4 ( 0<=Y<=255)
|
||||
U = B-G (-256<=U<=255)
|
||||
V = R-G (-256<=V<=255)
|
||||
*/
|
||||
#define ROUND(x) (((x)<0)?0:(((x)>255)?255:(x)))
|
||||
/* RCT is N-bit RGB to N-bit Y and N+1-bit UV.
|
||||
For make Same N-bit, UV is lossy.
|
||||
More exact PLHarr, we reduce to odd range(-127<=x<=127). */
|
||||
#define ZYWRLE_RGBYUV1(R,G,B,Y,U,V,ymask,uvmask) { \
|
||||
Y = (R+(G<<1)+B)>>2; \
|
||||
U = B-G; \
|
||||
V = R-G; \
|
||||
Y -= 128; \
|
||||
U >>= 1; \
|
||||
V >>= 1; \
|
||||
Y &= ymask; \
|
||||
U &= uvmask; \
|
||||
V &= uvmask; \
|
||||
if (Y == -128) \
|
||||
Y += (0xFFFFFFFF-ymask+1); \
|
||||
if (U == -128) \
|
||||
U += (0xFFFFFFFF-uvmask+1); \
|
||||
if (V == -128) \
|
||||
V += (0xFFFFFFFF-uvmask+1); \
|
||||
}
|
||||
#define ZYWRLE_YUVRGB1(R,G,B,Y,U,V) { \
|
||||
Y += 128; \
|
||||
U <<= 1; \
|
||||
V <<= 1; \
|
||||
G = Y-((U+V)>>2); \
|
||||
B = U+G; \
|
||||
R = V+G; \
|
||||
G = ROUND(G); \
|
||||
B = ROUND(B); \
|
||||
R = ROUND(R); \
|
||||
}
|
||||
|
||||
/*
|
||||
coefficient packing/unpacking stuffs.
|
||||
Wavelet transform makes 4 sub coefficient image from 1 original image.
|
||||
|
||||
model with pyramid decomposition:
|
||||
+------+------+
|
||||
| | |
|
||||
| L | Hx |
|
||||
| | |
|
||||
+------+------+
|
||||
| | |
|
||||
| H | Hxy |
|
||||
| | |
|
||||
+------+------+
|
||||
|
||||
So, we must transfer each sub images individually in strict meaning.
|
||||
But at least ZRLE meaning, following one decompositon image is same as
|
||||
avobe individual sub image. I use this format.
|
||||
(Strictly saying, transfer order is reverse(Hxy->Hy->Hx->L)
|
||||
for simplified procedure for any wavelet level.)
|
||||
|
||||
+------+------+
|
||||
| L |
|
||||
+------+------+
|
||||
| Hx |
|
||||
+------+------+
|
||||
| Hy |
|
||||
+------+------+
|
||||
| Hxy |
|
||||
+------+------+
|
||||
*/
|
||||
#define INC_PTR(data) \
|
||||
data++; \
|
||||
if( data-pData >= (w+uw) ){ \
|
||||
data += scanline-(w+uw); \
|
||||
pData = data; \
|
||||
}
|
||||
|
||||
#define ZYWRLE_TRANSFER_COEFF(pBuf,data,r,w,h,scanline,level,TRANS) \
|
||||
pH = pBuf; \
|
||||
s = 2<<level; \
|
||||
if (r & 0x01) \
|
||||
pH += s>>1; \
|
||||
if (r & 0x02) \
|
||||
pH += (s>>1)*w; \
|
||||
pEnd = pH+h*w; \
|
||||
while (pH < pEnd) { \
|
||||
pLine = pH+w; \
|
||||
while (pH < pLine) { \
|
||||
TRANS \
|
||||
INC_PTR(data) \
|
||||
pH += s; \
|
||||
} \
|
||||
pH += (s-1)*w; \
|
||||
}
|
||||
|
||||
#define ZYWRLE_PACK_COEFF(pBuf,data,r,width,height,scanline,level) \
|
||||
ZYWRLE_TRANSFER_COEFF(pBuf,data,r,width,height,scanline,level,ZYWRLE_LOAD_COEFF(pH,R,G,B);ZYWRLE_SAVE_PIXEL(data,R,G,B);)
|
||||
|
||||
#define ZYWRLE_UNPACK_COEFF(pBuf,data,r,width,height,scanline,level) \
|
||||
ZYWRLE_TRANSFER_COEFF(pBuf,data,r,width,height,scanline,level,ZYWRLE_LOAD_PIXEL(data,R,G,B);ZYWRLE_SAVE_COEFF(pH,R,G,B);)
|
||||
|
||||
#define ZYWRLE_SAVE_UNALIGN(data,TRANS) \
|
||||
pTop = pBuf+w*h; \
|
||||
pEnd = pBuf + (w+uw)*(h+uh); \
|
||||
while (pTop < pEnd) { \
|
||||
TRANS \
|
||||
INC_PTR(data) \
|
||||
pTop++; \
|
||||
}
|
||||
|
||||
#define ZYWRLE_LOAD_UNALIGN(data,TRANS) \
|
||||
pTop = pBuf+w*h; \
|
||||
if (uw) { \
|
||||
pData= data + w; \
|
||||
pEnd = (int*)(pData+ h*scanline); \
|
||||
while (pData < (PIXEL_T*)pEnd) { \
|
||||
pLine = (int*)(pData + uw); \
|
||||
while (pData < (PIXEL_T*)pLine) { \
|
||||
TRANS \
|
||||
pData++; \
|
||||
pTop++; \
|
||||
} \
|
||||
pData += scanline-uw; \
|
||||
} \
|
||||
} \
|
||||
if (uh) { \
|
||||
pData= data + h*scanline; \
|
||||
pEnd = (int*)(pData+ uh*scanline); \
|
||||
while (pData < (PIXEL_T*)pEnd) { \
|
||||
pLine = (int*)(pData + w); \
|
||||
while (pData < (PIXEL_T*)pLine) { \
|
||||
TRANS \
|
||||
pData++; \
|
||||
pTop++; \
|
||||
} \
|
||||
pData += scanline-w; \
|
||||
} \
|
||||
} \
|
||||
if (uw && uh) { \
|
||||
pData= data + w+ h*scanline; \
|
||||
pEnd = (int*)(pData+ uh*scanline); \
|
||||
while (pData < (PIXEL_T*)pEnd) { \
|
||||
pLine = (int*)(pData + uw); \
|
||||
while (pData < (PIXEL_T*)pLine) { \
|
||||
TRANS \
|
||||
pData++; \
|
||||
pTop++; \
|
||||
} \
|
||||
pData += scanline-uw; \
|
||||
} \
|
||||
}
|
||||
|
||||
static InlineX void zywrleCalcSize(int* pW, int* pH, int level)
|
||||
{
|
||||
*pW &= ~((1<<level)-1);
|
||||
*pH &= ~((1<<level)-1);
|
||||
}
|
||||
|
||||
#endif /* ZYWRLE_ONCE */
|
||||
|
||||
#ifndef CPIXEL
|
||||
#ifdef ZYWRLE_ENCODE
|
||||
static InlineX void ZYWRLE_RGBYUV(int* pBuf, PIXEL_T* data, int width, int height, int scanline)
|
||||
{
|
||||
int R, G, B;
|
||||
int Y, U, V;
|
||||
int* pLine;
|
||||
int* pEnd;
|
||||
pEnd = pBuf+height*width;
|
||||
while (pBuf < pEnd) {
|
||||
pLine = pBuf+width;
|
||||
while (pBuf < pLine) {
|
||||
ZYWRLE_LOAD_PIXEL(data,R,G,B);
|
||||
ZYWRLE_RGBYUV1(R,G,B,Y,U,V,ZYWRLE_YMASK,ZYWRLE_UVMASK);
|
||||
ZYWRLE_SAVE_COEFF(pBuf,V,Y,U);
|
||||
pBuf++;
|
||||
data++;
|
||||
}
|
||||
data += scanline-width;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef ZYWRLE_DECODE
|
||||
static InlineX void ZYWRLE_YUVRGB(int* pBuf, PIXEL_T* data, int width, int height, int scanline) {
|
||||
int R, G, B;
|
||||
int Y, U, V;
|
||||
int* pLine;
|
||||
int* pEnd;
|
||||
pEnd = pBuf+height*width;
|
||||
while (pBuf < pEnd) {
|
||||
pLine = pBuf+width;
|
||||
while (pBuf < pLine) {
|
||||
ZYWRLE_LOAD_COEFF(pBuf,V,Y,U);
|
||||
ZYWRLE_YUVRGB1(R,G,B,Y,U,V);
|
||||
ZYWRLE_SAVE_PIXEL(data,R,G,B);
|
||||
pBuf++;
|
||||
data++;
|
||||
}
|
||||
data += scanline-width;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ZYWRLE_ENCODE
|
||||
PIXEL_T* ZYWRLE_ANALYZE(PIXEL_T* dst, PIXEL_T* src, int w, int h, int scanline, int level, int* pBuf) {
|
||||
int l;
|
||||
int uw = w;
|
||||
int uh = h;
|
||||
int* pTop;
|
||||
int* pEnd;
|
||||
int* pLine;
|
||||
PIXEL_T* pData;
|
||||
int R, G, B;
|
||||
int s;
|
||||
int* pH;
|
||||
|
||||
zywrleCalcSize(&w, &h, level);
|
||||
if (w == 0 || h == 0)
|
||||
return NULL;
|
||||
uw -= w;
|
||||
uh -= h;
|
||||
|
||||
pData = dst;
|
||||
ZYWRLE_LOAD_UNALIGN(src,*(PIXEL_T*)pTop=*pData;)
|
||||
ZYWRLE_RGBYUV(pBuf, src, w, h, scanline);
|
||||
Wavelet(pBuf, w, h, level);
|
||||
for (l = 0; l < level; l++) {
|
||||
ZYWRLE_PACK_COEFF(pBuf, dst, 3, w, h, scanline, l);
|
||||
ZYWRLE_PACK_COEFF(pBuf, dst, 2, w, h, scanline, l);
|
||||
ZYWRLE_PACK_COEFF(pBuf, dst, 1, w, h, scanline, l);
|
||||
if (l == level - 1) {
|
||||
ZYWRLE_PACK_COEFF(pBuf, dst, 0, w, h, scanline, l);
|
||||
}
|
||||
}
|
||||
ZYWRLE_SAVE_UNALIGN(dst,*dst=*(PIXEL_T*)pTop;)
|
||||
return dst;
|
||||
}
|
||||
#endif
|
||||
#ifdef ZYWRLE_DECODE
|
||||
PIXEL_T* ZYWRLE_SYNTHESIZE(PIXEL_T* dst, PIXEL_T* src, int w, int h, int scanline, int level, int* pBuf)
|
||||
{
|
||||
int l;
|
||||
int uw = w;
|
||||
int uh = h;
|
||||
int* pTop;
|
||||
int* pEnd;
|
||||
int* pLine;
|
||||
PIXEL_T* pData;
|
||||
int R, G, B;
|
||||
int s;
|
||||
int* pH;
|
||||
|
||||
zywrleCalcSize(&w, &h, level);
|
||||
if (w == 0 || h == 0)
|
||||
return NULL;
|
||||
uw -= w;
|
||||
uh -= h;
|
||||
|
||||
pData = src;
|
||||
for (l = 0; l < level; l++) {
|
||||
ZYWRLE_UNPACK_COEFF(pBuf, src, 3, w, h, scanline, l);
|
||||
ZYWRLE_UNPACK_COEFF(pBuf, src, 2, w, h, scanline, l);
|
||||
ZYWRLE_UNPACK_COEFF(pBuf, src, 1, w, h, scanline, l);
|
||||
if (l == level - 1) {
|
||||
ZYWRLE_UNPACK_COEFF(pBuf, src, 0, w, h, scanline, l);
|
||||
}
|
||||
}
|
||||
ZYWRLE_SAVE_UNALIGN(src,*(PIXEL_T*)pTop=*src;)
|
||||
InvWavelet(pBuf, w, h, level);
|
||||
ZYWRLE_YUVRGB(pBuf, dst, w, h, scanline);
|
||||
ZYWRLE_LOAD_UNALIGN(dst,*pData=*(PIXEL_T*)pTop;)
|
||||
return src;
|
||||
}
|
||||
#endif
|
||||
#endif /* CPIXEL */
|
||||
|
||||
#undef ZYWRLE_RGBYUV
|
||||
#undef ZYWRLE_YUVRGB
|
||||
#undef ZYWRLE_LOAD_PIXEL
|
||||
#undef ZYWRLE_SAVE_PIXEL
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* Copyright (C) 2011-2012 Christian Beier <dontmind@freeshell.org>
|
||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* listen.c - listen for incoming connections
|
||||
*/
|
||||
|
||||
#ifdef __STRICT_ANSI__
|
||||
#define _BSD_SOURCE
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/time.h>
|
||||
#include <rfb/rfbclient.h>
|
||||
|
||||
/*
|
||||
* listenForIncomingConnections() - listen for incoming connections from
|
||||
* servers, and fork a new process to deal with each connection.
|
||||
*/
|
||||
|
||||
void
|
||||
listenForIncomingConnections(rfbClient* client)
|
||||
{
|
||||
int listenSocket = RFB_INVALID_SOCKET, listen6Socket = RFB_INVALID_SOCKET;
|
||||
fd_set fds;
|
||||
|
||||
client->listenSpecified = TRUE;
|
||||
|
||||
listenSocket = ListenAtTcpPortAndAddress(client->listenPort, client->listenAddress);
|
||||
|
||||
if (listenSocket == RFB_INVALID_SOCKET)
|
||||
return;
|
||||
|
||||
rfbClientLog("%s -listen: Listening on port %d\n",
|
||||
client->programName,client->listenPort);
|
||||
rfbClientLog("%s -listen: Command line errors are not reported until "
|
||||
"a connection comes in.\n", client->programName);
|
||||
|
||||
#ifdef LIBVNCSERVER_IPv6 /* only try that if we're IPv6-capable, otherwise we may try to bind to the same port which would make all that listening fail */
|
||||
/* only do IPv6 listen of listen6Port is set */
|
||||
if (client->listen6Port != RFB_INVALID_SOCKET)
|
||||
{
|
||||
listen6Socket = ListenAtTcpPortAndAddress(client->listen6Port, client->listen6Address);
|
||||
|
||||
if (listen6Socket == RFB_INVALID_SOCKET)
|
||||
return;
|
||||
|
||||
rfbClientLog("%s -listen: Listening on IPV6 port %d\n",
|
||||
client->programName,client->listenPort);
|
||||
rfbClientLog("%s -listen: Command line errors are not reported until "
|
||||
"a connection comes in.\n", client->programName);
|
||||
}
|
||||
|
||||
while (TRUE) {
|
||||
int r;
|
||||
/* reap any zombies */
|
||||
int status, pid;
|
||||
while ((pid= wait4(-1, &status, WNOHANG, (struct rusage *)0))>0);
|
||||
|
||||
/* TODO: callback for discard any events (like X11 events) */
|
||||
|
||||
FD_ZERO(&fds);
|
||||
|
||||
if(listenSocket != RFB_INVALID_SOCKET)
|
||||
FD_SET(listenSocket, &fds);
|
||||
if(listen6Socket != RFB_INVALID_SOCKET)
|
||||
FD_SET(listen6Socket, &fds);
|
||||
|
||||
r = select(rfbMax(listenSocket, listen6Socket)+1, &fds, NULL, NULL, NULL);
|
||||
|
||||
if (r > 0) {
|
||||
if (FD_ISSET(listenSocket, &fds))
|
||||
client->sock = AcceptTcpConnection(client->listenSock);
|
||||
else if (FD_ISSET(listen6Socket, &fds))
|
||||
client->sock = AcceptTcpConnection(client->listen6Sock);
|
||||
|
||||
if (client->sock == RFB_INVALID_SOCKET)
|
||||
return;
|
||||
if (!SetNonBlocking(client->sock))
|
||||
return;
|
||||
|
||||
/* Now fork off a new process to deal with it... */
|
||||
|
||||
switch (fork()) {
|
||||
|
||||
case -1:
|
||||
rfbClientErr("fork\n");
|
||||
return;
|
||||
|
||||
case 0:
|
||||
/* child - return to caller */
|
||||
rfbCloseSocket(listenSocket);
|
||||
rfbCloseSocket(listen6Socket);
|
||||
return;
|
||||
|
||||
default:
|
||||
/* parent - go round and listen again */
|
||||
rfbCloseSocket(client->sock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* listenForIncomingConnectionsNoFork() - listen for incoming connections
|
||||
* from servers, but DON'T fork, instead just wait timeout microseconds.
|
||||
* If timeout is negative, block indefinitely.
|
||||
* Returns 1 on success (there was an incoming connection on the listen socket
|
||||
* and we accepted it successfully), -1 on error, 0 on timeout.
|
||||
*/
|
||||
|
||||
int
|
||||
listenForIncomingConnectionsNoFork(rfbClient* client, int timeout)
|
||||
{
|
||||
fd_set fds;
|
||||
struct timeval to;
|
||||
int r;
|
||||
|
||||
to.tv_sec= timeout / 1000000;
|
||||
to.tv_usec= timeout % 1000000;
|
||||
|
||||
client->listenSpecified = TRUE;
|
||||
|
||||
if (client->listenSock == RFB_INVALID_SOCKET)
|
||||
{
|
||||
client->listenSock = ListenAtTcpPortAndAddress(client->listenPort, client->listenAddress);
|
||||
|
||||
if (client->listenSock == RFB_INVALID_SOCKET)
|
||||
return -1;
|
||||
|
||||
rfbClientLog("%s -listennofork: Listening on port %d\n",
|
||||
client->programName,client->listenPort);
|
||||
rfbClientLog("%s -listennofork: Command line errors are not reported until "
|
||||
"a connection comes in.\n", client->programName);
|
||||
}
|
||||
|
||||
#ifdef LIBVNCSERVER_IPv6 /* only try that if we're IPv6-capable, otherwise we may try to bind to the same port which would make all that listening fail */
|
||||
/* only do IPv6 listen of listen6Port is set */
|
||||
if (client->listen6Port != RFB_INVALID_SOCKET && client->listen6Sock == RFB_INVALID_SOCKET)
|
||||
{
|
||||
client->listen6Sock = ListenAtTcpPortAndAddress(client->listen6Port, client->listen6Address);
|
||||
|
||||
if (client->listen6Sock == RFB_INVALID_SOCKET)
|
||||
return -1;
|
||||
|
||||
rfbClientLog("%s -listennofork: Listening on IPV6 port %d\n",
|
||||
client->programName,client->listenPort);
|
||||
rfbClientLog("%s -listennofork: Command line errors are not reported until "
|
||||
"a connection comes in.\n", client->programName);
|
||||
}
|
||||
#endif
|
||||
|
||||
FD_ZERO(&fds);
|
||||
|
||||
if(client->listenSock != RFB_INVALID_SOCKET)
|
||||
FD_SET(client->listenSock, &fds);
|
||||
if(client->listen6Sock != RFB_INVALID_SOCKET)
|
||||
FD_SET(client->listen6Sock, &fds);
|
||||
|
||||
if (timeout < 0)
|
||||
r = select(rfbMax(client->listenSock, client->listen6Sock) +1, &fds, NULL, NULL, NULL);
|
||||
else
|
||||
r = select(rfbMax(client->listenSock, client->listen6Sock) +1, &fds, NULL, NULL, &to);
|
||||
|
||||
if (r > 0)
|
||||
{
|
||||
if (FD_ISSET(client->listenSock, &fds))
|
||||
client->sock = AcceptTcpConnection(client->listenSock);
|
||||
else if (FD_ISSET(client->listen6Sock, &fds))
|
||||
client->sock = AcceptTcpConnection(client->listen6Sock);
|
||||
|
||||
if (client->sock == RFB_INVALID_SOCKET)
|
||||
return -1;
|
||||
if (!SetNonBlocking(client->sock))
|
||||
return -1;
|
||||
|
||||
if(client->listenSock != RFB_INVALID_SOCKET) {
|
||||
rfbCloseSocket(client->listenSock);
|
||||
client->listenSock = RFB_INVALID_SOCKET;
|
||||
}
|
||||
if(client->listen6Sock != RFB_INVALID_SOCKET) {
|
||||
rfbCloseSocket(client->listen6Sock);
|
||||
client->listen6Sock = RFB_INVALID_SOCKET;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* r is now either 0 (timeout) or -1 (error) */
|
||||
return r;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,567 @@
|
|||
/*
|
||||
* The software in this file is derived from the vncconnection.c source file
|
||||
* from the GTK VNC Widget with modifications by S. Waterman <simon.waterman@zynstra.com>
|
||||
* for compatibility with libvncserver. The copyright and license
|
||||
* statements below apply only to this source file and to no other parts of the
|
||||
* libvncserver library.
|
||||
*
|
||||
* GTK VNC Widget
|
||||
*
|
||||
* Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
|
||||
* Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* sasl.c - functions to deal with client side of the SASL protocol.
|
||||
*/
|
||||
|
||||
#ifdef __STRICT_ANSI__
|
||||
#define _BSD_SOURCE
|
||||
#define _POSIX_SOURCE
|
||||
#define _XOPEN_SOURCE 600
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <rfb/rfbclient.h>
|
||||
|
||||
#include "sockets.h"
|
||||
|
||||
#include "sasl.h"
|
||||
|
||||
#include "tls.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define snprintf _snprintf /* MSVC went straight to the underscored syntax */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* NB, keep in sync with similar method in qemud/remote.c
|
||||
*/
|
||||
static char *vnc_connection_addr_to_string(char *host, int port)
|
||||
{
|
||||
char * buf = (char *)malloc(strlen(host) + 7);
|
||||
if (buf) {
|
||||
sprintf(buf, "%s;%hu", host, (unsigned short)port);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int log_func(void *context,
|
||||
int level,
|
||||
const char *message)
|
||||
{
|
||||
rfbClientLog("SASL: %s\n", message);
|
||||
|
||||
return SASL_OK;
|
||||
}
|
||||
|
||||
static int user_callback_adapt(void *context,
|
||||
int id,
|
||||
const char **result,
|
||||
unsigned *len)
|
||||
{
|
||||
rfbClient* client = (rfbClient *)context;
|
||||
|
||||
if (id != SASL_CB_AUTHNAME) {
|
||||
rfbClientLog("Unrecognized SASL callback ID %d\n", id);
|
||||
return SASL_FAIL;
|
||||
}
|
||||
|
||||
if (!client->GetUser) {
|
||||
rfbClientLog("Client user callback not found\n");
|
||||
return SASL_FAIL;
|
||||
}
|
||||
|
||||
*result = client->GetUser(client);
|
||||
|
||||
if (! *result) return SASL_FAIL;
|
||||
/**len = strlen(*result);*/
|
||||
return SASL_OK;
|
||||
}
|
||||
|
||||
static int password_callback_adapt(sasl_conn_t *conn,
|
||||
void * context,
|
||||
int id,
|
||||
sasl_secret_t **secret)
|
||||
{
|
||||
rfbClient* client = (rfbClient *)context;
|
||||
char * password;
|
||||
|
||||
if (id != SASL_CB_PASS) {
|
||||
rfbClientLog("Unrecognized SASL callback ID %d\n", id);
|
||||
return SASL_FAIL;
|
||||
}
|
||||
|
||||
if (client->saslSecret) { /* If we've already got it just return it. */
|
||||
*secret = client->saslSecret;
|
||||
return SASL_OK;
|
||||
}
|
||||
|
||||
if (!client->GetPassword) {
|
||||
rfbClientLog("Client password callback not found\n");
|
||||
return SASL_FAIL;
|
||||
}
|
||||
|
||||
password = client->GetPassword(client);
|
||||
|
||||
if (! password) return SASL_FAIL;
|
||||
|
||||
sasl_secret_t *lsec = (sasl_secret_t *)malloc(sizeof(sasl_secret_t) + strlen(password));
|
||||
if (!lsec) {
|
||||
rfbClientLog("Could not allocate sasl_secret_t\n");
|
||||
return SASL_FAIL;
|
||||
}
|
||||
|
||||
strcpy((char *)lsec->data, password);
|
||||
lsec->len = strlen(password);
|
||||
client->saslSecret = lsec;
|
||||
*secret = lsec;
|
||||
|
||||
/* Clear client password */
|
||||
size_t i;
|
||||
for (i = 0; i < lsec->len; i++) {
|
||||
password[i] = '\0';
|
||||
}
|
||||
free(password);
|
||||
|
||||
return SASL_OK;
|
||||
}
|
||||
|
||||
#define SASL_MAX_MECHLIST_LEN 300
|
||||
#define SASL_MAX_DATA_LEN (1024 * 1024)
|
||||
|
||||
/* Perform the SASL authentication process
|
||||
*/
|
||||
rfbBool
|
||||
HandleSASLAuth(rfbClient *client)
|
||||
{
|
||||
sasl_conn_t *saslconn = NULL;
|
||||
sasl_security_properties_t secprops;
|
||||
const char *clientout;
|
||||
char *serverin = NULL;
|
||||
unsigned int clientoutlen, serverinlen;
|
||||
int err, complete = 0;
|
||||
char *localAddr = NULL, *remoteAddr = NULL;
|
||||
const void *val;
|
||||
sasl_ssf_t ssf;
|
||||
sasl_callback_t saslcb[] = {
|
||||
{SASL_CB_LOG, (void *)log_func, NULL},
|
||||
{SASL_CB_AUTHNAME, client->GetUser ? (void *)user_callback_adapt : NULL, client},
|
||||
{SASL_CB_PASS, client->GetPassword ? (void *)password_callback_adapt : NULL, client},
|
||||
{ .id = 0 },
|
||||
};
|
||||
sasl_interact_t *interact = NULL;
|
||||
uint32_t mechlistlen;
|
||||
char *mechlist;
|
||||
char *wantmech;
|
||||
const char *mechname;
|
||||
|
||||
client->saslconn = NULL;
|
||||
|
||||
/* Sets up the SASL library as a whole */
|
||||
err = sasl_client_init(NULL);
|
||||
rfbClientLog("Client initialize SASL authentication %d\n", err);
|
||||
if (err != SASL_OK) {
|
||||
rfbClientLog("failed to initialize SASL library: %d (%s)\n",
|
||||
err, sasl_errstring(err, NULL, NULL));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Get local address in form IPADDR:PORT */
|
||||
struct sockaddr_storage localAddress;
|
||||
socklen_t addressLength = sizeof(localAddress);
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
int port;
|
||||
|
||||
if (getsockname(client->sock, (struct sockaddr*)&localAddress, &addressLength)) {
|
||||
rfbClientLog("failed to get local address\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (localAddress.ss_family == AF_INET) {
|
||||
struct sockaddr_in *sa_in = (struct sockaddr_in*)&localAddress;
|
||||
inet_ntop(AF_INET, &(sa_in->sin_addr), buf, INET_ADDRSTRLEN);
|
||||
port = ntohs(sa_in->sin_port);
|
||||
localAddr = vnc_connection_addr_to_string(buf, port);
|
||||
} else if (localAddress.ss_family == AF_INET6) {
|
||||
struct sockaddr_in6 *sa_in = (struct sockaddr_in6*)&localAddress;
|
||||
inet_ntop(AF_INET6, &(sa_in->sin6_addr), buf, INET6_ADDRSTRLEN);
|
||||
port = ntohs(sa_in->sin6_port);
|
||||
localAddr = vnc_connection_addr_to_string(buf, port);
|
||||
} else {
|
||||
rfbClientLog("failed to get local address\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Get remote address in form IPADDR:PORT */
|
||||
remoteAddr = vnc_connection_addr_to_string(client->serverHost, client->serverPort);
|
||||
|
||||
rfbClientLog("Client SASL new host:'%s' local:'%s' remote:'%s'\n", client->serverHost, localAddr, remoteAddr);
|
||||
|
||||
/* Setup a handle for being a client */
|
||||
err = sasl_client_new("vnc",
|
||||
client->serverHost,
|
||||
localAddr,
|
||||
remoteAddr,
|
||||
saslcb,
|
||||
SASL_SUCCESS_DATA,
|
||||
&saslconn);
|
||||
free(localAddr);
|
||||
free(remoteAddr);
|
||||
|
||||
if (err != SASL_OK) {
|
||||
rfbClientLog("Failed to create SASL client context: %d (%s)\n",
|
||||
err, sasl_errstring(err, NULL, NULL));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Initialize some connection props we care about */
|
||||
if (client->tlsSession) {
|
||||
if (!(ssf = (sasl_ssf_t)GetTLSCipherBits(client))) {
|
||||
rfbClientLog("%s", "invalid cipher size for TLS session\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rfbClientLog("Setting external SSF %d\n", ssf);
|
||||
err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf);
|
||||
if (err != SASL_OK) {
|
||||
rfbClientLog("cannot set external SSF %d (%s)\n",
|
||||
err, sasl_errstring(err, NULL, NULL));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
memset (&secprops, 0, sizeof secprops);
|
||||
/* If we've got TLS, we don't care about SSF */
|
||||
secprops.min_ssf = client->tlsSession ? 0 : 56; /* Equiv to DES supported by all Kerberos */
|
||||
secprops.max_ssf = client->tlsSession ? 0 : 100000; /* Very strong ! AES == 256 */
|
||||
secprops.maxbufsize = 100000;
|
||||
/* If we're not TLS, then forbid any anonymous or trivially crackable auth */
|
||||
secprops.security_flags = client->tlsSession ? 0 :
|
||||
SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
|
||||
|
||||
err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops);
|
||||
if (err != SASL_OK) {
|
||||
rfbClientLog("cannot set security props %d (%s)\n",
|
||||
err, sasl_errstring(err, NULL, NULL));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Get the supported mechanisms from the server */
|
||||
if (!ReadFromRFBServer(client, (char *)&mechlistlen, 4)) {
|
||||
rfbClientLog("failed to read mechlistlen\n");
|
||||
goto error;
|
||||
}
|
||||
mechlistlen = rfbClientSwap32IfLE(mechlistlen);
|
||||
rfbClientLog("mechlistlen is %d\n", mechlistlen);
|
||||
if (mechlistlen > SASL_MAX_MECHLIST_LEN) {
|
||||
rfbClientLog("mechlistlen %d too long\n", mechlistlen);
|
||||
goto error;
|
||||
}
|
||||
|
||||
mechlist = malloc(mechlistlen+1);
|
||||
if (!mechlist || !ReadFromRFBServer(client, mechlist, mechlistlen)) {
|
||||
free(mechlist);
|
||||
goto error;
|
||||
}
|
||||
mechlist[mechlistlen] = '\0';
|
||||
|
||||
/* Allow the client to influence the mechanism selected. */
|
||||
if (client->GetSASLMechanism) {
|
||||
wantmech = client->GetSASLMechanism(client, mechlist);
|
||||
|
||||
if (wantmech && *wantmech != 0) {
|
||||
if (strstr(mechlist, wantmech) == NULL) {
|
||||
rfbClientLog("Client requested SASL mechanism %s not supported by server\n",
|
||||
wantmech);
|
||||
free(mechlist);
|
||||
free(wantmech);
|
||||
goto error;
|
||||
} else {
|
||||
free(mechlist);
|
||||
mechlist = wantmech;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rfbClientLog("Client start negotiation mechlist '%s'\n", mechlist);
|
||||
|
||||
/* Start the auth negotiation on the client end first */
|
||||
err = sasl_client_start(saslconn,
|
||||
mechlist,
|
||||
&interact,
|
||||
&clientout,
|
||||
&clientoutlen,
|
||||
&mechname);
|
||||
if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) {
|
||||
rfbClientLog("Failed to start SASL negotiation: %d (%s)\n",
|
||||
err, sasl_errdetail(saslconn));
|
||||
free(mechlist);
|
||||
mechlist = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Need to gather some credentials from the client */
|
||||
if (err == SASL_INTERACT) {
|
||||
rfbClientLog("User interaction required but not currently supported\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rfbClientLog("Server start negotiation with mech %s. Data %d bytes %p '%s'\n",
|
||||
mechname, clientoutlen, clientout, clientout);
|
||||
|
||||
if (clientoutlen > SASL_MAX_DATA_LEN) {
|
||||
rfbClientLog("SASL negotiation data too long: %d bytes\n",
|
||||
clientoutlen);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Send back the chosen mechname */
|
||||
uint32_t mechnamelen = rfbClientSwap32IfLE(strlen(mechname));
|
||||
if (!WriteToRFBServer(client, (char *)&mechnamelen, 4)) goto error;
|
||||
if (!WriteToRFBServer(client, (char *)mechname, strlen(mechname))) goto error;
|
||||
|
||||
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
||||
if (clientout) {
|
||||
uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1);
|
||||
if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error;
|
||||
if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error;
|
||||
} else {
|
||||
uint32_t temp = 0;
|
||||
if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error;
|
||||
}
|
||||
|
||||
rfbClientLog("%s", "Getting sever start negotiation reply\n");
|
||||
/* Read the 'START' message reply from server */
|
||||
if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error;
|
||||
serverinlen = rfbClientSwap32IfLE(serverinlen);
|
||||
|
||||
if (serverinlen > SASL_MAX_DATA_LEN) {
|
||||
rfbClientLog("SASL negotiation data too long: %d bytes\n",
|
||||
serverinlen);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
||||
if (serverinlen) {
|
||||
serverin = malloc(serverinlen);
|
||||
if (!serverin || !ReadFromRFBServer(client, serverin, serverinlen)) goto error;
|
||||
serverin[serverinlen-1] = '\0';
|
||||
serverinlen--;
|
||||
} else {
|
||||
serverin = NULL;
|
||||
}
|
||||
if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error;
|
||||
|
||||
rfbClientLog("Client start result complete: %d. Data %d bytes %p '%s'\n",
|
||||
complete, serverinlen, serverin, serverin);
|
||||
|
||||
/* Loop-the-loop...
|
||||
* Even if the server has completed, the client must *always* do at least one step
|
||||
* in this loop to verify the server isn't lying about something. Mutual auth */
|
||||
for (;;) {
|
||||
err = sasl_client_step(saslconn,
|
||||
serverin,
|
||||
serverinlen,
|
||||
&interact,
|
||||
&clientout,
|
||||
&clientoutlen);
|
||||
if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) {
|
||||
rfbClientLog("Failed SASL step: %d (%s)\n",
|
||||
err, sasl_errdetail(saslconn));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Need to gather some credentials from the client */
|
||||
if (err == SASL_INTERACT) {
|
||||
rfbClientLog("User interaction required but not currently supported\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (serverin) {
|
||||
free(serverin);
|
||||
serverin = NULL;
|
||||
}
|
||||
|
||||
rfbClientLog("Client step result %d. Data %d bytes %p '%s'\n", err, clientoutlen, clientout, clientout);
|
||||
|
||||
/* Previous server call showed completion & we're now locally complete too */
|
||||
if (complete && err == SASL_OK)
|
||||
break;
|
||||
|
||||
/* Not done, prepare to talk with the server for another iteration */
|
||||
|
||||
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
||||
if (clientout) {
|
||||
uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1);
|
||||
if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error;
|
||||
if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error;
|
||||
} else {
|
||||
uint32_t temp = 0;
|
||||
if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error;
|
||||
}
|
||||
|
||||
rfbClientLog("Server step with %d bytes %p\n", clientoutlen, clientout);
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error;
|
||||
serverinlen = rfbClientSwap32IfLE(serverinlen);
|
||||
|
||||
if (serverinlen > SASL_MAX_DATA_LEN) {
|
||||
rfbClientLog("SASL negotiation data too long: %d bytes\n",
|
||||
serverinlen);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* NB, distinction of NULL vs "" is *critical* in SASL */
|
||||
if (serverinlen) {
|
||||
serverin = malloc(serverinlen);
|
||||
if (!serverin || !ReadFromRFBServer(client, serverin, serverinlen)) goto error;
|
||||
serverin[serverinlen-1] = '\0';
|
||||
serverinlen--;
|
||||
} else {
|
||||
serverin = NULL;
|
||||
}
|
||||
if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error;
|
||||
|
||||
rfbClientLog("Client step result complete: %d. Data %d bytes %p '%s'\n",
|
||||
complete, serverinlen, serverin, serverin);
|
||||
|
||||
/* This server call shows complete, and earlier client step was OK */
|
||||
if (complete && err == SASL_OK) {
|
||||
free(serverin);
|
||||
serverin = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for suitable SSF if non-TLS */
|
||||
if (!client->tlsSession) {
|
||||
err = sasl_getprop(saslconn, SASL_SSF, &val);
|
||||
if (err != SASL_OK) {
|
||||
rfbClientLog("cannot query SASL ssf on connection %d (%s)\n",
|
||||
err, sasl_errstring(err, NULL, NULL));
|
||||
goto error;
|
||||
}
|
||||
ssf = *(const int *)val;
|
||||
rfbClientLog("SASL SSF value %d\n", ssf);
|
||||
if (ssf < 56) { /* 56 == DES level, good for Kerberos */
|
||||
rfbClientLog("negotiation SSF %d was not strong enough\n", ssf);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rfbClientLog("%s", "SASL authentication complete\n");
|
||||
|
||||
uint32_t result;
|
||||
if (!ReadFromRFBServer(client, (char *)&result, 4)) {
|
||||
rfbClientLog("Failed to read authentication result\n");
|
||||
goto error;
|
||||
}
|
||||
result = rfbClientSwap32IfLE(result);
|
||||
|
||||
if (result != 0) {
|
||||
rfbClientLog("Authentication failure\n");
|
||||
goto error;
|
||||
}
|
||||
rfbClientLog("Authentication successful - switching to SSF\n");
|
||||
|
||||
/* This must come *after* check-auth-result, because the former
|
||||
* is defined to be sent unencrypted, and setting saslconn turns
|
||||
* on the SSF layer encryption processing */
|
||||
client->saslconn = saslconn;
|
||||
|
||||
/* Clear SASL secret from memory if set - it'll be free'd on dispose */
|
||||
if (client->saslSecret) {
|
||||
size_t i;
|
||||
for (i = 0; i < client->saslSecret->len; i++)
|
||||
client->saslSecret->data[i] = '\0';
|
||||
client->saslSecret->len = 0;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
if (client->saslSecret) {
|
||||
size_t i;
|
||||
for (i = 0; i < client->saslSecret->len; i++)
|
||||
client->saslSecret->data[i] = '\0';
|
||||
client->saslSecret->len = 0;
|
||||
}
|
||||
|
||||
if (saslconn)
|
||||
sasl_dispose(&saslconn);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int
|
||||
ReadFromSASL(rfbClient* client, char *out, unsigned int n)
|
||||
{
|
||||
size_t want;
|
||||
|
||||
if (client->saslDecoded == NULL) {
|
||||
char *encoded;
|
||||
int encodedLen;
|
||||
int err, ret;
|
||||
|
||||
encodedLen = 8192;
|
||||
encoded = (char *)malloc(encodedLen);
|
||||
if (!encoded) {
|
||||
errno = EIO;
|
||||
return -EIO;
|
||||
}
|
||||
ret = read(client->sock, encoded, encodedLen);
|
||||
if (ret < 0) {
|
||||
free(encoded);
|
||||
return ret;
|
||||
}
|
||||
if (ret == 0) {
|
||||
free(encoded);
|
||||
errno = EIO;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
err = sasl_decode(client->saslconn, encoded, ret,
|
||||
&client->saslDecoded, &client->saslDecodedLength);
|
||||
free(encoded);
|
||||
if (err != SASL_OK) {
|
||||
rfbClientLog("Failed to decode SASL data %s\n",
|
||||
sasl_errstring(err, NULL, NULL));
|
||||
return -EINVAL;
|
||||
}
|
||||
client->saslDecodedOffset = 0;
|
||||
}
|
||||
|
||||
want = client->saslDecodedLength - client->saslDecodedOffset;
|
||||
if (want > n)
|
||||
want = n;
|
||||
|
||||
memcpy(out,
|
||||
client->saslDecoded + client->saslDecodedOffset,
|
||||
want);
|
||||
client->saslDecodedOffset += want;
|
||||
if (client->saslDecodedOffset == client->saslDecodedLength) {
|
||||
client->saslDecodedLength = client->saslDecodedOffset = 0;
|
||||
client->saslDecoded = NULL;
|
||||
}
|
||||
|
||||
if (!want) {
|
||||
errno = EAGAIN;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
return want;
|
||||
}
|
|
@ -0,0 +1,868 @@
|
|||
/*
|
||||
* Copyright (C) 2011-2012 Christian Beier <dontmind@freeshell.org>
|
||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* sockets.c - functions to deal with sockets.
|
||||
*/
|
||||
|
||||
#ifdef __STRICT_ANSI__
|
||||
#define _BSD_SOURCE
|
||||
#ifdef __linux__
|
||||
/* Setting this on other systems hides definitions such as INADDR_LOOPBACK.
|
||||
* The check should be for __GLIBC__ in fact. */
|
||||
# define _POSIX_SOURCE
|
||||
#endif
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <rfb/rfbclient.h>
|
||||
#include "sockets.h"
|
||||
#include "tls.h"
|
||||
#include "sasl.h"
|
||||
|
||||
void PrintInHex(char *buf, int len);
|
||||
|
||||
rfbBool errorMessageOnReadFailure = TRUE;
|
||||
|
||||
/*
|
||||
* ReadFromRFBServer is called whenever we want to read some data from the RFB
|
||||
* server. It is non-trivial for two reasons:
|
||||
*
|
||||
* 1. For efficiency it performs some intelligent buffering, avoiding invoking
|
||||
* the read() system call too often. For small chunks of data, it simply
|
||||
* copies the data out of an internal buffer. For large amounts of data it
|
||||
* reads directly into the buffer provided by the caller.
|
||||
*
|
||||
* 2. Whenever read() would block, it invokes the Xt event dispatching
|
||||
* mechanism to process X events. In fact, this is the only place these
|
||||
* events are processed, as there is no XtAppMainLoop in the program.
|
||||
*/
|
||||
|
||||
rfbBool
|
||||
ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
|
||||
{
|
||||
const int USECS_WAIT_PER_RETRY = 100000;
|
||||
int retries = 0;
|
||||
#undef DEBUG_READ_EXACT
|
||||
#ifdef DEBUG_READ_EXACT
|
||||
char* oout=out;
|
||||
unsigned int nn=n;
|
||||
rfbClientLog("ReadFromRFBServer %d bytes\n",n);
|
||||
#endif
|
||||
|
||||
/* Handle attempts to write to NULL out buffer that might occur
|
||||
when an outside malloc() fails. For instance, memcpy() to NULL
|
||||
results in undefined behaviour and probably memory corruption.*/
|
||||
if(!out)
|
||||
return FALSE;
|
||||
|
||||
if (client->serverPort==-1) {
|
||||
/* vncrec playing */
|
||||
rfbVNCRec* rec = client->vncRec;
|
||||
struct timeval tv;
|
||||
|
||||
if (rec->readTimestamp) {
|
||||
rec->readTimestamp = FALSE;
|
||||
if (!fread(&tv,sizeof(struct timeval),1,rec->file))
|
||||
return FALSE;
|
||||
|
||||
tv.tv_sec = rfbClientSwap32IfLE (tv.tv_sec);
|
||||
tv.tv_usec = rfbClientSwap32IfLE (tv.tv_usec);
|
||||
|
||||
if (rec->tv.tv_sec!=0 && !rec->doNotSleep) {
|
||||
struct timeval diff;
|
||||
diff.tv_sec = tv.tv_sec - rec->tv.tv_sec;
|
||||
diff.tv_usec = tv.tv_usec - rec->tv.tv_usec;
|
||||
if(diff.tv_usec<0) {
|
||||
diff.tv_sec--;
|
||||
diff.tv_usec+=1000000;
|
||||
}
|
||||
sleep (diff.tv_sec);
|
||||
usleep (diff.tv_usec);
|
||||
}
|
||||
|
||||
rec->tv=tv;
|
||||
}
|
||||
|
||||
return (fread(out,1,n,rec->file) != n ? FALSE : TRUE);
|
||||
}
|
||||
|
||||
if (n <= client->buffered) {
|
||||
memcpy(out, client->bufoutptr, n);
|
||||
client->bufoutptr += n;
|
||||
client->buffered -= n;
|
||||
#ifdef DEBUG_READ_EXACT
|
||||
goto hexdump;
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
memcpy(out, client->bufoutptr, client->buffered);
|
||||
|
||||
out += client->buffered;
|
||||
n -= client->buffered;
|
||||
|
||||
client->bufoutptr = client->buf;
|
||||
client->buffered = 0;
|
||||
|
||||
if (n <= RFB_BUF_SIZE) {
|
||||
|
||||
while (client->buffered < n) {
|
||||
int i;
|
||||
if (client->tlsSession)
|
||||
i = ReadFromTLS(client, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered);
|
||||
else
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
if (client->saslconn)
|
||||
i = ReadFromSASL(client, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered);
|
||||
else {
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
i = read(client->sock, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered);
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
}
|
||||
#endif
|
||||
|
||||
if (i <= 0) {
|
||||
if (i < 0) {
|
||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||
if (client->readTimeout > 0 &&
|
||||
++retries > (client->readTimeout * 1000 * 1000 / USECS_WAIT_PER_RETRY))
|
||||
{
|
||||
rfbClientLog("Connection timed out\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* TODO:
|
||||
ProcessXtEvents();
|
||||
*/
|
||||
WaitForMessage(client, USECS_WAIT_PER_RETRY);
|
||||
i = 0;
|
||||
} else {
|
||||
rfbClientErr("read (%d: %s)\n",errno,strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
if (errorMessageOnReadFailure) {
|
||||
rfbClientLog("VNC server closed connection\n");
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
client->buffered += i;
|
||||
}
|
||||
|
||||
memcpy(out, client->bufoutptr, n);
|
||||
client->bufoutptr += n;
|
||||
client->buffered -= n;
|
||||
|
||||
} else {
|
||||
|
||||
while (n > 0) {
|
||||
int i;
|
||||
if (client->tlsSession)
|
||||
i = ReadFromTLS(client, out, n);
|
||||
else
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
if (client->saslconn)
|
||||
i = ReadFromSASL(client, out, n);
|
||||
else
|
||||
#endif
|
||||
i = read(client->sock, out, n);
|
||||
|
||||
if (i <= 0) {
|
||||
if (i < 0) {
|
||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||
if (client->readTimeout > 0 &&
|
||||
++retries > (client->readTimeout * 1000 * 1000 / USECS_WAIT_PER_RETRY))
|
||||
{
|
||||
rfbClientLog("Connection timed out\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* TODO:
|
||||
ProcessXtEvents();
|
||||
*/
|
||||
WaitForMessage(client, USECS_WAIT_PER_RETRY);
|
||||
i = 0;
|
||||
} else {
|
||||
rfbClientErr("read (%s)\n",strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
if (errorMessageOnReadFailure) {
|
||||
rfbClientLog("VNC server closed connection\n");
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
out += i;
|
||||
n -= i;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_READ_EXACT
|
||||
hexdump:
|
||||
{ unsigned int ii;
|
||||
for(ii=0;ii<nn;ii++)
|
||||
fprintf(stderr,"%02x ",(unsigned char)oout[ii]);
|
||||
fprintf(stderr,"\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write an exact number of bytes, and don't return until you've sent them.
|
||||
*/
|
||||
|
||||
rfbBool
|
||||
WriteToRFBServer(rfbClient* client, const char *buf, unsigned int n)
|
||||
{
|
||||
fd_set fds;
|
||||
int i = 0;
|
||||
int j;
|
||||
const char *obuf = buf;
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
const char *output;
|
||||
unsigned int outputlen;
|
||||
int err;
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
|
||||
if (client->serverPort==-1)
|
||||
return TRUE; /* vncrec playing */
|
||||
|
||||
if (client->tlsSession) {
|
||||
/* WriteToTLS() will guarantee either everything is written, or error/eof returns */
|
||||
i = WriteToTLS(client, buf, n);
|
||||
if (i <= 0) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
if (client->saslconn) {
|
||||
err = sasl_encode(client->saslconn,
|
||||
buf, n,
|
||||
&output, &outputlen);
|
||||
if (err != SASL_OK) {
|
||||
rfbClientLog("Failed to encode SASL data %s",
|
||||
sasl_errstring(err, NULL, NULL));
|
||||
return FALSE;
|
||||
}
|
||||
obuf = output;
|
||||
n = outputlen;
|
||||
}
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
|
||||
while (i < n) {
|
||||
j = write(client->sock, obuf + i, (n - i));
|
||||
if (j <= 0) {
|
||||
if (j < 0) {
|
||||
if (errno == EWOULDBLOCK ||
|
||||
errno == EAGAIN) {
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(client->sock,&fds);
|
||||
|
||||
if (select(client->sock+1, NULL, &fds, NULL, NULL) <= 0) {
|
||||
rfbClientErr("select\n");
|
||||
return FALSE;
|
||||
}
|
||||
j = 0;
|
||||
} else {
|
||||
rfbClientErr("write\n");
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
rfbClientLog("write failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
i += j;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static rfbBool WaitForConnected(int socket, unsigned int secs)
|
||||
{
|
||||
fd_set writefds;
|
||||
fd_set exceptfds;
|
||||
struct timeval timeout;
|
||||
|
||||
timeout.tv_sec=secs;
|
||||
timeout.tv_usec=0;
|
||||
|
||||
FD_ZERO(&writefds);
|
||||
FD_SET(socket, &writefds);
|
||||
FD_ZERO(&exceptfds);
|
||||
FD_SET(socket, &exceptfds);
|
||||
if (select(socket+1, NULL, &writefds, &exceptfds, &timeout)==1) {
|
||||
int so_error;
|
||||
socklen_t len = sizeof so_error;
|
||||
getsockopt(socket, SOL_SOCKET, SO_ERROR, &so_error, &len);
|
||||
if (so_error!=0)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
rfbSocket
|
||||
ConnectClientToTcpAddr(unsigned int host, int port)
|
||||
{
|
||||
rfbSocket sock = ConnectClientToTcpAddrWithTimeout(host, port, DEFAULT_CONNECT_TIMEOUT);
|
||||
/* put socket back into blocking mode for compatibility reasons */
|
||||
if (sock != RFB_INVALID_SOCKET) {
|
||||
SetBlocking(sock);
|
||||
}
|
||||
return sock;
|
||||
}
|
||||
|
||||
rfbSocket
|
||||
ConnectClientToTcpAddrWithTimeout(unsigned int host, int port, unsigned int timeout)
|
||||
{
|
||||
rfbSocket sock;
|
||||
struct sockaddr_in addr;
|
||||
int one = 1;
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = host;
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == RFB_INVALID_SOCKET) {
|
||||
rfbClientErr("ConnectToTcpAddr: socket (%s)\n",strerror(errno));
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (!SetNonBlocking(sock))
|
||||
return FALSE;
|
||||
|
||||
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
if (!((errno == EWOULDBLOCK || errno == EINPROGRESS) && WaitForConnected(sock, timeout))) {
|
||||
rfbClientErr("ConnectToTcpAddr: connect\n");
|
||||
rfbCloseSocket(sock);
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
|
||||
(char *)&one, sizeof(one)) < 0) {
|
||||
rfbClientErr("ConnectToTcpAddr: setsockopt\n");
|
||||
rfbCloseSocket(sock);
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
rfbSocket
|
||||
ConnectClientToTcpAddr6(const char *hostname, int port)
|
||||
{
|
||||
rfbSocket sock = ConnectClientToTcpAddr6WithTimeout(hostname, port, DEFAULT_CONNECT_TIMEOUT);
|
||||
/* put socket back into blocking mode for compatibility reasons */
|
||||
if (sock != RFB_INVALID_SOCKET) {
|
||||
SetBlocking(sock);
|
||||
}
|
||||
return sock;
|
||||
}
|
||||
|
||||
rfbSocket
|
||||
ConnectClientToTcpAddr6WithTimeout(const char *hostname, int port, unsigned int timeout)
|
||||
{
|
||||
#ifdef LIBVNCSERVER_IPv6
|
||||
rfbSocket sock;
|
||||
int n;
|
||||
struct addrinfo hints, *res, *ressave;
|
||||
char port_s[10];
|
||||
int one = 1;
|
||||
|
||||
snprintf(port_s, 10, "%d", port);
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
if ((n = getaddrinfo(strcmp(hostname,"") == 0 ? "localhost": hostname, port_s, &hints, &res)))
|
||||
{
|
||||
rfbClientErr("ConnectClientToTcpAddr6: getaddrinfo (%s)\n", gai_strerror(n));
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
ressave = res;
|
||||
sock = RFB_INVALID_SOCKET;
|
||||
while (res)
|
||||
{
|
||||
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (sock != RFB_INVALID_SOCKET)
|
||||
{
|
||||
if (SetNonBlocking(sock)) {
|
||||
if (connect(sock, res->ai_addr, res->ai_addrlen) == 0) {
|
||||
break;
|
||||
} else {
|
||||
if ((errno == EWOULDBLOCK || errno == EINPROGRESS) && WaitForConnected(sock, timeout))
|
||||
break;
|
||||
rfbCloseSocket(sock);
|
||||
sock = RFB_INVALID_SOCKET;
|
||||
}
|
||||
} else {
|
||||
rfbCloseSocket(sock);
|
||||
sock = RFB_INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
res = res->ai_next;
|
||||
}
|
||||
freeaddrinfo(ressave);
|
||||
|
||||
if (sock == RFB_INVALID_SOCKET)
|
||||
{
|
||||
rfbClientErr("ConnectClientToTcpAddr6: connect\n");
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
|
||||
(char *)&one, sizeof(one)) < 0) {
|
||||
rfbClientErr("ConnectToTcpAddr: setsockopt\n");
|
||||
rfbCloseSocket(sock);
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
return sock;
|
||||
|
||||
#else
|
||||
|
||||
rfbClientErr("ConnectClientToTcpAddr6: IPv6 disabled\n");
|
||||
return RFB_INVALID_SOCKET;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
rfbSocket
|
||||
ConnectClientToUnixSock(const char *sockFile)
|
||||
{
|
||||
rfbSocket sock = ConnectClientToUnixSockWithTimeout(sockFile, DEFAULT_CONNECT_TIMEOUT);
|
||||
/* put socket back into blocking mode for compatibility reasons */
|
||||
if (sock != RFB_INVALID_SOCKET) {
|
||||
SetBlocking(sock);
|
||||
}
|
||||
return sock;
|
||||
}
|
||||
|
||||
rfbSocket
|
||||
ConnectClientToUnixSockWithTimeout(const char *sockFile, unsigned int timeout)
|
||||
{
|
||||
rfbSocket sock;
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
if(strlen(sockFile) + 1 > sizeof(addr.sun_path)) {
|
||||
rfbClientErr("ConnectToUnixSock: socket file name too long\n");
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
strcpy(addr.sun_path, sockFile);
|
||||
|
||||
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock == RFB_INVALID_SOCKET) {
|
||||
rfbClientErr("ConnectToUnixSock: socket (%s)\n",strerror(errno));
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (!SetNonBlocking(sock))
|
||||
return RFB_INVALID_SOCKET;
|
||||
|
||||
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr.sun_family) + strlen(addr.sun_path)) < 0 &&
|
||||
!(errno == EINPROGRESS && WaitForConnected(sock, timeout))) {
|
||||
rfbClientErr("ConnectToUnixSock: connect\n");
|
||||
rfbCloseSocket(sock);
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* FindFreeTcpPort tries to find unused TCP port in the range
|
||||
* (TUNNEL_PORT_OFFSET, TUNNEL_PORT_OFFSET + 99]. Returns 0 on failure.
|
||||
*/
|
||||
|
||||
int
|
||||
FindFreeTcpPort(void)
|
||||
{
|
||||
rfbSocket sock;
|
||||
int port;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == RFB_INVALID_SOCKET) {
|
||||
rfbClientErr(": FindFreeTcpPort: socket\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--) {
|
||||
addr.sin_port = htons((unsigned short)port);
|
||||
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
|
||||
rfbCloseSocket(sock);
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
rfbCloseSocket(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ListenAtTcpPort starts listening at the given TCP port.
|
||||
*/
|
||||
|
||||
rfbSocket
|
||||
ListenAtTcpPort(int port)
|
||||
{
|
||||
return ListenAtTcpPortAndAddress(port, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ListenAtTcpPortAndAddress starts listening at the given TCP port on
|
||||
* the given IP address
|
||||
*/
|
||||
|
||||
rfbSocket
|
||||
ListenAtTcpPortAndAddress(int port, const char *address)
|
||||
{
|
||||
rfbSocket sock = RFB_INVALID_SOCKET;
|
||||
int one = 1;
|
||||
#ifndef LIBVNCSERVER_IPv6
|
||||
struct sockaddr_in addr;
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
if (address) {
|
||||
addr.sin_addr.s_addr = inet_addr(address);
|
||||
} else {
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == RFB_INVALID_SOCKET) {
|
||||
rfbClientErr("ListenAtTcpPort: socket\n");
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
|
||||
(const char *)&one, sizeof(one)) < 0) {
|
||||
rfbClientErr("ListenAtTcpPort: setsockopt\n");
|
||||
rfbCloseSocket(sock);
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
rfbClientErr("ListenAtTcpPort: bind\n");
|
||||
rfbCloseSocket(sock);
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
#else
|
||||
int rv;
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
char port_str[8];
|
||||
|
||||
snprintf(port_str, 8, "%d", port);
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE; /* fill in wildcard address if address == NULL */
|
||||
|
||||
if ((rv = getaddrinfo(address, port_str, &hints, &servinfo)) != 0) {
|
||||
rfbClientErr("ListenAtTcpPortAndAddress: error in getaddrinfo: %s\n", gai_strerror(rv));
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/* loop through all the results and bind to the first we can */
|
||||
for(p = servinfo; p != NULL; p = p->ai_next) {
|
||||
if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == RFB_INVALID_SOCKET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef IPV6_V6ONLY
|
||||
/* we have separate IPv4 and IPv6 sockets since some OS's do not support dual binding */
|
||||
if (p->ai_family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&one, sizeof(one)) < 0) {
|
||||
rfbClientErr("ListenAtTcpPortAndAddress: error in setsockopt IPV6_V6ONLY: %s\n", strerror(errno));
|
||||
rfbCloseSocket(sock);
|
||||
freeaddrinfo(servinfo);
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) {
|
||||
rfbClientErr("ListenAtTcpPortAndAddress: error in setsockopt SO_REUSEADDR: %s\n", strerror(errno));
|
||||
rfbCloseSocket(sock);
|
||||
freeaddrinfo(servinfo);
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (bind(sock, p->ai_addr, p->ai_addrlen) < 0) {
|
||||
rfbCloseSocket(sock);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (p == NULL) {
|
||||
rfbClientErr("ListenAtTcpPortAndAddress: error in bind: %s\n", strerror(errno));
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/* all done with this structure now */
|
||||
freeaddrinfo(servinfo);
|
||||
#endif
|
||||
|
||||
if (listen(sock, 5) < 0) {
|
||||
rfbClientErr("ListenAtTcpPort: listen\n");
|
||||
rfbCloseSocket(sock);
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* AcceptTcpConnection accepts a TCP connection.
|
||||
*/
|
||||
|
||||
rfbSocket
|
||||
AcceptTcpConnection(rfbSocket listenSock)
|
||||
{
|
||||
rfbSocket sock;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
int one = 1;
|
||||
|
||||
sock = accept(listenSock, (struct sockaddr *) &addr, &addrlen);
|
||||
if (sock == RFB_INVALID_SOCKET) {
|
||||
rfbClientErr("AcceptTcpConnection: accept\n");
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
|
||||
(char *)&one, sizeof(one)) < 0) {
|
||||
rfbClientErr("AcceptTcpConnection: setsockopt\n");
|
||||
rfbCloseSocket(sock);
|
||||
return RFB_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SetNonBlocking sets a socket into non-blocking mode.
|
||||
*/
|
||||
|
||||
rfbBool
|
||||
SetNonBlocking(rfbSocket sock)
|
||||
{
|
||||
int flags = fcntl(sock, F_GETFL);
|
||||
if(flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
|
||||
rfbClientErr("Setting socket to non-blocking failed: %s\n",strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
rfbBool SetBlocking(rfbSocket sock)
|
||||
{
|
||||
int flags = fcntl(sock, F_GETFL);
|
||||
if(flags < 0 || fcntl(sock, F_SETFL, flags & ~O_NONBLOCK) < 0) {
|
||||
rfbClientErr("Setting socket to blocking failed: %s\n",strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SetDSCP sets a socket's IP QoS parameters aka Differentiated Services Code Point field
|
||||
*/
|
||||
|
||||
rfbBool
|
||||
SetDSCP(rfbSocket sock, int dscp)
|
||||
{
|
||||
int level, cmd;
|
||||
struct sockaddr addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
|
||||
if(getsockname(sock, &addr, &addrlen) != 0) {
|
||||
rfbClientErr("Setting socket QoS failed while getting socket address: %s\n",strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch(addr.sa_family)
|
||||
{
|
||||
#if defined LIBVNCSERVER_IPv6 && defined IPV6_TCLASS
|
||||
case AF_INET6:
|
||||
level = IPPROTO_IPV6;
|
||||
cmd = IPV6_TCLASS;
|
||||
break;
|
||||
#endif
|
||||
case AF_INET:
|
||||
level = IPPROTO_IP;
|
||||
cmd = IP_TOS;
|
||||
break;
|
||||
default:
|
||||
rfbClientErr("Setting socket QoS failed: Not bound to IP address");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(setsockopt(sock, level, cmd, (void*)&dscp, sizeof(dscp)) != 0) {
|
||||
rfbClientErr("Setting socket QoS failed: %s\n", strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* StringToIPAddr - convert a host string to an IP address.
|
||||
*/
|
||||
|
||||
rfbBool
|
||||
StringToIPAddr(const char *str, unsigned int *addr)
|
||||
{
|
||||
struct hostent *hp;
|
||||
|
||||
if (strcmp(str,"") == 0) {
|
||||
*addr = htonl(INADDR_LOOPBACK); /* local */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
*addr = inet_addr(str);
|
||||
|
||||
if (*addr != -1)
|
||||
return TRUE;
|
||||
|
||||
hp = gethostbyname(str);
|
||||
|
||||
if (hp) {
|
||||
*addr = *(unsigned int *)hp->h_addr;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Test if the other end of a socket is on the same machine.
|
||||
*/
|
||||
|
||||
rfbBool
|
||||
SameMachine(rfbSocket sock)
|
||||
{
|
||||
struct sockaddr_in peeraddr, myaddr;
|
||||
socklen_t addrlen = sizeof(struct sockaddr_in);
|
||||
|
||||
getpeername(sock, (struct sockaddr *)&peeraddr, &addrlen);
|
||||
getsockname(sock, (struct sockaddr *)&myaddr, &addrlen);
|
||||
|
||||
return (peeraddr.sin_addr.s_addr == myaddr.sin_addr.s_addr);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Print out the contents of a packet for debugging.
|
||||
*/
|
||||
|
||||
void
|
||||
PrintInHex(char *buf, int len)
|
||||
{
|
||||
int i, j;
|
||||
char c, str[17];
|
||||
|
||||
str[16] = 0;
|
||||
|
||||
rfbClientLog("ReadExact: ");
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if ((i % 16 == 0) && (i != 0)) {
|
||||
rfbClientLog(" ");
|
||||
}
|
||||
c = buf[i];
|
||||
str[i % 16] = (((c > 31) && (c < 127)) ? c : '.');
|
||||
rfbClientLog("%02x ",(unsigned char)c);
|
||||
if ((i % 4) == 3)
|
||||
rfbClientLog(" ");
|
||||
if ((i % 16) == 15)
|
||||
{
|
||||
rfbClientLog("%s\n",str);
|
||||
}
|
||||
}
|
||||
if ((i % 16) != 0)
|
||||
{
|
||||
for (j = i % 16; j < 16; j++)
|
||||
{
|
||||
rfbClientLog(" ");
|
||||
if ((j % 4) == 3) rfbClientLog(" ");
|
||||
}
|
||||
str[i % 16] = 0;
|
||||
rfbClientLog("%s\n",str);
|
||||
}
|
||||
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
int WaitForMessage(rfbClient* client,unsigned int usecs)
|
||||
{
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
int num;
|
||||
|
||||
if (client->serverPort==-1)
|
||||
/* playing back vncrec file */
|
||||
return 1;
|
||||
|
||||
timeout.tv_sec=(usecs/1000000);
|
||||
timeout.tv_usec=(usecs%1000000);
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(client->sock,&fds);
|
||||
|
||||
num=select(client->sock+1, &fds, NULL, NULL, &timeout);
|
||||
if(num<0) {
|
||||
rfbClientLog("Waiting for message failed: %d (%s)\n",errno,strerror(errno));
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,583 @@
|
|||
/*
|
||||
* Copyright (C) 2009 Vic Lee.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/x509.h>
|
||||
#include <rfb/rfbclient.h>
|
||||
#include <errno.h>
|
||||
#include "tls.h"
|
||||
|
||||
|
||||
static const char *rfbTLSPriority = "NORMAL:+DHE-DSS:+RSA:+DHE-RSA:+SRP";
|
||||
static const char *rfbAnonTLSPriority= "NORMAL:+ANON-DH";
|
||||
|
||||
#define DH_BITS 1024
|
||||
static gnutls_dh_params_t rfbDHParams;
|
||||
|
||||
static rfbBool rfbTLSInitialized = FALSE;
|
||||
|
||||
static int
|
||||
verify_certificate_callback (gnutls_session_t session)
|
||||
{
|
||||
unsigned int status;
|
||||
const gnutls_datum_t *cert_list;
|
||||
unsigned int cert_list_size;
|
||||
int ret;
|
||||
gnutls_x509_crt_t cert;
|
||||
rfbClient *sptr;
|
||||
char *hostname;
|
||||
|
||||
sptr = (rfbClient *)gnutls_session_get_ptr(session);
|
||||
if (!sptr) {
|
||||
rfbClientLog("Failed to validate certificate - missing client data\n");
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
hostname = sptr->serverHost;
|
||||
if (!hostname) {
|
||||
rfbClientLog("No server hostname found for client\n");
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
/* This verification function uses the trusted CAs in the credentials
|
||||
* structure. So you must have installed one or more CA certificates.
|
||||
*/
|
||||
ret = gnutls_certificate_verify_peers2 (session, &status);
|
||||
if (ret < 0)
|
||||
{
|
||||
rfbClientLog ("Certificate validation call failed\n");
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
if (status & GNUTLS_CERT_INVALID)
|
||||
rfbClientLog("The certificate is not trusted.\n");
|
||||
|
||||
if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
|
||||
rfbClientLog("The certificate hasn't got a known issuer.\n");
|
||||
|
||||
if (status & GNUTLS_CERT_REVOKED)
|
||||
rfbClientLog("The certificate has been revoked.\n");
|
||||
|
||||
if (status & GNUTLS_CERT_EXPIRED)
|
||||
rfbClientLog("The certificate has expired\n");
|
||||
|
||||
if (status & GNUTLS_CERT_NOT_ACTIVATED)
|
||||
rfbClientLog("The certificate is not yet activated\n");
|
||||
|
||||
if (status)
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
|
||||
/* Up to here the process is the same for X.509 certificates and
|
||||
* OpenPGP keys. From now on X.509 certificates are assumed. This can
|
||||
* be easily extended to work with openpgp keys as well.
|
||||
*/
|
||||
if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) {
|
||||
rfbClientLog("The certificate was not X509\n");
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
if (gnutls_x509_crt_init (&cert) < 0)
|
||||
{
|
||||
rfbClientLog("Error initialising certificate structure\n");
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
|
||||
if (cert_list == NULL)
|
||||
{
|
||||
rfbClientLog("No certificate was found!\n");
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
|
||||
{
|
||||
rfbClientLog("Error parsing certificate\n");
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
if (!gnutls_x509_crt_check_hostname (cert, hostname))
|
||||
{
|
||||
rfbClientLog("The certificate's owner does not match hostname '%s'\n",
|
||||
hostname);
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
gnutls_x509_crt_deinit (cert);
|
||||
|
||||
/* notify gnutls to continue handshake normally */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static rfbBool
|
||||
InitializeTLS(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (rfbTLSInitialized) return TRUE;
|
||||
if ((ret = gnutls_global_init()) < 0 ||
|
||||
(ret = gnutls_dh_params_init(&rfbDHParams)) < 0 ||
|
||||
(ret = gnutls_dh_params_generate2(rfbDHParams, DH_BITS)) < 0)
|
||||
{
|
||||
rfbClientLog("Failed to initialized GnuTLS: %s.\n", gnutls_strerror(ret));
|
||||
return FALSE;
|
||||
}
|
||||
rfbClientLog("GnuTLS version %s initialized.\n", gnutls_check_version(NULL));
|
||||
rfbTLSInitialized = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
PushTLS(gnutls_transport_ptr_t transport, const void *data, size_t len)
|
||||
{
|
||||
rfbClient *client = (rfbClient*)transport;
|
||||
int ret;
|
||||
|
||||
while (1)
|
||||
{
|
||||
ret = write(client->sock, data, len);
|
||||
if (ret < 0)
|
||||
{
|
||||
if (errno == EINTR) continue;
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
PullTLS(gnutls_transport_ptr_t transport, void *data, size_t len)
|
||||
{
|
||||
rfbClient *client = (rfbClient*)transport;
|
||||
int ret;
|
||||
|
||||
while (1)
|
||||
{
|
||||
ret = read(client->sock, data, len);
|
||||
if (ret < 0)
|
||||
{
|
||||
if (errno == EINTR) continue;
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static rfbBool
|
||||
InitializeTLSSession(rfbClient* client, rfbBool anonTLS)
|
||||
{
|
||||
int ret;
|
||||
const char *p;
|
||||
|
||||
if (client->tlsSession) return TRUE;
|
||||
|
||||
if ((ret = gnutls_init((gnutls_session_t*)&client->tlsSession, GNUTLS_CLIENT)) < 0)
|
||||
{
|
||||
rfbClientLog("Failed to initialized TLS session: %s.\n", gnutls_strerror(ret));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((ret = gnutls_priority_set_direct((gnutls_session_t)client->tlsSession,
|
||||
anonTLS ? rfbAnonTLSPriority : rfbTLSPriority, &p)) < 0)
|
||||
{
|
||||
rfbClientLog("Warning: Failed to set TLS priority: %s (%s).\n", gnutls_strerror(ret), p);
|
||||
}
|
||||
|
||||
gnutls_transport_set_ptr((gnutls_session_t)client->tlsSession, (gnutls_transport_ptr_t)client);
|
||||
gnutls_transport_set_push_function((gnutls_session_t)client->tlsSession, PushTLS);
|
||||
gnutls_transport_set_pull_function((gnutls_session_t)client->tlsSession, PullTLS);
|
||||
|
||||
INIT_MUTEX(client->tlsRwMutex);
|
||||
|
||||
rfbClientLog("TLS session initialized.\n");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static rfbBool
|
||||
SetTLSAnonCredential(rfbClient* client)
|
||||
{
|
||||
gnutls_anon_client_credentials_t anonCred;
|
||||
int ret;
|
||||
|
||||
if ((ret = gnutls_anon_allocate_client_credentials(&anonCred)) < 0 ||
|
||||
(ret = gnutls_credentials_set((gnutls_session_t)client->tlsSession, GNUTLS_CRD_ANON, anonCred)) < 0)
|
||||
{
|
||||
FreeTLS(client);
|
||||
rfbClientLog("Failed to create anonymous credentials: %s", gnutls_strerror(ret));
|
||||
return FALSE;
|
||||
}
|
||||
rfbClientLog("TLS anonymous credential created.\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static rfbBool
|
||||
HandshakeTLS(rfbClient* client)
|
||||
{
|
||||
int timeout = 15;
|
||||
int ret;
|
||||
|
||||
while (timeout > 0 && (ret = gnutls_handshake((gnutls_session_t)client->tlsSession)) < 0)
|
||||
{
|
||||
if (!gnutls_error_is_fatal(ret))
|
||||
{
|
||||
rfbClientLog("TLS handshake blocking.\n");
|
||||
sleep(1);
|
||||
timeout--;
|
||||
continue;
|
||||
}
|
||||
rfbClientLog("TLS handshake failed: %s.\n", gnutls_strerror(ret));
|
||||
|
||||
FreeTLS(client);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (timeout <= 0)
|
||||
{
|
||||
rfbClientLog("TLS handshake timeout.\n");
|
||||
FreeTLS(client);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rfbClientLog("TLS handshake done.\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* VeNCrypt sub auth. 1 byte auth count, followed by count * 4 byte integers */
|
||||
static rfbBool
|
||||
ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result)
|
||||
{
|
||||
uint8_t count=0;
|
||||
uint8_t loop=0;
|
||||
uint8_t flag=0;
|
||||
uint32_t tAuth[256], t;
|
||||
char buf1[500],buf2[10];
|
||||
uint32_t authScheme;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&count, 1)) return FALSE;
|
||||
|
||||
if (count==0)
|
||||
{
|
||||
rfbClientLog("List of security types is ZERO. Giving up.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rfbClientLog("We have %d security types to read\n", count);
|
||||
authScheme=0;
|
||||
/* now, we have a list of available security types to read ( uint8_t[] ) */
|
||||
for (loop=0;loop<count;loop++)
|
||||
{
|
||||
if (!ReadFromRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
|
||||
t=rfbClientSwap32IfLE(tAuth[loop]);
|
||||
rfbClientLog("%d) Received security type %d\n", loop, t);
|
||||
if (flag) continue;
|
||||
if (t==rfbVeNCryptTLSNone ||
|
||||
t==rfbVeNCryptTLSVNC ||
|
||||
t==rfbVeNCryptTLSPlain ||
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
t==rfbVeNCryptTLSSASL ||
|
||||
t==rfbVeNCryptX509SASL ||
|
||||
#endif /*LIBVNCSERVER_HAVE_SASL */
|
||||
t==rfbVeNCryptX509None ||
|
||||
t==rfbVeNCryptX509VNC ||
|
||||
t==rfbVeNCryptX509Plain)
|
||||
{
|
||||
flag++;
|
||||
authScheme=t;
|
||||
rfbClientLog("Selecting security type %d (%d/%d in the list)\n", authScheme, loop, count);
|
||||
/* send back 4 bytes (in original byte order!) indicating which security type to use */
|
||||
if (!WriteToRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
|
||||
}
|
||||
tAuth[loop]=t;
|
||||
}
|
||||
if (authScheme==0)
|
||||
{
|
||||
memset(buf1, 0, sizeof(buf1));
|
||||
for (loop=0;loop<count;loop++)
|
||||
{
|
||||
if (strlen(buf1)>=sizeof(buf1)-1) break;
|
||||
snprintf(buf2, sizeof(buf2), (loop>0 ? ", %d" : "%d"), (int)tAuth[loop]);
|
||||
strncat(buf1, buf2, sizeof(buf1)-strlen(buf1)-1);
|
||||
}
|
||||
rfbClientLog("Unknown VeNCrypt authentication scheme from VNC server: %s\n",
|
||||
buf1);
|
||||
return FALSE;
|
||||
}
|
||||
*result = authScheme;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
FreeX509Credential(rfbCredential *cred)
|
||||
{
|
||||
if (cred->x509Credential.x509CACertFile) free(cred->x509Credential.x509CACertFile);
|
||||
if (cred->x509Credential.x509CACrlFile) free(cred->x509Credential.x509CACrlFile);
|
||||
if (cred->x509Credential.x509ClientCertFile) free(cred->x509Credential.x509ClientCertFile);
|
||||
if (cred->x509Credential.x509ClientKeyFile) free(cred->x509Credential.x509ClientKeyFile);
|
||||
free(cred);
|
||||
}
|
||||
|
||||
static gnutls_certificate_credentials_t
|
||||
CreateX509CertCredential(rfbCredential *cred)
|
||||
{
|
||||
gnutls_certificate_credentials_t x509_cred;
|
||||
int ret;
|
||||
|
||||
if (!cred->x509Credential.x509CACertFile)
|
||||
{
|
||||
rfbClientLog("No CA certificate provided.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0)
|
||||
{
|
||||
rfbClientLog("Cannot allocate credentials: %s.\n", gnutls_strerror(ret));
|
||||
return NULL;
|
||||
}
|
||||
if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
|
||||
cred->x509Credential.x509CACertFile, GNUTLS_X509_FMT_PEM)) < 0)
|
||||
{
|
||||
rfbClientLog("Cannot load CA credentials: %s.\n", gnutls_strerror(ret));
|
||||
gnutls_certificate_free_credentials (x509_cred);
|
||||
return NULL;
|
||||
}
|
||||
if (cred->x509Credential.x509ClientCertFile && cred->x509Credential.x509ClientKeyFile)
|
||||
{
|
||||
if ((ret = gnutls_certificate_set_x509_key_file(x509_cred,
|
||||
cred->x509Credential.x509ClientCertFile, cred->x509Credential.x509ClientKeyFile,
|
||||
GNUTLS_X509_FMT_PEM)) < 0)
|
||||
{
|
||||
rfbClientLog("Cannot load client certificate or key: %s.\n", gnutls_strerror(ret));
|
||||
gnutls_certificate_free_credentials (x509_cred);
|
||||
return NULL;
|
||||
}
|
||||
} else
|
||||
{
|
||||
rfbClientLog("No client certificate or key provided.\n");
|
||||
}
|
||||
if (cred->x509Credential.x509CACrlFile)
|
||||
{
|
||||
if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
|
||||
cred->x509Credential.x509CACrlFile, GNUTLS_X509_FMT_PEM)) < 0)
|
||||
{
|
||||
rfbClientLog("Cannot load CRL: %s.\n", gnutls_strerror(ret));
|
||||
gnutls_certificate_free_credentials (x509_cred);
|
||||
return NULL;
|
||||
}
|
||||
} else
|
||||
{
|
||||
rfbClientLog("No CRL provided.\n");
|
||||
}
|
||||
gnutls_certificate_set_dh_params (x509_cred, rfbDHParams);
|
||||
return x509_cred;
|
||||
}
|
||||
|
||||
|
||||
rfbBool
|
||||
HandleAnonTLSAuth(rfbClient* client)
|
||||
{
|
||||
if (!InitializeTLS() || !InitializeTLSSession(client, TRUE)) return FALSE;
|
||||
|
||||
if (!SetTLSAnonCredential(client)) return FALSE;
|
||||
|
||||
if (!HandshakeTLS(client)) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
rfbBool
|
||||
HandleVeNCryptAuth(rfbClient* client)
|
||||
{
|
||||
uint8_t major, minor, status;
|
||||
uint32_t authScheme;
|
||||
rfbBool anonTLS;
|
||||
gnutls_certificate_credentials_t x509_cred = NULL;
|
||||
int ret;
|
||||
|
||||
if (!InitializeTLS()) return FALSE;
|
||||
|
||||
/* Read VeNCrypt version */
|
||||
if (!ReadFromRFBServer(client, (char *)&major, 1) ||
|
||||
!ReadFromRFBServer(client, (char *)&minor, 1))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
rfbClientLog("Got VeNCrypt version %d.%d from server.\n", (int)major, (int)minor);
|
||||
|
||||
if (major != 0 && minor != 2)
|
||||
{
|
||||
rfbClientLog("Unsupported VeNCrypt version.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!WriteToRFBServer(client, (char *)&major, 1) ||
|
||||
!WriteToRFBServer(client, (char *)&minor, 1) ||
|
||||
!ReadFromRFBServer(client, (char *)&status, 1))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
rfbClientLog("Server refused VeNCrypt version %d.%d.\n", (int)major, (int)minor);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ReadVeNCryptSecurityType(client, &authScheme)) return FALSE;
|
||||
if (!ReadFromRFBServer(client, (char *)&status, 1) || status != 1)
|
||||
{
|
||||
rfbClientLog("Server refused VeNCrypt authentication %d (%d).\n", authScheme, (int)status);
|
||||
return FALSE;
|
||||
}
|
||||
client->subAuthScheme = authScheme;
|
||||
|
||||
/* Some VeNCrypt security types are anonymous TLS, others are X509 */
|
||||
switch (authScheme)
|
||||
{
|
||||
case rfbVeNCryptTLSNone:
|
||||
case rfbVeNCryptTLSVNC:
|
||||
case rfbVeNCryptTLSPlain:
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
case rfbVeNCryptTLSSASL:
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
anonTLS = TRUE;
|
||||
break;
|
||||
default:
|
||||
anonTLS = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get X509 Credentials if it's not anonymous */
|
||||
if (!anonTLS)
|
||||
{
|
||||
rfbCredential *cred;
|
||||
|
||||
if (!client->GetCredential)
|
||||
{
|
||||
rfbClientLog("GetCredential callback is not set.\n");
|
||||
return FALSE;
|
||||
}
|
||||
cred = client->GetCredential(client, rfbCredentialTypeX509);
|
||||
if (!cred)
|
||||
{
|
||||
rfbClientLog("Reading credential failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
x509_cred = CreateX509CertCredential(cred);
|
||||
FreeX509Credential(cred);
|
||||
if (!x509_cred) return FALSE;
|
||||
}
|
||||
|
||||
/* Start up the TLS session */
|
||||
if (!InitializeTLSSession(client, anonTLS)) return FALSE;
|
||||
|
||||
if (anonTLS)
|
||||
{
|
||||
if (!SetTLSAnonCredential(client)) return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the certificate verification callback. */
|
||||
gnutls_certificate_set_verify_function (x509_cred, verify_certificate_callback);
|
||||
gnutls_session_set_ptr ((gnutls_session_t)client->tlsSession, (void *)client);
|
||||
|
||||
if ((ret = gnutls_credentials_set((gnutls_session_t)client->tlsSession, GNUTLS_CRD_CERTIFICATE, x509_cred)) < 0)
|
||||
{
|
||||
rfbClientLog("Cannot set x509 credential: %s.\n", gnutls_strerror(ret));
|
||||
FreeTLS(client);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!HandshakeTLS(client)) return FALSE;
|
||||
|
||||
/* We are done here. The caller should continue with client->subAuthScheme
|
||||
* to do actual sub authentication.
|
||||
*/
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ReadFromTLS(rfbClient* client, char *out, unsigned int n)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
LOCK(client->tlsRwMutex);
|
||||
ret = gnutls_record_recv((gnutls_session_t)client->tlsSession, out, n);
|
||||
UNLOCK(client->tlsRwMutex);
|
||||
|
||||
if (ret >= 0) return ret;
|
||||
if (ret == GNUTLS_E_REHANDSHAKE || ret == GNUTLS_E_AGAIN)
|
||||
{
|
||||
errno = EAGAIN;
|
||||
} else
|
||||
{
|
||||
rfbClientLog("Error reading from TLS: %s.\n", gnutls_strerror(ret));
|
||||
errno = EINTR;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
WriteToTLS(rfbClient* client, const char *buf, unsigned int n)
|
||||
{
|
||||
unsigned int offset = 0;
|
||||
ssize_t ret;
|
||||
|
||||
while (offset < n)
|
||||
{
|
||||
LOCK(client->tlsRwMutex);
|
||||
ret = gnutls_record_send((gnutls_session_t)client->tlsSession, buf+offset, (size_t)(n-offset));
|
||||
UNLOCK(client->tlsRwMutex);
|
||||
|
||||
if (ret == 0) continue;
|
||||
if (ret < 0)
|
||||
{
|
||||
if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) continue;
|
||||
rfbClientLog("Error writing to TLS: %s.\n", gnutls_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
offset += (unsigned int)ret;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void FreeTLS(rfbClient* client)
|
||||
{
|
||||
if (client->tlsSession)
|
||||
{
|
||||
gnutls_deinit((gnutls_session_t)client->tlsSession);
|
||||
client->tlsSession = NULL;
|
||||
TINI_MUTEX(client->tlsRwMutex);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
int
|
||||
GetTLSCipherBits(rfbClient* client)
|
||||
{
|
||||
gnutls_cipher_algorithm_t cipher = gnutls_cipher_get((gnutls_session_t)client->tlsSession);
|
||||
|
||||
return gnutls_cipher_get_key_size(cipher) * 8;
|
||||
}
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Christian Beier.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <rfb/rfbclient.h>
|
||||
#include <errno.h>
|
||||
#include "tls.h"
|
||||
|
||||
rfbBool HandleAnonTLSAuth(rfbClient* client)
|
||||
{
|
||||
rfbClientLog("TLS is not supported.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
rfbBool HandleVeNCryptAuth(rfbClient* client)
|
||||
{
|
||||
rfbClientLog("TLS is not supported.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
int ReadFromTLS(rfbClient* client, char *out, unsigned int n)
|
||||
{
|
||||
rfbClientLog("TLS is not supported.\n");
|
||||
errno = EINTR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int WriteToTLS(rfbClient* client, const char *buf, unsigned int n)
|
||||
{
|
||||
rfbClientLog("TLS is not supported.\n");
|
||||
errno = EINTR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void FreeTLS(rfbClient* client)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
int
|
||||
GetTLSCipherBits(rfbClient* client)
|
||||
{
|
||||
rfbClientLog("TLS is not supported.\n");
|
||||
return 0;
|
||||
}
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
|
|
@ -0,0 +1,685 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Philip Van Hoof <philip@codeminded.be>
|
||||
* Copyright (C) 2009 Vic Lee.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <rfb/rfbclient.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include "tls.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
static rfbBool rfbTLSInitialized = FALSE;
|
||||
|
||||
// Locking callbacks are only initialized if we have mutex support.
|
||||
#if defined(LIBVNCSERVER_HAVE_LIBPTHREAD)
|
||||
|
||||
static MUTEX(*mutex_buf) = NULL;
|
||||
|
||||
struct CRYPTO_dynlock_value {
|
||||
MUTEX(mutex);
|
||||
};
|
||||
|
||||
static void locking_function(int mode, int n, const char *file, int line)
|
||||
{
|
||||
if (mode & CRYPTO_LOCK)
|
||||
LOCK(mutex_buf[n]);
|
||||
else
|
||||
UNLOCK(mutex_buf[n]);
|
||||
}
|
||||
|
||||
static unsigned long id_function(void)
|
||||
{
|
||||
return ((unsigned long) CURRENT_THREAD_ID);
|
||||
}
|
||||
|
||||
static struct CRYPTO_dynlock_value *dyn_create_function(const char *file, int line)
|
||||
{
|
||||
struct CRYPTO_dynlock_value *value;
|
||||
|
||||
value = (struct CRYPTO_dynlock_value *)
|
||||
malloc(sizeof(struct CRYPTO_dynlock_value));
|
||||
if (!value)
|
||||
goto err;
|
||||
INIT_MUTEX(value->mutex);
|
||||
|
||||
return value;
|
||||
|
||||
err:
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static void dyn_lock_function (int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
|
||||
{
|
||||
if (mode & CRYPTO_LOCK)
|
||||
LOCK(l->mutex);
|
||||
else
|
||||
UNLOCK(l->mutex);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dyn_destroy_function(struct CRYPTO_dynlock_value *l, const char *file, int line)
|
||||
{
|
||||
TINI_MUTEX(l->mutex);
|
||||
free(l);
|
||||
}
|
||||
|
||||
static rfbBool InitLockingCb()
|
||||
{
|
||||
mutex_buf = malloc(CRYPTO_num_locks() * MUTEX_SIZE);
|
||||
if (mutex_buf == NULL) {
|
||||
rfbClientLog("Failed to initialized OpenSSL: memory.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < CRYPTO_num_locks(); i++)
|
||||
INIT_MUTEX(mutex_buf[i]);
|
||||
|
||||
CRYPTO_set_locking_callback(locking_function);
|
||||
CRYPTO_set_id_callback(id_function);
|
||||
CRYPTO_set_dynlock_create_callback(dyn_create_function);
|
||||
CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
|
||||
CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#else
|
||||
//If mutex support is not available, locking initialization is a no-op.
|
||||
static rfbBool InitLockingCb() { return TRUE; }
|
||||
#endif
|
||||
|
||||
static int
|
||||
ssl_error_to_errno (int ssl_error)
|
||||
{
|
||||
switch (ssl_error) {
|
||||
case SSL_ERROR_NONE:
|
||||
return 0;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
/* this one does not map well at all */
|
||||
//d(printf ("ssl_errno: SSL_ERROR_ZERO_RETURN\n"));
|
||||
return EINVAL;
|
||||
case SSL_ERROR_WANT_READ: /* non-fatal; retry */
|
||||
case SSL_ERROR_WANT_WRITE: /* non-fatal; retry */
|
||||
//d(printf ("ssl_errno: SSL_ERROR_WANT_[READ,WRITE]\n"));
|
||||
return EAGAIN;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
//d(printf ("ssl_errno: SSL_ERROR_SYSCALL\n"));
|
||||
return EINTR;
|
||||
case SSL_ERROR_SSL:
|
||||
//d(printf ("ssl_errno: SSL_ERROR_SSL <-- very useful error...riiiiight\n"));
|
||||
return EINTR;
|
||||
default:
|
||||
//d(printf ("ssl_errno: default error\n"));
|
||||
return EINTR;
|
||||
}
|
||||
}
|
||||
|
||||
static rfbBool
|
||||
InitializeTLS(void)
|
||||
{
|
||||
if (rfbTLSInitialized)
|
||||
return TRUE;
|
||||
|
||||
if (!InitLockingCb())
|
||||
return FALSE;
|
||||
|
||||
SSL_load_error_strings();
|
||||
SSLeay_add_ssl_algorithms();
|
||||
RAND_load_file("/dev/urandom", 1024);
|
||||
|
||||
rfbClientLog("OpenSSL version %s initialized.\n", SSLeay_version(SSLEAY_VERSION));
|
||||
rfbTLSInitialized = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int sock_read_ready(SSL *ssl, uint32_t ms)
|
||||
{
|
||||
int r = 0;
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
|
||||
FD_SET(SSL_get_fd(ssl), &fds);
|
||||
|
||||
tv.tv_sec = ms / 1000;
|
||||
tv.tv_usec = (ms % 1000) * 1000;
|
||||
|
||||
r = select (SSL_get_fd(ssl) + 1, &fds, NULL, NULL, &tv);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int wait_for_data(SSL *ssl, int ret, int timeout)
|
||||
{
|
||||
int err;
|
||||
int retval = 1;
|
||||
|
||||
err = SSL_get_error(ssl, ret);
|
||||
|
||||
switch(err)
|
||||
{
|
||||
case SSL_ERROR_WANT_READ:
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
ret = sock_read_ready(ssl, timeout*1000);
|
||||
|
||||
if (ret == -1) {
|
||||
retval = 2;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
retval = 3;
|
||||
long verify_res = SSL_get_verify_result(ssl);
|
||||
if (verify_res != X509_V_OK)
|
||||
rfbClientLog("Could not verify server certificate: %s.\n",
|
||||
X509_verify_cert_error_string(verify_res));
|
||||
break;
|
||||
}
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static rfbBool
|
||||
load_crls_from_file(char *file, SSL_CTX *ssl_ctx)
|
||||
{
|
||||
X509_STORE *st;
|
||||
int i;
|
||||
int count = 0;
|
||||
BIO *bio;
|
||||
STACK_OF(X509_INFO) *xis = NULL;
|
||||
X509_INFO *xi;
|
||||
|
||||
st = SSL_CTX_get_cert_store(ssl_ctx);
|
||||
|
||||
bio = BIO_new_file(file, "r");
|
||||
if (bio == NULL)
|
||||
return FALSE;
|
||||
|
||||
xis = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
|
||||
BIO_free(bio);
|
||||
|
||||
for (i = 0; i < sk_X509_INFO_num(xis); i++)
|
||||
{
|
||||
xi = sk_X509_INFO_value(xis, i);
|
||||
if (xi->crl)
|
||||
{
|
||||
X509_STORE_add_crl(st, xi->crl);
|
||||
xi->crl = NULL;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
sk_X509_INFO_pop_free(xis, X509_INFO_free);
|
||||
|
||||
if (count > 0)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static SSL *
|
||||
open_ssl_connection (rfbClient *client, int sockfd, rfbBool anonTLS, rfbCredential *cred)
|
||||
{
|
||||
SSL_CTX *ssl_ctx = NULL;
|
||||
SSL *ssl = NULL;
|
||||
int n, finished = 0;
|
||||
X509_VERIFY_PARAM *param;
|
||||
uint8_t verify_crls;
|
||||
|
||||
if (!(ssl_ctx = SSL_CTX_new(SSLv23_client_method())))
|
||||
{
|
||||
rfbClientLog("Could not create new SSL context.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
param = X509_VERIFY_PARAM_new();
|
||||
|
||||
/* Setup verification if not anonymous */
|
||||
if (!anonTLS)
|
||||
{
|
||||
verify_crls = cred->x509Credential.x509CrlVerifyMode;
|
||||
if (cred->x509Credential.x509CACertFile)
|
||||
{
|
||||
if (!SSL_CTX_load_verify_locations(ssl_ctx, cred->x509Credential.x509CACertFile, NULL))
|
||||
{
|
||||
rfbClientLog("Failed to load CA certificate from %s.\n",
|
||||
cred->x509Credential.x509CACertFile);
|
||||
goto error_free_ctx;
|
||||
}
|
||||
} else {
|
||||
rfbClientLog("Using default paths for certificate verification.\n");
|
||||
SSL_CTX_set_default_verify_paths (ssl_ctx);
|
||||
}
|
||||
|
||||
if (cred->x509Credential.x509CACrlFile)
|
||||
{
|
||||
if (!load_crls_from_file(cred->x509Credential.x509CACrlFile, ssl_ctx))
|
||||
{
|
||||
rfbClientLog("CRLs could not be loaded.\n");
|
||||
goto error_free_ctx;
|
||||
}
|
||||
if (verify_crls == rfbX509CrlVerifyNone) verify_crls = rfbX509CrlVerifyAll;
|
||||
}
|
||||
|
||||
if (cred->x509Credential.x509ClientCertFile && cred->x509Credential.x509ClientKeyFile)
|
||||
{
|
||||
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cred->x509Credential.x509ClientCertFile) != 1)
|
||||
{
|
||||
rfbClientLog("Client certificate could not be loaded.\n");
|
||||
goto error_free_ctx;
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, cred->x509Credential.x509ClientKeyFile,
|
||||
SSL_FILETYPE_PEM) != 1)
|
||||
{
|
||||
rfbClientLog("Client private key could not be loaded.\n");
|
||||
goto error_free_ctx;
|
||||
}
|
||||
|
||||
if (SSL_CTX_check_private_key(ssl_ctx) == 0) {
|
||||
rfbClientLog("Client certificate and private key do not match.\n");
|
||||
goto error_free_ctx;
|
||||
}
|
||||
}
|
||||
|
||||
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
|
||||
|
||||
if (verify_crls == rfbX509CrlVerifyClient)
|
||||
X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
|
||||
else if (verify_crls == rfbX509CrlVerifyAll)
|
||||
X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
|
||||
|
||||
if(!X509_VERIFY_PARAM_set1_host(param, client->serverHost, strlen(client->serverHost)))
|
||||
{
|
||||
rfbClientLog("Could not set server name for verification.\n");
|
||||
goto error_free_ctx;
|
||||
}
|
||||
SSL_CTX_set1_param(ssl_ctx, param);
|
||||
SSL_CTX_set_cipher_list(ssl_ctx, "ALL");
|
||||
} else { /* anonTLS here */
|
||||
/* Need anonymous ciphers for anonTLS, see https://github.com/LibVNC/libvncserver/issues/347#issuecomment-597477103 */
|
||||
SSL_CTX_set_cipher_list(ssl_ctx, "aNULL");
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined LIBRESSL_VERSION_NUMBER
|
||||
/*
|
||||
See https://www.openssl.org/docs/man1.1.0/man3/SSL_set_security_level.html
|
||||
Not specifying 0 here makes LibVNCClient fail connecting to some servers.
|
||||
*/
|
||||
SSL_CTX_set_security_level(ssl_ctx, 0);
|
||||
/*
|
||||
Specifying a maximum protocol version of 1.2 gets us ADH cipher on OpenSSL 1.1.x,
|
||||
see https://github.com/LibVNC/libvncserver/issues/347#issuecomment-597974313
|
||||
*/
|
||||
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_2_VERSION);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!(ssl = SSL_new (ssl_ctx)))
|
||||
{
|
||||
rfbClientLog("Could not create a new SSL session.\n");
|
||||
goto error_free_ctx;
|
||||
}
|
||||
|
||||
SSL_set_fd (ssl, sockfd);
|
||||
SSL_CTX_set_app_data (ssl_ctx, client);
|
||||
|
||||
do
|
||||
{
|
||||
n = SSL_connect(ssl);
|
||||
|
||||
if (n != 1)
|
||||
{
|
||||
if (wait_for_data(ssl, n, 1) != 1)
|
||||
{
|
||||
finished = 1;
|
||||
SSL_shutdown(ssl);
|
||||
|
||||
goto error_free_ssl;
|
||||
}
|
||||
}
|
||||
} while( n != 1 && finished != 1 );
|
||||
|
||||
X509_VERIFY_PARAM_free(param);
|
||||
return ssl;
|
||||
|
||||
error_free_ssl:
|
||||
SSL_free(ssl);
|
||||
|
||||
error_free_ctx:
|
||||
X509_VERIFY_PARAM_free(param);
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static rfbBool
|
||||
InitializeTLSSession(rfbClient* client, rfbBool anonTLS, rfbCredential *cred)
|
||||
{
|
||||
if (client->tlsSession) return TRUE;
|
||||
|
||||
client->tlsSession = open_ssl_connection (client, client->sock, anonTLS, cred);
|
||||
|
||||
if (!client->tlsSession)
|
||||
return FALSE;
|
||||
|
||||
INIT_MUTEX(client->tlsRwMutex);
|
||||
|
||||
rfbClientLog("TLS session initialized.\n");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static rfbBool
|
||||
HandshakeTLS(rfbClient* client)
|
||||
{
|
||||
int timeout = 15;
|
||||
int ret;
|
||||
|
||||
return TRUE;
|
||||
|
||||
while (timeout > 0 && (ret = SSL_do_handshake(client->tlsSession)) < 0)
|
||||
{
|
||||
if (ret != -1)
|
||||
{
|
||||
rfbClientLog("TLS handshake blocking.\n");
|
||||
sleep(1);
|
||||
timeout--;
|
||||
continue;
|
||||
}
|
||||
rfbClientLog("TLS handshake failed.\n");
|
||||
|
||||
FreeTLS(client);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (timeout <= 0)
|
||||
{
|
||||
rfbClientLog("TLS handshake timeout.\n");
|
||||
FreeTLS(client);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rfbClientLog("TLS handshake done.\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* VeNCrypt sub auth. 1 byte auth count, followed by count * 4 byte integers */
|
||||
static rfbBool
|
||||
ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result)
|
||||
{
|
||||
uint8_t count=0;
|
||||
uint8_t loop=0;
|
||||
uint8_t flag=0;
|
||||
uint32_t tAuth[256], t;
|
||||
char buf1[500],buf2[10];
|
||||
uint32_t authScheme;
|
||||
|
||||
if (!ReadFromRFBServer(client, (char *)&count, 1)) return FALSE;
|
||||
|
||||
if (count==0)
|
||||
{
|
||||
rfbClientLog("List of security types is ZERO. Giving up.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rfbClientLog("We have %d security types to read\n", count);
|
||||
authScheme=0;
|
||||
/* now, we have a list of available security types to read ( uint8_t[] ) */
|
||||
for (loop=0;loop<count;loop++)
|
||||
{
|
||||
if (!ReadFromRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
|
||||
t=rfbClientSwap32IfLE(tAuth[loop]);
|
||||
rfbClientLog("%d) Received security type %d\n", loop, t);
|
||||
if (flag) continue;
|
||||
if (t==rfbVeNCryptTLSNone ||
|
||||
t==rfbVeNCryptTLSVNC ||
|
||||
t==rfbVeNCryptTLSPlain ||
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
t==rfbVeNCryptTLSSASL ||
|
||||
t==rfbVeNCryptX509SASL ||
|
||||
#endif /*LIBVNCSERVER_HAVE_SASL */
|
||||
t==rfbVeNCryptX509None ||
|
||||
t==rfbVeNCryptX509VNC ||
|
||||
t==rfbVeNCryptX509Plain)
|
||||
{
|
||||
flag++;
|
||||
authScheme=t;
|
||||
rfbClientLog("Selecting security type %d (%d/%d in the list)\n", authScheme, loop, count);
|
||||
/* send back 4 bytes (in original byte order!) indicating which security type to use */
|
||||
if (!WriteToRFBServer(client, (char *)&tAuth[loop], 4)) return FALSE;
|
||||
}
|
||||
tAuth[loop]=t;
|
||||
}
|
||||
if (authScheme==0)
|
||||
{
|
||||
memset(buf1, 0, sizeof(buf1));
|
||||
for (loop=0;loop<count;loop++)
|
||||
{
|
||||
if (strlen(buf1)>=sizeof(buf1)-1) break;
|
||||
snprintf(buf2, sizeof(buf2), (loop>0 ? ", %d" : "%d"), (int)tAuth[loop]);
|
||||
strncat(buf1, buf2, sizeof(buf1)-strlen(buf1)-1);
|
||||
}
|
||||
rfbClientLog("Unknown VeNCrypt authentication scheme from VNC server: %s\n",
|
||||
buf1);
|
||||
return FALSE;
|
||||
}
|
||||
*result = authScheme;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
rfbBool
|
||||
HandleAnonTLSAuth(rfbClient* client)
|
||||
{
|
||||
if (!InitializeTLS() || !InitializeTLSSession(client, TRUE, NULL)) return FALSE;
|
||||
|
||||
if (!HandshakeTLS(client)) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
FreeX509Credential(rfbCredential *cred)
|
||||
{
|
||||
if (cred->x509Credential.x509CACertFile) free(cred->x509Credential.x509CACertFile);
|
||||
if (cred->x509Credential.x509CACrlFile) free(cred->x509Credential.x509CACrlFile);
|
||||
if (cred->x509Credential.x509ClientCertFile) free(cred->x509Credential.x509ClientCertFile);
|
||||
if (cred->x509Credential.x509ClientKeyFile) free(cred->x509Credential.x509ClientKeyFile);
|
||||
free(cred);
|
||||
}
|
||||
|
||||
rfbBool
|
||||
HandleVeNCryptAuth(rfbClient* client)
|
||||
{
|
||||
uint8_t major, minor, status;
|
||||
uint32_t authScheme;
|
||||
rfbBool anonTLS;
|
||||
rfbCredential *cred = NULL;
|
||||
rfbBool result = TRUE;
|
||||
|
||||
if (!InitializeTLS()) return FALSE;
|
||||
|
||||
/* Read VeNCrypt version */
|
||||
if (!ReadFromRFBServer(client, (char *)&major, 1) ||
|
||||
!ReadFromRFBServer(client, (char *)&minor, 1))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
rfbClientLog("Got VeNCrypt version %d.%d from server.\n", (int)major, (int)minor);
|
||||
|
||||
if (major != 0 && minor != 2)
|
||||
{
|
||||
rfbClientLog("Unsupported VeNCrypt version.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!WriteToRFBServer(client, (char *)&major, 1) ||
|
||||
!WriteToRFBServer(client, (char *)&minor, 1) ||
|
||||
!ReadFromRFBServer(client, (char *)&status, 1))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
rfbClientLog("Server refused VeNCrypt version %d.%d.\n", (int)major, (int)minor);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ReadVeNCryptSecurityType(client, &authScheme)) return FALSE;
|
||||
if (!ReadFromRFBServer(client, (char *)&status, 1) || status != 1)
|
||||
{
|
||||
rfbClientLog("Server refused VeNCrypt authentication %d (%d).\n", authScheme, (int)status);
|
||||
return FALSE;
|
||||
}
|
||||
client->subAuthScheme = authScheme;
|
||||
|
||||
/* Some VeNCrypt security types are anonymous TLS, others are X509 */
|
||||
switch (authScheme)
|
||||
{
|
||||
case rfbVeNCryptTLSNone:
|
||||
case rfbVeNCryptTLSVNC:
|
||||
case rfbVeNCryptTLSPlain:
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
case rfbVeNCryptTLSSASL:
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
anonTLS = TRUE;
|
||||
break;
|
||||
default:
|
||||
anonTLS = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get X509 Credentials if it's not anonymous */
|
||||
if (!anonTLS)
|
||||
{
|
||||
|
||||
if (!client->GetCredential)
|
||||
{
|
||||
rfbClientLog("GetCredential callback is not set.\n");
|
||||
return FALSE;
|
||||
}
|
||||
cred = client->GetCredential(client, rfbCredentialTypeX509);
|
||||
if (!cred)
|
||||
{
|
||||
rfbClientLog("Reading credential failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start up the TLS session */
|
||||
if (!InitializeTLSSession(client, anonTLS, cred)) result = FALSE;
|
||||
|
||||
if (!HandshakeTLS(client)) result = FALSE;
|
||||
|
||||
/* We are done here. The caller should continue with client->subAuthScheme
|
||||
* to do actual sub authentication.
|
||||
*/
|
||||
if (cred) FreeX509Credential(cred);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
ReadFromTLS(rfbClient* client, char *out, unsigned int n)
|
||||
{
|
||||
int ret = 0;
|
||||
int ssl_error = SSL_ERROR_NONE;
|
||||
|
||||
LOCK(client->tlsRwMutex);
|
||||
ret = SSL_read (client->tlsSession, out, n);
|
||||
|
||||
if (ret < 0)
|
||||
ssl_error = SSL_get_error(client->tlsSession, ret);
|
||||
UNLOCK(client->tlsRwMutex);
|
||||
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
else {
|
||||
errno = ssl_error_to_errno(ssl_error);
|
||||
if (errno != EAGAIN) {
|
||||
rfbClientLog("Error reading from TLS: -.\n");
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
WriteToTLS(rfbClient* client, const char *buf, unsigned int n)
|
||||
{
|
||||
unsigned int offset = 0;
|
||||
int ret = 0;
|
||||
int ssl_error = SSL_ERROR_NONE;
|
||||
|
||||
while (offset < n)
|
||||
{
|
||||
LOCK(client->tlsRwMutex);
|
||||
ret = SSL_write (client->tlsSession, buf + offset, (size_t)(n-offset));
|
||||
|
||||
if (ret < 0)
|
||||
ssl_error = SSL_get_error (client->tlsSession, ret);
|
||||
UNLOCK(client->tlsRwMutex);
|
||||
|
||||
if (ret == 0) continue;
|
||||
if (ret < 0)
|
||||
{
|
||||
errno = ssl_error_to_errno(ssl_error);
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) continue;
|
||||
rfbClientLog("Error writing to TLS: -\n");
|
||||
return -1;
|
||||
}
|
||||
offset += (unsigned int)ret;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
void FreeTLS(rfbClient* client)
|
||||
{
|
||||
if (client->tlsSession)
|
||||
{
|
||||
SSL_free(client->tlsSession);
|
||||
client->tlsSession = NULL;
|
||||
TINI_MUTEX(client->tlsRwMutex);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
int GetTLSCipherBits(rfbClient* client)
|
||||
{
|
||||
SSL *ssl = (SSL *)(client->tlsSession);
|
||||
|
||||
const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
|
||||
|
||||
return SSL_CIPHER_get_bits(cipher, NULL);
|
||||
}
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
|
|
@ -0,0 +1,858 @@
|
|||
/*
|
||||
* Copyright (C)2009-2012 D. R. Commander. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* TurboJPEG/OSS: this implements the TurboJPEG API using libjpeg-turbo */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef JCS_EXTENSIONS
|
||||
#define JPEG_INTERNAL_OPTIONS
|
||||
#endif
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
#include <setjmp.h>
|
||||
#include "./turbojpeg.h"
|
||||
|
||||
#define PAD(v, p) ((v+(p)-1)&(~((p)-1)))
|
||||
|
||||
#define CSTATE_START 100
|
||||
#define DSTATE_START 200
|
||||
#define MEMZERO(ptr, size) memset(ptr, 0, size)
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a)<(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
|
||||
/* Error handling (based on example in example.c) */
|
||||
|
||||
static char errStr[JMSG_LENGTH_MAX]="No error";
|
||||
|
||||
struct my_error_mgr
|
||||
{
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
};
|
||||
typedef struct my_error_mgr *my_error_ptr;
|
||||
|
||||
static void my_error_exit(j_common_ptr cinfo)
|
||||
{
|
||||
my_error_ptr myerr=(my_error_ptr)cinfo->err;
|
||||
(*cinfo->err->output_message)(cinfo);
|
||||
longjmp(myerr->setjmp_buffer, 1);
|
||||
}
|
||||
|
||||
/* Based on output_message() in jerror.c */
|
||||
|
||||
static void my_output_message(j_common_ptr cinfo)
|
||||
{
|
||||
(*cinfo->err->format_message)(cinfo, errStr);
|
||||
}
|
||||
|
||||
|
||||
/* Global structures, macros, etc. */
|
||||
|
||||
enum {COMPRESS=1, DECOMPRESS=2};
|
||||
|
||||
typedef struct _tjinstance
|
||||
{
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_decompress_struct dinfo;
|
||||
struct jpeg_destination_mgr jdst;
|
||||
struct jpeg_source_mgr jsrc;
|
||||
struct my_error_mgr jerr;
|
||||
int init;
|
||||
} tjinstance;
|
||||
|
||||
static const int pixelsize[TJ_NUMSAMP]={3, 3, 3, 1, 3};
|
||||
|
||||
#define NUMSF 4
|
||||
static const tjscalingfactor sf[NUMSF]={
|
||||
{1, 1},
|
||||
{1, 2},
|
||||
{1, 4},
|
||||
{1, 8}
|
||||
};
|
||||
|
||||
#define _throw(m) {snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
|
||||
retval=-1; goto bailout;}
|
||||
#define getinstance(handle) tjinstance *this=(tjinstance *)handle; \
|
||||
j_compress_ptr cinfo=NULL; j_decompress_ptr dinfo=NULL; \
|
||||
(void) cinfo; (void) dinfo; /* silence warnings */ \
|
||||
if(!this) {snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
|
||||
return -1;} \
|
||||
cinfo=&this->cinfo; dinfo=&this->dinfo;
|
||||
|
||||
static int getPixelFormat(int pixelSize, int flags)
|
||||
{
|
||||
if(pixelSize==1) return TJPF_GRAY;
|
||||
if(pixelSize==3)
|
||||
{
|
||||
if(flags&TJ_BGR) return TJPF_BGR;
|
||||
else return TJPF_RGB;
|
||||
}
|
||||
if(pixelSize==4)
|
||||
{
|
||||
if(flags&TJ_ALPHAFIRST)
|
||||
{
|
||||
if(flags&TJ_BGR) return TJPF_XBGR;
|
||||
else return TJPF_XRGB;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(flags&TJ_BGR) return TJPF_BGRX;
|
||||
else return TJPF_RGBX;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int setCompDefaults(struct jpeg_compress_struct *cinfo,
|
||||
int pixelFormat, int subsamp, int jpegQual)
|
||||
{
|
||||
int retval=0;
|
||||
|
||||
switch(pixelFormat)
|
||||
{
|
||||
case TJPF_GRAY:
|
||||
cinfo->in_color_space=JCS_GRAYSCALE; break;
|
||||
#if JCS_EXTENSIONS==1
|
||||
case TJPF_RGB:
|
||||
cinfo->in_color_space=JCS_EXT_RGB; break;
|
||||
case TJPF_BGR:
|
||||
cinfo->in_color_space=JCS_EXT_BGR; break;
|
||||
case TJPF_RGBX:
|
||||
case TJPF_RGBA:
|
||||
cinfo->in_color_space=JCS_EXT_RGBX; break;
|
||||
case TJPF_BGRX:
|
||||
case TJPF_BGRA:
|
||||
cinfo->in_color_space=JCS_EXT_BGRX; break;
|
||||
case TJPF_XRGB:
|
||||
case TJPF_ARGB:
|
||||
cinfo->in_color_space=JCS_EXT_XRGB; break;
|
||||
case TJPF_XBGR:
|
||||
case TJPF_ABGR:
|
||||
cinfo->in_color_space=JCS_EXT_XBGR; break;
|
||||
#else
|
||||
case TJPF_RGB:
|
||||
case TJPF_BGR:
|
||||
case TJPF_RGBX:
|
||||
case TJPF_BGRX:
|
||||
case TJPF_XRGB:
|
||||
case TJPF_XBGR:
|
||||
case TJPF_RGBA:
|
||||
case TJPF_BGRA:
|
||||
case TJPF_ARGB:
|
||||
case TJPF_ABGR:
|
||||
cinfo->in_color_space=JCS_RGB; pixelFormat=TJPF_RGB;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
cinfo->input_components=tjPixelSize[pixelFormat];
|
||||
jpeg_set_defaults(cinfo);
|
||||
if(jpegQual>=0)
|
||||
{
|
||||
jpeg_set_quality(cinfo, jpegQual, TRUE);
|
||||
if(jpegQual>=96) cinfo->dct_method=JDCT_ISLOW;
|
||||
else cinfo->dct_method=JDCT_FASTEST;
|
||||
}
|
||||
if(subsamp==TJSAMP_GRAY)
|
||||
jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
|
||||
else
|
||||
jpeg_set_colorspace(cinfo, JCS_YCbCr);
|
||||
|
||||
cinfo->comp_info[0].h_samp_factor=tjMCUWidth[subsamp]/8;
|
||||
cinfo->comp_info[1].h_samp_factor=1;
|
||||
cinfo->comp_info[2].h_samp_factor=1;
|
||||
cinfo->comp_info[0].v_samp_factor=tjMCUHeight[subsamp]/8;
|
||||
cinfo->comp_info[1].v_samp_factor=1;
|
||||
cinfo->comp_info[2].v_samp_factor=1;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int setDecompDefaults(struct jpeg_decompress_struct *dinfo,
|
||||
int pixelFormat)
|
||||
{
|
||||
int retval=0;
|
||||
|
||||
switch(pixelFormat)
|
||||
{
|
||||
case TJPF_GRAY:
|
||||
dinfo->out_color_space=JCS_GRAYSCALE; break;
|
||||
#if JCS_EXTENSIONS==1
|
||||
case TJPF_RGB:
|
||||
dinfo->out_color_space=JCS_EXT_RGB; break;
|
||||
case TJPF_BGR:
|
||||
dinfo->out_color_space=JCS_EXT_BGR; break;
|
||||
case TJPF_RGBX:
|
||||
dinfo->out_color_space=JCS_EXT_RGBX; break;
|
||||
case TJPF_BGRX:
|
||||
dinfo->out_color_space=JCS_EXT_BGRX; break;
|
||||
case TJPF_XRGB:
|
||||
dinfo->out_color_space=JCS_EXT_XRGB; break;
|
||||
case TJPF_XBGR:
|
||||
dinfo->out_color_space=JCS_EXT_XBGR; break;
|
||||
#if JCS_ALPHA_EXTENSIONS==1
|
||||
case TJPF_RGBA:
|
||||
dinfo->out_color_space=JCS_EXT_RGBA; break;
|
||||
case TJPF_BGRA:
|
||||
dinfo->out_color_space=JCS_EXT_BGRA; break;
|
||||
case TJPF_ARGB:
|
||||
dinfo->out_color_space=JCS_EXT_ARGB; break;
|
||||
case TJPF_ABGR:
|
||||
dinfo->out_color_space=JCS_EXT_ABGR; break;
|
||||
#endif
|
||||
#else
|
||||
case TJPF_RGB:
|
||||
case TJPF_BGR:
|
||||
case TJPF_RGBX:
|
||||
case TJPF_BGRX:
|
||||
case TJPF_XRGB:
|
||||
case TJPF_XBGR:
|
||||
case TJPF_RGBA:
|
||||
case TJPF_BGRA:
|
||||
case TJPF_ARGB:
|
||||
case TJPF_ABGR:
|
||||
dinfo->out_color_space=JCS_RGB; break;
|
||||
#endif
|
||||
default:
|
||||
_throw("Unsupported pixel format");
|
||||
}
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static int getSubsamp(j_decompress_ptr dinfo)
|
||||
{
|
||||
int retval=-1, i, k;
|
||||
for(i=0; i<NUMSUBOPT; i++)
|
||||
{
|
||||
if(dinfo->num_components==pixelsize[i])
|
||||
{
|
||||
if(dinfo->comp_info[0].h_samp_factor==tjMCUWidth[i]/8
|
||||
&& dinfo->comp_info[0].v_samp_factor==tjMCUHeight[i]/8)
|
||||
{
|
||||
int match=0;
|
||||
for(k=1; k<dinfo->num_components; k++)
|
||||
{
|
||||
if(dinfo->comp_info[k].h_samp_factor==1
|
||||
&& dinfo->comp_info[k].v_samp_factor==1)
|
||||
match++;
|
||||
}
|
||||
if(match==dinfo->num_components-1)
|
||||
{
|
||||
retval=i; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
#ifndef JCS_EXTENSIONS
|
||||
|
||||
/* Conversion functions to emulate the colorspace extensions. This allows the
|
||||
TurboJPEG wrapper to be used with libjpeg */
|
||||
|
||||
#define TORGB(PS, ROFFSET, GOFFSET, BOFFSET) { \
|
||||
int rowPad=pitch-width*PS; \
|
||||
while(height--) \
|
||||
{ \
|
||||
unsigned char *endOfRow=src+width*PS; \
|
||||
while(src<endOfRow) \
|
||||
{ \
|
||||
dst[RGB_RED]=src[ROFFSET]; \
|
||||
dst[RGB_GREEN]=src[GOFFSET]; \
|
||||
dst[RGB_BLUE]=src[BOFFSET]; \
|
||||
dst+=RGB_PIXELSIZE; src+=PS; \
|
||||
} \
|
||||
src+=rowPad; \
|
||||
} \
|
||||
}
|
||||
|
||||
static unsigned char *toRGB(unsigned char *src, int width, int pitch,
|
||||
int height, int pixelFormat, unsigned char *dst)
|
||||
{
|
||||
unsigned char *retval=src;
|
||||
switch(pixelFormat)
|
||||
{
|
||||
case TJPF_RGB:
|
||||
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=3
|
||||
retval=dst; TORGB(3, 0, 1, 2);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_BGR:
|
||||
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=3
|
||||
retval=dst; TORGB(3, 2, 1, 0);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_RGBX:
|
||||
case TJPF_RGBA:
|
||||
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4
|
||||
retval=dst; TORGB(4, 0, 1, 2);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_BGRX:
|
||||
case TJPF_BGRA:
|
||||
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4
|
||||
retval=dst; TORGB(4, 2, 1, 0);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_XRGB:
|
||||
case TJPF_ARGB:
|
||||
#if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4
|
||||
retval=dst; TORGB(4, 1, 2, 3);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_XBGR:
|
||||
case TJPF_ABGR:
|
||||
#if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4
|
||||
retval=dst; TORGB(4, 3, 2, 1);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#define FROMRGB(PS, ROFFSET, GOFFSET, BOFFSET, SETALPHA) { \
|
||||
int rowPad=pitch-width*PS; \
|
||||
while(height--) \
|
||||
{ \
|
||||
unsigned char *endOfRow=dst+width*PS; \
|
||||
while(dst<endOfRow) \
|
||||
{ \
|
||||
dst[ROFFSET]=src[RGB_RED]; \
|
||||
dst[GOFFSET]=src[RGB_GREEN]; \
|
||||
dst[BOFFSET]=src[RGB_BLUE]; \
|
||||
SETALPHA \
|
||||
dst+=PS; src+=RGB_PIXELSIZE; \
|
||||
} \
|
||||
dst+=rowPad; \
|
||||
} \
|
||||
}
|
||||
|
||||
static void fromRGB(unsigned char *src, unsigned char *dst, int width,
|
||||
int pitch, int height, int pixelFormat)
|
||||
{
|
||||
switch(pixelFormat)
|
||||
{
|
||||
case TJPF_RGB:
|
||||
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=3
|
||||
FROMRGB(3, 0, 1, 2,);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_BGR:
|
||||
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=3
|
||||
FROMRGB(3, 2, 1, 0,);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_RGBX:
|
||||
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 0, 1, 2,);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_RGBA:
|
||||
#if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 0, 1, 2, dst[3]=0xFF;);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_BGRX:
|
||||
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 2, 1, 0,);
|
||||
#endif
|
||||
break;
|
||||
case TJPF_BGRA:
|
||||
#if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 2, 1, 0, dst[3]=0xFF;); return;
|
||||
#endif
|
||||
break;
|
||||
case TJPF_XRGB:
|
||||
#if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 1, 2, 3,); return;
|
||||
#endif
|
||||
break;
|
||||
case TJPF_ARGB:
|
||||
#if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 1, 2, 3, dst[0]=0xFF;); return;
|
||||
#endif
|
||||
break;
|
||||
case TJPF_XBGR:
|
||||
#if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 3, 2, 1,); return;
|
||||
#endif
|
||||
break;
|
||||
case TJPF_ABGR:
|
||||
#if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4
|
||||
FROMRGB(4, 3, 2, 1, dst[0]=0xFF;); return;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* General API functions */
|
||||
|
||||
DLLEXPORT char* DLLCALL tjGetErrorStr(void)
|
||||
{
|
||||
return errStr;
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT int DLLCALL tjDestroy(tjhandle handle)
|
||||
{
|
||||
getinstance(handle);
|
||||
if(setjmp(this->jerr.setjmp_buffer)) return -1;
|
||||
if(this->init&COMPRESS) jpeg_destroy_compress(cinfo);
|
||||
if(this->init&DECOMPRESS) jpeg_destroy_decompress(dinfo);
|
||||
free(this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Compressor */
|
||||
|
||||
static boolean empty_output_buffer(j_compress_ptr cinfo)
|
||||
{
|
||||
ERREXIT(cinfo, JERR_BUFFER_SIZE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void dst_noop(j_compress_ptr cinfo)
|
||||
{
|
||||
}
|
||||
|
||||
static tjhandle _tjInitCompress(tjinstance *this)
|
||||
{
|
||||
/* This is also straight out of example.c */
|
||||
this->cinfo.err=jpeg_std_error(&this->jerr.pub);
|
||||
this->jerr.pub.error_exit=my_error_exit;
|
||||
this->jerr.pub.output_message=my_output_message;
|
||||
|
||||
if(setjmp(this->jerr.setjmp_buffer))
|
||||
{
|
||||
/* If we get here, the JPEG code has signaled an error. */
|
||||
if(this) free(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jpeg_create_compress(&this->cinfo);
|
||||
this->cinfo.dest=&this->jdst;
|
||||
this->jdst.init_destination=dst_noop;
|
||||
this->jdst.empty_output_buffer=empty_output_buffer;
|
||||
this->jdst.term_destination=dst_noop;
|
||||
|
||||
this->init|=COMPRESS;
|
||||
return (tjhandle)this;
|
||||
}
|
||||
|
||||
DLLEXPORT tjhandle DLLCALL tjInitCompress(void)
|
||||
{
|
||||
tjinstance *this=NULL;
|
||||
if((this=(tjinstance *)malloc(sizeof(tjinstance)))==NULL)
|
||||
{
|
||||
snprintf(errStr, JMSG_LENGTH_MAX,
|
||||
"tjInitCompress(): Memory allocation failure");
|
||||
return NULL;
|
||||
}
|
||||
MEMZERO(this, sizeof(tjinstance));
|
||||
return _tjInitCompress(this);
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT unsigned long DLLCALL tjBufSize(int width, int height,
|
||||
int jpegSubsamp)
|
||||
{
|
||||
unsigned long retval=0; int mcuw, mcuh, chromasf;
|
||||
if(width<1 || height<1 || jpegSubsamp<0 || jpegSubsamp>=NUMSUBOPT)
|
||||
_throw("tjBufSize(): Invalid argument");
|
||||
|
||||
/*
|
||||
* This allows for rare corner cases in which a JPEG image can actually be
|
||||
* larger than the uncompressed input (we wouldn't mention it if it hadn't
|
||||
* happened before.)
|
||||
*/
|
||||
mcuw=tjMCUWidth[jpegSubsamp];
|
||||
mcuh=tjMCUHeight[jpegSubsamp];
|
||||
chromasf=jpegSubsamp==TJSAMP_GRAY? 0: 4*64/(mcuw*mcuh);
|
||||
retval=PAD(width, mcuw) * PAD(height, mcuh) * (2 + chromasf) + 2048;
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height)
|
||||
{
|
||||
unsigned long retval=0;
|
||||
if(width<1 || height<1)
|
||||
_throw("TJBUFSIZE(): Invalid argument");
|
||||
|
||||
/*
|
||||
* This allows for rare corner cases in which a JPEG image can actually be
|
||||
* larger than the uncompressed input (we wouldn't mention it if it hadn't
|
||||
* happened before.)
|
||||
*/
|
||||
retval=PAD(width, 16) * PAD(height, 16) * 6 + 2048;
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT int DLLCALL tjCompress2(tjhandle handle, unsigned char *srcBuf,
|
||||
int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf,
|
||||
unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags)
|
||||
{
|
||||
int i, retval=0; JSAMPROW *row_pointer=NULL;
|
||||
#ifndef JCS_EXTENSIONS
|
||||
unsigned char *rgbBuf=NULL;
|
||||
#endif
|
||||
|
||||
getinstance(handle)
|
||||
if((this->init&COMPRESS)==0)
|
||||
_throw("tjCompress2(): Instance has not been initialized for compression");
|
||||
|
||||
if(srcBuf==NULL || width<=0 || pitch<0 || height<=0 || pixelFormat<0
|
||||
|| pixelFormat>=TJ_NUMPF || jpegBuf==NULL || jpegSize==NULL
|
||||
|| jpegSubsamp<0 || jpegSubsamp>=NUMSUBOPT || jpegQual<0 || jpegQual>100)
|
||||
_throw("tjCompress2(): Invalid argument");
|
||||
|
||||
if(setjmp(this->jerr.setjmp_buffer))
|
||||
{
|
||||
/* If we get here, the JPEG code has signaled an error. */
|
||||
retval=-1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if(pitch==0) pitch=width*tjPixelSize[pixelFormat];
|
||||
|
||||
#ifndef JCS_EXTENSIONS
|
||||
if(pixelFormat!=TJPF_GRAY)
|
||||
{
|
||||
rgbBuf=(unsigned char *)malloc(width*height*RGB_PIXELSIZE);
|
||||
if(!rgbBuf) _throw("tjCompress2(): Memory allocation failure");
|
||||
srcBuf=toRGB(srcBuf, width, pitch, height, pixelFormat, rgbBuf);
|
||||
pitch=width*RGB_PIXELSIZE;
|
||||
}
|
||||
#endif
|
||||
|
||||
cinfo->image_width=width;
|
||||
cinfo->image_height=height;
|
||||
|
||||
if(flags&TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
|
||||
else if(flags&TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
|
||||
else if(flags&TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
|
||||
|
||||
if(setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual)==-1)
|
||||
return -1;
|
||||
|
||||
this->jdst.next_output_byte=*jpegBuf;
|
||||
this->jdst.free_in_buffer=tjBufSize(width, height, jpegSubsamp);
|
||||
|
||||
jpeg_start_compress(cinfo, TRUE);
|
||||
if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL)
|
||||
_throw("tjCompress2(): Memory allocation failure");
|
||||
for(i=0; i<height; i++)
|
||||
{
|
||||
if(flags&TJFLAG_BOTTOMUP) row_pointer[i]=&srcBuf[(height-i-1)*pitch];
|
||||
else row_pointer[i]=&srcBuf[i*pitch];
|
||||
}
|
||||
while(cinfo->next_scanline<cinfo->image_height)
|
||||
{
|
||||
jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
|
||||
cinfo->image_height-cinfo->next_scanline);
|
||||
}
|
||||
jpeg_finish_compress(cinfo);
|
||||
*jpegSize=tjBufSize(width, height, jpegSubsamp)
|
||||
-(unsigned long)(this->jdst.free_in_buffer);
|
||||
|
||||
bailout:
|
||||
if(cinfo->global_state>CSTATE_START) jpeg_abort_compress(cinfo);
|
||||
#ifndef JCS_EXTENSIONS
|
||||
if(rgbBuf) free(rgbBuf);
|
||||
#endif
|
||||
if(row_pointer) free(row_pointer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
DLLEXPORT int DLLCALL tjCompress(tjhandle handle, unsigned char *srcBuf,
|
||||
int width, int pitch, int height, int pixelSize, unsigned char *jpegBuf,
|
||||
unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags)
|
||||
{
|
||||
int retval=0; unsigned long size = 0;
|
||||
retval=tjCompress2(handle, srcBuf, width, pitch, height,
|
||||
getPixelFormat(pixelSize, flags), &jpegBuf, &size, jpegSubsamp, jpegQual,
|
||||
flags);
|
||||
*jpegSize=size;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/* Decompressor */
|
||||
|
||||
static boolean fill_input_buffer(j_decompress_ptr dinfo)
|
||||
{
|
||||
ERREXIT(dinfo, JERR_BUFFER_SIZE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void skip_input_data(j_decompress_ptr dinfo, long num_bytes)
|
||||
{
|
||||
dinfo->src->next_input_byte += (size_t) num_bytes;
|
||||
dinfo->src->bytes_in_buffer -= (size_t) num_bytes;
|
||||
}
|
||||
|
||||
static void src_noop(j_decompress_ptr dinfo)
|
||||
{
|
||||
}
|
||||
|
||||
static tjhandle _tjInitDecompress(tjinstance *this)
|
||||
{
|
||||
/* This is also straight out of example.c */
|
||||
this->dinfo.err=jpeg_std_error(&this->jerr.pub);
|
||||
this->jerr.pub.error_exit=my_error_exit;
|
||||
this->jerr.pub.output_message=my_output_message;
|
||||
|
||||
if(setjmp(this->jerr.setjmp_buffer))
|
||||
{
|
||||
/* If we get here, the JPEG code has signaled an error. */
|
||||
if(this) free(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jpeg_create_decompress(&this->dinfo);
|
||||
this->dinfo.src=&this->jsrc;
|
||||
this->jsrc.init_source=src_noop;
|
||||
this->jsrc.fill_input_buffer=fill_input_buffer;
|
||||
this->jsrc.skip_input_data=skip_input_data;
|
||||
this->jsrc.resync_to_restart=jpeg_resync_to_restart;
|
||||
this->jsrc.term_source=src_noop;
|
||||
|
||||
this->init|=DECOMPRESS;
|
||||
return (tjhandle)this;
|
||||
}
|
||||
|
||||
DLLEXPORT tjhandle DLLCALL tjInitDecompress(void)
|
||||
{
|
||||
tjinstance *this;
|
||||
if((this=(tjinstance *)malloc(sizeof(tjinstance)))==NULL)
|
||||
{
|
||||
snprintf(errStr, JMSG_LENGTH_MAX,
|
||||
"tjInitDecompress(): Memory allocation failure");
|
||||
return NULL;
|
||||
}
|
||||
MEMZERO(this, sizeof(tjinstance));
|
||||
return _tjInitDecompress(this);
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle handle,
|
||||
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height,
|
||||
int *jpegSubsamp)
|
||||
{
|
||||
int retval=0;
|
||||
|
||||
getinstance(handle);
|
||||
if((this->init&DECOMPRESS)==0)
|
||||
_throw("tjDecompressHeader2(): Instance has not been initialized for decompression");
|
||||
|
||||
if(jpegBuf==NULL || jpegSize<=0 || width==NULL || height==NULL
|
||||
|| jpegSubsamp==NULL)
|
||||
_throw("tjDecompressHeader2(): Invalid argument");
|
||||
|
||||
if(setjmp(this->jerr.setjmp_buffer))
|
||||
{
|
||||
/* If we get here, the JPEG code has signaled an error. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
this->jsrc.bytes_in_buffer=jpegSize;
|
||||
this->jsrc.next_input_byte=jpegBuf;
|
||||
jpeg_read_header(dinfo, TRUE);
|
||||
|
||||
*width=dinfo->image_width;
|
||||
*height=dinfo->image_height;
|
||||
*jpegSubsamp=getSubsamp(dinfo);
|
||||
|
||||
jpeg_abort_decompress(dinfo);
|
||||
|
||||
if(*jpegSubsamp<0)
|
||||
_throw("tjDecompressHeader2(): Could not determine subsampling type for JPEG image");
|
||||
if(*width<1 || *height<1)
|
||||
_throw("tjDecompressHeader2(): Invalid data returned in header");
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
}
|
||||
|
||||
DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle handle,
|
||||
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height)
|
||||
{
|
||||
int jpegSubsamp;
|
||||
return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height,
|
||||
&jpegSubsamp);
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT tjscalingfactor* DLLCALL tjGetScalingFactors(int *numscalingfactors)
|
||||
{
|
||||
if(numscalingfactors==NULL)
|
||||
{
|
||||
snprintf(errStr, JMSG_LENGTH_MAX,
|
||||
"tjGetScalingFactors(): Invalid argument");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*numscalingfactors=NUMSF;
|
||||
return (tjscalingfactor *)sf;
|
||||
}
|
||||
|
||||
|
||||
DLLEXPORT int DLLCALL tjDecompress2(tjhandle handle, unsigned char *jpegBuf,
|
||||
unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch,
|
||||
int height, int pixelFormat, int flags)
|
||||
{
|
||||
int i, retval=0; JSAMPROW *row_pointer=NULL;
|
||||
int jpegwidth, jpegheight, scaledw, scaledh;
|
||||
#ifndef JCS_EXTENSIONS
|
||||
unsigned char *rgbBuf=NULL;
|
||||
unsigned char *_dstBuf=NULL; int _pitch=0;
|
||||
#endif
|
||||
|
||||
getinstance(handle);
|
||||
if((this->init&DECOMPRESS)==0)
|
||||
_throw("tjDecompress2(): Instance has not been initialized for decompression");
|
||||
|
||||
if(jpegBuf==NULL || jpegSize<=0 || dstBuf==NULL || width<0 || pitch<0
|
||||
|| height<0 || pixelFormat<0 || pixelFormat>=TJ_NUMPF)
|
||||
_throw("tjDecompress2(): Invalid argument");
|
||||
|
||||
if(flags&TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
|
||||
else if(flags&TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
|
||||
else if(flags&TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
|
||||
|
||||
if(setjmp(this->jerr.setjmp_buffer))
|
||||
{
|
||||
/* If we get here, the JPEG code has signaled an error. */
|
||||
retval=-1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
this->jsrc.bytes_in_buffer=jpegSize;
|
||||
this->jsrc.next_input_byte=jpegBuf;
|
||||
jpeg_read_header(dinfo, TRUE);
|
||||
if(setDecompDefaults(dinfo, pixelFormat)==-1)
|
||||
{
|
||||
retval=-1; goto bailout;
|
||||
}
|
||||
|
||||
if(flags&TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling=FALSE;
|
||||
|
||||
jpegwidth=dinfo->image_width; jpegheight=dinfo->image_height;
|
||||
if(width==0) width=jpegwidth;
|
||||
if(height==0) height=jpegheight;
|
||||
for(i=0; i<NUMSF; i++)
|
||||
{
|
||||
scaledw=TJSCALED(jpegwidth, sf[i]);
|
||||
scaledh=TJSCALED(jpegheight, sf[i]);
|
||||
if(scaledw<=width && scaledh<=height)
|
||||
break;
|
||||
}
|
||||
if(scaledw>width || scaledh>height)
|
||||
_throw("tjDecompress2(): Could not scale down to desired image dimensions");
|
||||
#ifndef JCS_EXTENSIONS
|
||||
width=scaledw; height=scaledh;
|
||||
#endif
|
||||
dinfo->scale_num=sf[i].num;
|
||||
dinfo->scale_denom=sf[i].denom;
|
||||
|
||||
jpeg_start_decompress(dinfo);
|
||||
if(pitch==0) pitch=dinfo->output_width*tjPixelSize[pixelFormat];
|
||||
|
||||
#ifndef JCS_EXTENSIONS
|
||||
if(pixelFormat!=TJPF_GRAY &&
|
||||
(RGB_RED!=tjRedOffset[pixelFormat] ||
|
||||
RGB_GREEN!=tjGreenOffset[pixelFormat] ||
|
||||
RGB_BLUE!=tjBlueOffset[pixelFormat] ||
|
||||
RGB_PIXELSIZE!=tjPixelSize[pixelFormat]))
|
||||
{
|
||||
rgbBuf=(unsigned char *)malloc(width*height*3);
|
||||
if(!rgbBuf) _throw("tjDecompress2(): Memory allocation failure");
|
||||
_pitch=pitch; pitch=width*3;
|
||||
_dstBuf=dstBuf; dstBuf=rgbBuf;
|
||||
}
|
||||
#endif
|
||||
|
||||
if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)
|
||||
*dinfo->output_height))==NULL)
|
||||
_throw("tjDecompress2(): Memory allocation failure");
|
||||
for(i=0; i<(int)dinfo->output_height; i++)
|
||||
{
|
||||
if(flags&TJFLAG_BOTTOMUP)
|
||||
row_pointer[i]=&dstBuf[(dinfo->output_height-i-1)*pitch];
|
||||
else row_pointer[i]=&dstBuf[i*pitch];
|
||||
}
|
||||
while(dinfo->output_scanline<dinfo->output_height)
|
||||
{
|
||||
jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
|
||||
dinfo->output_height-dinfo->output_scanline);
|
||||
}
|
||||
jpeg_finish_decompress(dinfo);
|
||||
|
||||
#ifndef JCS_EXTENSIONS
|
||||
fromRGB(rgbBuf, _dstBuf, width, _pitch, height, pixelFormat);
|
||||
#endif
|
||||
|
||||
bailout:
|
||||
if(dinfo->global_state>DSTATE_START) jpeg_abort_decompress(dinfo);
|
||||
#ifndef JCS_EXTENSIONS
|
||||
if(rgbBuf) free(rgbBuf);
|
||||
#endif
|
||||
if(row_pointer) free(row_pointer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
DLLEXPORT int DLLCALL tjDecompress(tjhandle handle, unsigned char *jpegBuf,
|
||||
unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch,
|
||||
int height, int pixelSize, int flags)
|
||||
{
|
||||
return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
|
||||
height, getPixelFormat(pixelSize, flags), flags);
|
||||
}
|
|
@ -21,10 +21,10 @@
|
|||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
#include <pixman.h>
|
||||
#include <rfb/rfbclient.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <libavutil/frame.h>
|
||||
|
||||
#include "rfb/rfbclient.h"
|
||||
#include "vnc.h"
|
||||
#include "open-h264.h"
|
||||
|
||||
|
|
|
@ -0,0 +1,543 @@
|
|||
/*
|
||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
*
|
||||
* This 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* vncviewer.c - the Xt-based VNC viewer.
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define strdup _strdup /* Prevent POSIX deprecation warnings */
|
||||
#endif
|
||||
|
||||
#ifdef __STRICT_ANSI__
|
||||
#define _BSD_SOURCE
|
||||
#define _POSIX_SOURCE
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <rfb/rfbclient.h>
|
||||
#include "tls.h"
|
||||
|
||||
static void Dummy(rfbClient* client) {
|
||||
}
|
||||
static rfbBool DummyPoint(rfbClient* client, int x, int y) {
|
||||
return TRUE;
|
||||
}
|
||||
static void DummyRect(rfbClient* client, int x, int y, int w, int h) {
|
||||
}
|
||||
|
||||
#include <termios.h>
|
||||
|
||||
static char* ReadPassword(rfbClient* client) {
|
||||
int i;
|
||||
char* p=calloc(1,9);
|
||||
if (!p) return p;
|
||||
struct termios save,noecho;
|
||||
if(tcgetattr(fileno(stdin),&save)!=0) return p;
|
||||
noecho=save; noecho.c_lflag &= ~ECHO;
|
||||
if(tcsetattr(fileno(stdin),TCSAFLUSH,&noecho)!=0) return p;
|
||||
fprintf(stderr,"Password: ");
|
||||
fflush(stderr);
|
||||
i=0;
|
||||
while(1) {
|
||||
int c=fgetc(stdin);
|
||||
if(c=='\n')
|
||||
break;
|
||||
if(i<8) {
|
||||
p[i]=c;
|
||||
i++;
|
||||
p[i]=0;
|
||||
}
|
||||
}
|
||||
tcsetattr(fileno(stdin),TCSAFLUSH,&save);
|
||||
return p;
|
||||
}
|
||||
static rfbBool MallocFrameBuffer(rfbClient* client) {
|
||||
uint64_t allocSize;
|
||||
|
||||
if(client->frameBuffer) {
|
||||
free(client->frameBuffer);
|
||||
client->frameBuffer = NULL;
|
||||
}
|
||||
|
||||
/* SECURITY: promote 'width' into uint64_t so that the multiplication does not overflow
|
||||
'width' and 'height' are 16-bit integers per RFB protocol design
|
||||
SIZE_MAX is the maximum value that can fit into size_t
|
||||
*/
|
||||
allocSize = (uint64_t)client->width * client->height * client->format.bitsPerPixel/8;
|
||||
|
||||
if (allocSize >= SIZE_MAX) {
|
||||
rfbClientErr("CRITICAL: cannot allocate frameBuffer, requested size is too large\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
client->frameBuffer=malloc( (size_t)allocSize );
|
||||
|
||||
if (client->frameBuffer == NULL)
|
||||
rfbClientErr("CRITICAL: frameBuffer allocation failed, requested size too large or not enough memory?\n");
|
||||
|
||||
return client->frameBuffer?TRUE:FALSE;
|
||||
}
|
||||
|
||||
/* messages */
|
||||
|
||||
static rfbBool CheckRect(rfbClient* client, int x, int y, int w, int h) {
|
||||
return x + w <= client->width && y + h <= client->height;
|
||||
}
|
||||
|
||||
static void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_t colour) {
|
||||
int i,j;
|
||||
|
||||
if (client->frameBuffer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckRect(client, x, y, w, h)) {
|
||||
rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", x, y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
#define FILL_RECT(BPP) \
|
||||
for(j=y*client->width;j<(y+h)*client->width;j+=client->width) \
|
||||
for(i=x;i<x+w;i++) \
|
||||
((uint##BPP##_t*)client->frameBuffer)[j+i]=colour;
|
||||
|
||||
switch(client->format.bitsPerPixel) {
|
||||
case 8: FILL_RECT(8); break;
|
||||
case 16: FILL_RECT(16); break;
|
||||
case 32: FILL_RECT(32); break;
|
||||
default:
|
||||
rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
|
||||
}
|
||||
}
|
||||
|
||||
static void CopyRectangle(rfbClient* client, const uint8_t* buffer, int x, int y, int w, int h) {
|
||||
int j;
|
||||
|
||||
if (client->frameBuffer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckRect(client, x, y, w, h)) {
|
||||
rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", x, y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
#define COPY_RECT(BPP) \
|
||||
{ \
|
||||
int rs = w * BPP / 8, rs2 = client->width * BPP / 8; \
|
||||
for (j = ((x * (BPP / 8)) + (y * rs2)); j < (y + h) * rs2; j += rs2) { \
|
||||
memcpy(client->frameBuffer + j, buffer, rs); \
|
||||
buffer += rs; \
|
||||
} \
|
||||
}
|
||||
|
||||
switch(client->format.bitsPerPixel) {
|
||||
case 8: COPY_RECT(8); break;
|
||||
case 16: COPY_RECT(16); break;
|
||||
case 32: COPY_RECT(32); break;
|
||||
default:
|
||||
rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: test */
|
||||
static void CopyRectangleFromRectangle(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) {
|
||||
int i,j;
|
||||
|
||||
if (client->frameBuffer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckRect(client, src_x, src_y, w, h)) {
|
||||
rfbClientLog("Source rect out of bounds: %dx%d at (%d, %d)\n", src_x, src_y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckRect(client, dest_x, dest_y, w, h)) {
|
||||
rfbClientLog("Dest rect out of bounds: %dx%d at (%d, %d)\n", dest_x, dest_y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
#define COPY_RECT_FROM_RECT(BPP) \
|
||||
{ \
|
||||
uint##BPP##_t* _buffer=((uint##BPP##_t*)client->frameBuffer)+(src_y-dest_y)*client->width+src_x-dest_x; \
|
||||
if (dest_y < src_y) { \
|
||||
for(j = dest_y*client->width; j < (dest_y+h)*client->width; j += client->width) { \
|
||||
if (dest_x < src_x) { \
|
||||
for(i = dest_x; i < dest_x+w; i++) { \
|
||||
((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
|
||||
} \
|
||||
} else { \
|
||||
for(i = dest_x+w-1; i >= dest_x; i--) { \
|
||||
((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} else { \
|
||||
for(j = (dest_y+h-1)*client->width; j >= dest_y*client->width; j-=client->width) { \
|
||||
if (dest_x < src_x) { \
|
||||
for(i = dest_x; i < dest_x+w; i++) { \
|
||||
((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
|
||||
} \
|
||||
} else { \
|
||||
for(i = dest_x+w-1; i >= dest_x; i--) { \
|
||||
((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
switch(client->format.bitsPerPixel) {
|
||||
case 8: COPY_RECT_FROM_RECT(8); break;
|
||||
case 16: COPY_RECT_FROM_RECT(16); break;
|
||||
case 32: COPY_RECT_FROM_RECT(32); break;
|
||||
default:
|
||||
rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
|
||||
}
|
||||
}
|
||||
|
||||
static void initAppData(AppData* data) {
|
||||
data->shareDesktop=TRUE;
|
||||
data->viewOnly=FALSE;
|
||||
data->encodingsString="tight zrle ultra copyrect hextile zlib corre rre raw";
|
||||
data->useBGR233=FALSE;
|
||||
data->nColours=0;
|
||||
data->forceOwnCmap=FALSE;
|
||||
data->forceTrueColour=FALSE;
|
||||
data->requestedDepth=0;
|
||||
data->compressLevel=3;
|
||||
data->qualityLevel=5;
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
||||
data->enableJPEG=TRUE;
|
||||
#else
|
||||
data->enableJPEG=FALSE;
|
||||
#endif
|
||||
data->useRemoteCursor=FALSE;
|
||||
}
|
||||
|
||||
rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,
|
||||
int bytesPerPixel) {
|
||||
rfbClient* client=(rfbClient*)calloc(sizeof(rfbClient),1);
|
||||
if(!client) {
|
||||
rfbClientErr("Couldn't allocate client structure!\n");
|
||||
return NULL;
|
||||
}
|
||||
initAppData(&client->appData);
|
||||
client->endianTest = 1;
|
||||
client->programName="";
|
||||
client->serverHost=strdup("");
|
||||
client->serverPort=5900;
|
||||
|
||||
client->destHost = NULL;
|
||||
client->destPort = 5900;
|
||||
|
||||
client->connectTimeout = DEFAULT_CONNECT_TIMEOUT;
|
||||
client->readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
|
||||
/* default: use complete frame buffer */
|
||||
client->updateRect.x = -1;
|
||||
|
||||
client->frameBuffer = NULL;
|
||||
client->outputWindow = 0;
|
||||
|
||||
client->format.bitsPerPixel = bytesPerPixel*8;
|
||||
client->format.depth = bitsPerSample*samplesPerPixel;
|
||||
client->appData.requestedDepth=client->format.depth;
|
||||
client->format.bigEndian = *(char *)&client->endianTest?FALSE:TRUE;
|
||||
client->format.trueColour = 1;
|
||||
|
||||
if (client->format.bitsPerPixel == 8) {
|
||||
client->format.redMax = 7;
|
||||
client->format.greenMax = 7;
|
||||
client->format.blueMax = 3;
|
||||
client->format.redShift = 0;
|
||||
client->format.greenShift = 3;
|
||||
client->format.blueShift = 6;
|
||||
} else {
|
||||
client->format.redMax = (1 << bitsPerSample) - 1;
|
||||
client->format.greenMax = (1 << bitsPerSample) - 1;
|
||||
client->format.blueMax = (1 << bitsPerSample) - 1;
|
||||
if(!client->format.bigEndian) {
|
||||
client->format.redShift = 0;
|
||||
client->format.greenShift = bitsPerSample;
|
||||
client->format.blueShift = bitsPerSample * 2;
|
||||
} else {
|
||||
if(client->format.bitsPerPixel==8*3) {
|
||||
client->format.redShift = bitsPerSample*2;
|
||||
client->format.greenShift = bitsPerSample*1;
|
||||
client->format.blueShift = 0;
|
||||
} else {
|
||||
client->format.redShift = bitsPerSample*3;
|
||||
client->format.greenShift = bitsPerSample*2;
|
||||
client->format.blueShift = bitsPerSample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client->bufoutptr=client->buf;
|
||||
client->buffered=0;
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||
client->raw_buffer_size = -1;
|
||||
client->decompStreamInited = FALSE;
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
||||
memset(client->zlibStreamActive,0,sizeof(rfbBool)*4);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
client->HandleCursorPos = DummyPoint;
|
||||
client->SoftCursorLockArea = DummyRect;
|
||||
client->SoftCursorUnlockScreen = Dummy;
|
||||
client->GotFrameBufferUpdate = DummyRect;
|
||||
client->GotCopyRect = CopyRectangleFromRectangle;
|
||||
client->GotFillRect = FillRectangle;
|
||||
client->GotBitmap = CopyRectangle;
|
||||
client->FinishedFrameBufferUpdate = NULL;
|
||||
client->GetPassword = ReadPassword;
|
||||
client->MallocFrameBuffer = MallocFrameBuffer;
|
||||
client->Bell = Dummy;
|
||||
client->CurrentKeyboardLedState = 0;
|
||||
client->HandleKeyboardLedState = (HandleKeyboardLedStateProc)DummyPoint;
|
||||
client->QoS_DSCP = 0;
|
||||
|
||||
client->authScheme = 0;
|
||||
client->subAuthScheme = 0;
|
||||
client->GetCredential = NULL;
|
||||
client->tlsSession = NULL;
|
||||
client->LockWriteToTLS = NULL;
|
||||
client->UnlockWriteToTLS = NULL;
|
||||
client->sock = RFB_INVALID_SOCKET;
|
||||
client->listenSock = RFB_INVALID_SOCKET;
|
||||
client->listenAddress = NULL;
|
||||
client->listen6Sock = RFB_INVALID_SOCKET;
|
||||
client->listen6Address = NULL;
|
||||
client->clientAuthSchemes = NULL;
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
client->GetSASLMechanism = NULL;
|
||||
client->GetUser = NULL;
|
||||
client->saslSecret = NULL;
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
|
||||
client->requestedResize = FALSE;
|
||||
client->screen.width = 0;
|
||||
client->screen.height = 0;
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
static rfbBool rfbInitConnection(rfbClient* client)
|
||||
{
|
||||
/* Unless we accepted an incoming connection, make a TCP connection to the
|
||||
given VNC server */
|
||||
|
||||
if (!client->listenSpecified) {
|
||||
if (!client->serverHost)
|
||||
return FALSE;
|
||||
if (client->destHost) {
|
||||
if (!ConnectToRFBRepeater(client,client->serverHost,client->serverPort,client->destHost,client->destPort))
|
||||
return FALSE;
|
||||
} else {
|
||||
if (!ConnectToRFBServer(client,client->serverHost,client->serverPort))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialise the VNC connection, including reading the password */
|
||||
|
||||
if (!InitialiseRFBConnection(client))
|
||||
return FALSE;
|
||||
|
||||
client->width=client->si.framebufferWidth;
|
||||
client->height=client->si.framebufferHeight;
|
||||
if (!client->MallocFrameBuffer(client))
|
||||
return FALSE;
|
||||
|
||||
if (!SetFormatAndEncodings(client))
|
||||
return FALSE;
|
||||
|
||||
if (client->updateRect.x < 0) {
|
||||
client->updateRect.x = client->updateRect.y = 0;
|
||||
client->updateRect.w = client->width;
|
||||
client->updateRect.h = client->height;
|
||||
}
|
||||
|
||||
if (client->appData.scaleSetting>1)
|
||||
{
|
||||
if (!SendScaleSetting(client, client->appData.scaleSetting))
|
||||
return FALSE;
|
||||
if (!SendFramebufferUpdateRequest(client,
|
||||
client->updateRect.x / client->appData.scaleSetting,
|
||||
client->updateRect.y / client->appData.scaleSetting,
|
||||
client->updateRect.w / client->appData.scaleSetting,
|
||||
client->updateRect.h / client->appData.scaleSetting,
|
||||
FALSE))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!SendFramebufferUpdateRequest(client,
|
||||
client->updateRect.x, client->updateRect.y,
|
||||
client->updateRect.w, client->updateRect.h,
|
||||
FALSE))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
rfbBool rfbInitClient(rfbClient* client,int* argc,char** argv) {
|
||||
int i,j;
|
||||
|
||||
if(argv && argc && *argc) {
|
||||
if(client->programName==0)
|
||||
client->programName=argv[0];
|
||||
|
||||
for (i = 1; i < *argc; i++) {
|
||||
j = i;
|
||||
if (strcmp(argv[i], "-listen") == 0) {
|
||||
listenForIncomingConnections(client);
|
||||
break;
|
||||
} else if (strcmp(argv[i], "-listennofork") == 0) {
|
||||
listenForIncomingConnectionsNoFork(client, -1);
|
||||
break;
|
||||
} else if (strcmp(argv[i], "-play") == 0) {
|
||||
client->serverPort = -1;
|
||||
j++;
|
||||
} else if (i+1<*argc && strcmp(argv[i], "-encodings") == 0) {
|
||||
client->appData.encodingsString = argv[i+1];
|
||||
j+=2;
|
||||
} else if (i+1<*argc && strcmp(argv[i], "-compress") == 0) {
|
||||
client->appData.compressLevel = atoi(argv[i+1]);
|
||||
j+=2;
|
||||
} else if (i+1<*argc && strcmp(argv[i], "-quality") == 0) {
|
||||
client->appData.qualityLevel = atoi(argv[i+1]);
|
||||
j+=2;
|
||||
} else if (i+1<*argc && strcmp(argv[i], "-scale") == 0) {
|
||||
client->appData.scaleSetting = atoi(argv[i+1]);
|
||||
j+=2;
|
||||
} else if (i+1<*argc && strcmp(argv[i], "-qosdscp") == 0) {
|
||||
client->QoS_DSCP = atoi(argv[i+1]);
|
||||
j+=2;
|
||||
} else if (i+1<*argc && strcmp(argv[i], "-repeaterdest") == 0) {
|
||||
char* colon=strchr(argv[i+1],':');
|
||||
|
||||
if(client->destHost)
|
||||
free(client->destHost);
|
||||
client->destPort = 5900;
|
||||
|
||||
client->destHost = strdup(argv[i+1]);
|
||||
if(client->destHost && colon) {
|
||||
client->destHost[(int)(colon-argv[i+1])] = '\0';
|
||||
client->destPort = atoi(colon+1);
|
||||
}
|
||||
j+=2;
|
||||
} else {
|
||||
char* colon=strrchr(argv[i],':');
|
||||
|
||||
if(client->serverHost)
|
||||
free(client->serverHost);
|
||||
|
||||
if(colon) {
|
||||
client->serverHost = strdup(argv[i]);
|
||||
if(client->serverHost) {
|
||||
client->serverHost[(int)(colon-argv[i])] = '\0';
|
||||
client->serverPort = atoi(colon+1);
|
||||
}
|
||||
} else {
|
||||
client->serverHost = strdup(argv[i]);
|
||||
}
|
||||
if(client->serverPort >= 0 && client->serverPort < 5900)
|
||||
client->serverPort += 5900;
|
||||
}
|
||||
/* purge arguments */
|
||||
if (j>i) {
|
||||
*argc-=j-i;
|
||||
memmove(argv+i,argv+j,(*argc-i)*sizeof(char*));
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!rfbInitConnection(client)) {
|
||||
rfbClientCleanup(client);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void rfbClientCleanup(rfbClient* client) {
|
||||
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < 4; i++ ) {
|
||||
if (client->zlibStreamActive[i] == TRUE ) {
|
||||
if (inflateEnd (&client->zlibStream[i]) != Z_OK &&
|
||||
client->zlibStream[i].msg != NULL)
|
||||
rfbClientLog("inflateEnd: %s\n", client->zlibStream[i].msg);
|
||||
}
|
||||
}
|
||||
|
||||
if ( client->decompStreamInited == TRUE ) {
|
||||
if (inflateEnd (&client->decompStream) != Z_OK &&
|
||||
client->decompStream.msg != NULL)
|
||||
rfbClientLog("inflateEnd: %s\n", client->decompStream.msg );
|
||||
}
|
||||
#endif
|
||||
|
||||
if (client->ultra_buffer)
|
||||
free(client->ultra_buffer);
|
||||
|
||||
if (client->raw_buffer)
|
||||
free(client->raw_buffer);
|
||||
|
||||
FreeTLS(client);
|
||||
|
||||
while (client->clientData) {
|
||||
rfbClientData* next = client->clientData->next;
|
||||
free(client->clientData);
|
||||
client->clientData = next;
|
||||
}
|
||||
|
||||
free(client->vncRec);
|
||||
|
||||
if (client->sock != RFB_INVALID_SOCKET)
|
||||
rfbCloseSocket(client->sock);
|
||||
if (client->listenSock != RFB_INVALID_SOCKET)
|
||||
rfbCloseSocket(client->listenSock);
|
||||
free(client->desktopName);
|
||||
free(client->serverHost);
|
||||
if (client->destHost)
|
||||
free(client->destHost);
|
||||
if (client->clientAuthSchemes)
|
||||
free(client->clientAuthSchemes);
|
||||
|
||||
#ifdef LIBVNCSERVER_HAVE_SASL
|
||||
if (client->saslSecret)
|
||||
free(client->saslSecret);
|
||||
#endif /* LIBVNCSERVER_HAVE_SASL */
|
||||
|
||||
free(client);
|
||||
}
|
Loading…
Reference in New Issue