Compare commits

...

15 Commits

Author SHA1 Message Date
Andri Yngvason ab3dacbfd7 open-h264: Add padding to packet buffer
According to documentation this padding should be there and valgrind will
complain if it isn't.
2022-06-29 16:30:17 +00:00
Andri Yngvason b86a3f295a Free av_frames when done with them 2022-06-29 15:59:56 +00:00
Andri Yngvason 1317e847b6 renderer-egl: Clean up properly on exit 2022-06-29 15:49:57 +00:00
Andri Yngvason 40c4cd7aba main: Add a canary ticker
This should help to notice when something is badly blocking the main loop.
2022-06-29 12:31:27 +00:00
Andri Yngvason cde18272ce Handle all buffered messages in the socket handler
Otherwise, some buffered messages will be left over
2022-06-29 11:49:47 +00:00
Andri Yngvason 8bc25aa700 sockets: Simplify write function 2022-06-29 11:48:50 +00:00
Andri Yngvason bd4c3a7286 Add dtrace probes for pts rects 2022-06-29 11:47:04 +00:00
Andri Yngvason 34ad07b267 sockets: Replace select() with poll()
select() has been deprecated for a very long time and is considered harmful
2022-06-05 13:54:12 +00:00
Andri Yngvason fe239b3e54 Remove useless feature test macros 2022-06-05 13:28:24 +00:00
Andri Yngvason b7d314439f Move SetFormatAndEncodings back into rfbproto.c 2022-06-05 13:23:18 +00:00
Andri Yngvason 68ff2eb4cb Remove dead code 2022-06-05 13:23:18 +00:00
Andri Yngvason dc18278961 README: Remove libvncclient dependency 2022-06-05 12:46:41 +00:00
Andri Yngvason 33d13e4b4f Dispatch events while waiting for server data
Blocking the event loop is very bad
2022-06-05 12:42:57 +00:00
Andri Yngvason 60574ba34b Merge libvncclient into the project 2022-05-31 21:18:34 +00:00
Andri Yngvason 9a61d8b159 .gitignore: Add .clang_complete 2022-05-31 20:20:16 +00:00
41 changed files with 13666 additions and 227 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
build*
subprojects
.clang_complete

340
COPYING.GPL 100644
View File

@ -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.

View File

@ -6,7 +6,6 @@ Expect bugs and missing features.
## Runtime Dependencies
* aml
* libvncclient
* libwayland
* libxkbcommon
* pixman
@ -19,19 +18,13 @@ Expect bugs and missing features.
* wayland-protocols
## Building & Running
At the time of writing, an unreleased version of libvncclient is required. So,
you must either use bleeding edge git based packages for the project, or build
libvncclient as a subproject.
```
git clone https://github.com/any1/aml.git
git clone https://github.com/LibVNC/libvncserver.git
git clone https://github.com/any1/wlvncc.git
mkdir wlvncc/subprojects
cd wlvncc/subprojects
ln -s ../../aml .
ln -s ../../libvncserver .
cd -
meson build

46
include/crypto.h 100644
View File

@ -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

View File

@ -0,0 +1,786 @@
#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);
extern void ReadToBuffer(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);
/**
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 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);
/* 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);
/**
* 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

39
include/sasl.h 100644
View File

@ -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 */

32
include/sockets.h 100644
View File

@ -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 */

56
include/tls.h 100644
View File

@ -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 */

534
include/turbojpeg.h 100644
View File

@ -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

31
include/usdt.h 100644
View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2020 Andri Yngvason
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "config.h"
#ifdef HAVE_USDT
#include <sys/sdt.h>
#else
#define DTRACE_PROBE(...)
#define DTRACE_PROBE1(...)
#define DTRACE_PROBE2(...)
#define DTRACE_PROBE3(...)
#define DTRACE_PROBE4(...)
#define DTRACE_PROBE5(...)
#define DTRACE_PROBE6(...)
#endif

View File

@ -47,12 +47,15 @@ struct vnc_client {
void* userdata;
struct pixman_region16 damage;
bool handler_lock;
};
struct vnc_client* vnc_client_create(void);
void vnc_client_destroy(struct vnc_client* self);
int vnc_client_connect(struct vnc_client* self, const char* address, int port);
int vnc_client_init(struct vnc_client* self);
int vnc_client_set_pixel_format(struct vnc_client* self, uint32_t format);

View File

@ -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')
@ -73,7 +68,6 @@ sources = [
'src/pointer.c',
'src/keyboard.c',
'src/vnc.c',
'src/vnc_encodings.c',
'src/strlcpy.c',
'src/evdev-to-qnum.c',
'src/pixels.c',
@ -82,6 +76,11 @@ sources = [
'src/renderer-egl.c',
'src/buffer.c',
'src/open-h264.c',
'src/cursor.c',
'src/rfbproto.c',
'src/sockets.c',
'src/vncviewer.c',
'src/turbojpeg.c',
]
dependencies = [
@ -98,7 +97,6 @@ dependencies = [
glesv2,
lavc,
lavu,
libvncclient,
client_protos,
]
@ -106,6 +104,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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

178
src/cursor.c 100644
View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,191 @@
/*
* 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"
#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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -45,6 +45,10 @@
#include "renderer.h"
#include "renderer-egl.h"
#include "linux-dmabuf-unstable-v1.h"
#include "time-util.h"
#define CANARY_TICK_PERIOD INT64_C(100000) // us
#define CANARY_LETHALITY_LEVEL INT64_C(8000) // us
struct window {
struct wl_surface* wl_surface;
@ -70,6 +74,7 @@ static struct wl_list seats;
struct pointer_collection* pointers;
struct keyboard_collection* keyboards;
static int drm_fd = -1;
static uint64_t last_canary_tick;
static bool have_egl = false;
@ -670,6 +675,43 @@ failure:
return -1;
}
static void on_canary_tick(void* obj)
{
(void)obj;
uint64_t t = gettime_us();
int64_t dt = t - last_canary_tick;
last_canary_tick = t;
int64_t delay = dt - CANARY_TICK_PERIOD;
// Early ticks are just a result of late ticks...
if (delay < CANARY_LETHALITY_LEVEL)
return;
fprintf(stderr, "WARNING: Long delays observed (%"PRIi64"). Something is blocking the main loop\n",
delay);
}
static void create_canary_ticker(void)
{
last_canary_tick = gettime_us();
struct aml* aml = aml_get_default();
struct aml_ticker* ticker = aml_ticker_new(CANARY_TICK_PERIOD / 1000ULL,
on_canary_tick, NULL, NULL);
aml_start(aml, ticker);
aml_unref(ticker);
}
void run_main_loop_once(void)
{
struct aml* aml = aml_get_default();
wl_display_flush(wl_display);
aml_poll(aml, -1);
aml_dispatch(aml);
}
static int usage(int r)
{
fprintf(r ? stderr : stdout, "\
@ -842,16 +884,20 @@ int main(int argc, char* argv[])
if (init_vnc_client_handler(vnc) < 0)
goto vnc_setup_failure;
if (vnc_client_init(vnc) < 0) {
fprintf(stderr, "Failed to connect to server\n");
goto vnc_setup_failure;
}
pointers->userdata = vnc;
keyboards->userdata = vnc;
wl_display_dispatch(wl_display);
while (do_run) {
wl_display_flush(wl_display);
aml_poll(aml, -1);
aml_dispatch(aml);
}
create_canary_ticker();
while (do_run)
run_main_loop_once();
rc = 0;
if (window)

View File

@ -234,7 +234,7 @@ struct AVFrame* open_h264_decode_rect(struct open_h264* self,
if (!context)
return NULL;
char* data = malloc(length);
char* data = calloc(1, length + AV_INPUT_BUFFER_PADDING_SIZE);
if (!data)
return NULL;

View File

@ -222,7 +222,10 @@ void egl_finish(void)
glDeleteProgram(shader_program_ext);
if (shader_program)
glDeleteProgram(shader_program);
eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
eglDestroyContext(egl_display, egl_context);
eglTerminate(egl_display);
}
static inline void append_attr(EGLint* dst, int* i, EGLint name, EGLint value)

2502
src/rfbproto.c 100644

File diff suppressed because it is too large Load Diff

557
src/sasl.c 100644
View File

@ -0,0 +1,557 @@
/*
* 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.
*/
#include <errno.h>
#include "rfb/rfbclient.h"
#include "sockets.h"
#include "sasl.h"
#include "tls.h"
/*
* 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;
}

464
src/sockets.c 100644
View File

@ -0,0 +1,464 @@
/*
* Copyright (C) 2022 Andri Yngvasin <andri@yngvason.is>
* 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.
*/
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/param.h>
#include <poll.h>
#include "rfb/rfbclient.h"
#include "sockets.h"
#include "tls.h"
#include "sasl.h"
void run_main_loop_once(void);
rfbBool errorMessageOnReadFailure = TRUE;
void ReadToBuffer(rfbClient* client) {
if (client->buffered == RFB_BUF_SIZE)
return;
ssize_t size;
#if defined(LIBVNCSERVER_HAVE_GNUTLS) || defined(LIBVNCSERVER_HAVE_LIBSSL)
if (client->tlsSession) {
size = ReadFromTLS(client, client->buf + client->buffered,
RFB_BUF_SIZE - client->buffered);
} else
#endif
#ifdef LIBVNCSERVER_HAVE_SASL
if (client->saslconn) {
size = ReadFromSASL(client, client->buf + client->buffered,
RFB_BUF_SIZE - client->buffered);
} else
#endif
{
size = recv(client->sock, client->buf + client->buffered,
RFB_BUF_SIZE - client->buffered, MSG_DONTWAIT);
}
if (size > 0)
client->buffered += size;
}
rfbBool ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
{
if (!out)
return FALSE;
while (n != 0) {
while (n != 0 && client->buffered == 0) {
run_main_loop_once();
ReadToBuffer(client);
}
unsigned int size = MIN(client->buffered, n);
memcpy(out, client->buf, size);
client->buffered -= size;
memmove(client->buf, client->buf + size, client->buffered);
out += size;
n -= size;
}
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)
{
struct pollfd 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 */
// TODO: Dispatch events while waiting
while (i < n) {
j = write(client->sock, obuf + i, (n - i));
if (j > 0) {
i += j;
continue;
}
if (j == 0) {
rfbClientLog("write failed\n");
return FALSE;
}
if (errno != EWOULDBLOCK && errno != EAGAIN) {
rfbClientErr("write\n");
return FALSE;
}
fds.fd = client->sock;
fds.events = POLLOUT;
if (poll(&fds, 1, -1) <= 0) {
rfbClientErr("poll\n");
return FALSE;
}
}
return TRUE;
}
static rfbBool WaitForConnected(int socket, unsigned int secs)
{
struct pollfd fds = {
.fd = socket,
.events = POLLIN | POLLOUT | POLLERR | POLLHUP,
};
if (poll(&fds, 1, secs * 1000) != 1)
return FALSE;
int so_error = 0;
socklen_t len = sizeof(so_error);
getsockopt(socket, SOL_SOCKET, SO_ERROR, &so_error, &len);
return so_error == 0 ? TRUE : 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;
}
/*
* 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);
}

583
src/tls_gnutls.c 100644
View File

@ -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 */

67
src/tls_none.c 100644
View File

@ -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 */

685
src/tls_openssl.c 100644
View File

@ -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 */

858
src/turbojpeg.c 100644
View File

@ -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);
}

View File

@ -21,12 +21,13 @@
#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"
#include "usdt.h"
#define RFB_ENCODING_OPEN_H264 50
#define RFB_ENCODING_PTS -1000
@ -36,8 +37,6 @@
extern const unsigned short code_map_linux_to_qnum[];
extern const unsigned int code_map_linux_to_qnum_len;
rfbBool vnc_client_set_format_and_encodings(rfbClient* client);
static uint64_t vnc_client_htonll(uint64_t x)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
@ -47,6 +46,21 @@ static uint64_t vnc_client_htonll(uint64_t x)
#endif
}
static bool vnc_client_lock_handler(struct vnc_client* self)
{
if (self->handler_lock)
return false;
self->handler_lock = true;
return true;
}
static void vnc_client_unlock_handler(struct vnc_client* self)
{
assert(self->handler_lock);
self->handler_lock = false;
}
static rfbBool vnc_client_alloc_fb(rfbClient* client)
{
struct vnc_client* self = rfbClientGetClientData(client, NULL);
@ -74,6 +88,7 @@ static void vnc_client_clear_av_frames(struct vnc_client* self)
{
for (int i = 0; i < self->n_av_frames; ++i) {
av_frame_unref(self->av_frames[i]->frame);
av_frame_free(&self->av_frames[i]->frame);
free(self->av_frames[i]);
}
self->n_av_frames = 0;
@ -84,6 +99,8 @@ static void vnc_client_finish_update(rfbClient* client)
struct vnc_client* self = rfbClientGetClientData(client, NULL);
assert(self);
DTRACE_PROBE2(wlvncc, vnc_client_finish_update, client, self->pts);
self->update_fb(self);
self->pts = NO_PTS;
@ -125,6 +142,7 @@ static rfbBool vnc_client_handle_open_h264_rect(rfbClient* client,
struct vnc_av_frame* f = calloc(1, sizeof(*f));
if (!f) {
av_frame_unref(frame);
av_frame_free(&frame);
return false;
}
@ -165,6 +183,8 @@ static rfbBool vnc_client_handle_pts_rect(rfbClient* client,
self->pts = vnc_client_htonll(pts_msg);
DTRACE_PROBE1(wlvncc, vnc_client_handle_pts_rect, self->pts);
return TRUE;
}
@ -228,20 +248,27 @@ int vnc_client_connect(struct vnc_client* self, const char* address, int port)
{
rfbClient* client = self->client;
if (!ConnectToRFBServer(client, address, port))
return -1;
return ConnectToRFBServer(client, address, port) ? 0 : -1;
}
int vnc_client_init(struct vnc_client* self)
{
int rc = -1;
rfbClient* client = self->client;
vnc_client_lock_handler(self);
if (!InitialiseRFBConnection(client))
return -1;
goto failure;
client->width = client->si.framebufferWidth;
client->height = client->si.framebufferHeight;
if (!client->MallocFrameBuffer(client))
return -1;
goto failure;
if (!vnc_client_set_format_and_encodings(client))
return -1;
if (!SetFormatAndEncodings(client))
goto failure;
if (client->updateRect.x < 0) {
client->updateRect.x = client->updateRect.y = 0;
@ -253,12 +280,15 @@ int vnc_client_connect(struct vnc_client* self, const char* address, int port)
client->updateRect.x, client->updateRect.y,
client->updateRect.w, client->updateRect.h,
FALSE))
return -1;
goto failure;
SendIncrementalFramebufferUpdateRequest(client);
SendIncrementalFramebufferUpdateRequest(client);
return 0;
rc = 0;
failure:
vnc_client_unlock_handler(self);
return rc;
}
int vnc_client_set_pixel_format(struct vnc_client* self, uint32_t format)
@ -335,7 +365,20 @@ const char* vnc_client_get_desktop_name(const struct vnc_client* self)
int vnc_client_process(struct vnc_client* self)
{
return HandleRFBServerMessage(self->client) ? 0 : -1;
ReadToBuffer(self->client);
if (!vnc_client_lock_handler(self))
return 0;
int rc;
while (self->client->buffered > 0) {
rc = HandleRFBServerMessage(self->client) ? 0 : -1;
if (rc < 0)
break;
}
vnc_client_unlock_handler(self);
return rc;
}
void vnc_client_send_pointer_event(struct vnc_client* self, int x, int y,

View File

@ -1,185 +0,0 @@
/*
* Copyright (C) 2022 Andri Yngvason. All Rights Reserved.
* Copyright (C) 2000-2002 Constantin 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.
*/
#include <rfb/rfbclient.h>
#include <assert.h>
rfbBool vnc_client_set_format_and_encodings(rfbClient* client)
{
assert(client->appData.encodingsString);
rfbSetPixelFormatMsg spf;
union {
char bytes[sz_rfbSetEncodingsMsg + MAX_ENCODINGS*4];
rfbSetEncodingsMsg msg;
} buf;
rfbSetEncodingsMsg *se = &buf.msg;
uint32_t *encs = (uint32_t *)(&buf.bytes[sz_rfbSetEncodingsMsg]);
int len = 0;
rfbBool requestCompressLevel = FALSE;
rfbBool requestQualityLevel = FALSE;
rfbBool requestLastRectEncoding = FALSE;
if (!SupportsClient2Server(client, rfbSetPixelFormat))
return TRUE;
spf.type = rfbSetPixelFormat;
spf.pad1 = 0;
spf.pad2 = 0;
spf.format = client->format;
spf.format.redMax = rfbClientSwap16IfLE(spf.format.redMax);
spf.format.greenMax = rfbClientSwap16IfLE(spf.format.greenMax);
spf.format.blueMax = rfbClientSwap16IfLE(spf.format.blueMax);
if (!WriteToRFBServer(client, (char *)&spf, sz_rfbSetPixelFormatMsg))
return FALSE;
if (!SupportsClient2Server(client, rfbSetEncodings))
return TRUE;
se->type = rfbSetEncodings;
se->pad = 0;
se->nEncodings = 0;
const char *encStr = client->appData.encodingsString;
int encStrLen;
do {
const char *nextEncStr = strchr(encStr, ',');
if (nextEncStr) {
encStrLen = nextEncStr - encStr;
nextEncStr++;
} else {
encStrLen = strlen(encStr);
}
if (strncasecmp(encStr,"raw",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRaw);
} else if (strncasecmp(encStr,"copyrect",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCopyRect);
#ifdef LIBVNCSERVER_HAVE_LIBZ
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
} else if (strncasecmp(encStr,"tight",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingTight);
requestLastRectEncoding = TRUE;
if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9)
requestCompressLevel = TRUE;
if (client->appData.enableJPEG)
requestQualityLevel = TRUE;
#endif
#endif
} else if (strncasecmp(encStr,"hextile",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingHextile);
#ifdef LIBVNCSERVER_HAVE_LIBZ
} else if (strncasecmp(encStr,"zlib",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZlib);
if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9)
requestCompressLevel = TRUE;
} else if (strncasecmp(encStr,"zlibhex",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZlibHex);
if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9)
requestCompressLevel = TRUE;
} else if (strncasecmp(encStr,"trle",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingTRLE);
} else if (strncasecmp(encStr,"zrle",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZRLE);
} else if (strncasecmp(encStr,"zywrle",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZYWRLE);
requestQualityLevel = TRUE;
#endif
} else if ((strncasecmp(encStr,"ultra",encStrLen) == 0) || (strncasecmp(encStr,"ultrazip",encStrLen) == 0)) {
/* There are 2 encodings used in 'ultra' */
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingUltra);
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingUltraZip);
} else if (strncasecmp(encStr,"corre",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCoRRE);
} else if (strncasecmp(encStr,"rre",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRRE);
} else if (strncasecmp(encStr,"open-h264",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(50);
} else {
rfbClientLog("Unknown encoding '%.*s'\n",encStrLen,encStr);
}
encStr = nextEncStr;
} while (encStr && se->nEncodings < MAX_ENCODINGS);
if (se->nEncodings < MAX_ENCODINGS && requestCompressLevel) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(client->appData.compressLevel +
rfbEncodingCompressLevel0);
}
if (se->nEncodings < MAX_ENCODINGS && requestQualityLevel) {
if (client->appData.qualityLevel < 0 || client->appData.qualityLevel > 9)
client->appData.qualityLevel = 5;
encs[se->nEncodings++] = rfbClientSwap32IfLE(client->appData.qualityLevel +
rfbEncodingQualityLevel0);
}
/* Remote Cursor Support (local to viewer) */
if (client->appData.useRemoteCursor) {
if (se->nEncodings < MAX_ENCODINGS)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingXCursor);
if (se->nEncodings < MAX_ENCODINGS)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRichCursor);
if (se->nEncodings < MAX_ENCODINGS)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingPointerPos);
}
/* Keyboard State Encodings */
if (se->nEncodings < MAX_ENCODINGS)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingKeyboardLedState);
/* New Frame Buffer Size */
if (se->nEncodings < MAX_ENCODINGS && client->canHandleNewFBSize)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingNewFBSize);
/* Last Rect */
if (se->nEncodings < MAX_ENCODINGS && requestLastRectEncoding)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingLastRect);
/* Server Capabilities */
if (se->nEncodings < MAX_ENCODINGS)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingSupportedMessages);
if (se->nEncodings < MAX_ENCODINGS)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingSupportedEncodings);
if (se->nEncodings < MAX_ENCODINGS)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingServerIdentity);
/* xvp */
if (se->nEncodings < MAX_ENCODINGS)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingXvp);
if (se->nEncodings < MAX_ENCODINGS)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingQemuExtendedKeyEvent);
/* pts */
if (se->nEncodings < MAX_ENCODINGS)
encs[se->nEncodings++] = rfbClientSwap32IfLE(-1000);
len = sz_rfbSetEncodingsMsg + se->nEncodings * 4;
se->nEncodings = rfbClientSwap16IfLE(se->nEncodings);
return WriteToRFBServer(client, buf.bytes, len);
}

393
src/vncviewer.c 100644
View File

@ -0,0 +1,393 @@
/*
* 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.
*/
#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;
}
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);
}