Skip to content

Instantly share code, notes, and snippets.

@fis
Created March 11, 2012 18:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fis/2017454 to your computer and use it in GitHub Desktop.
Save fis/2017454 to your computer and use it in GitHub Desktop.
vnckvm
vnckvm
------
This is a GTK+ program to use the input devices (keyboard, mouse) of one
computer as the input devices of a remote computer, via VNC. Think of it as a
sort of a DIY "Synergy". The main benefit, compared to an actual VNC client, is
significantly improved latency in some cases, as it never actually requests the
screen contents from the remote computer.
I've sometimes used this to avoid key-jamming problems when doing some
same-computer multiplayer things.
This was originally written in 2007 or so, no guarantees it even works any more.
Copyright (c) 2012, Heikki Kallasjoki
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. 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.
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 OWNER 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.
/*
* This is D3DES (V5.09) by Richard Outerbridge with the double and
* triple-length support removed for use in VNC. Also the bytebit[] array
* has been reversed so that the most significant bit in each byte of the
* key is ignored, not the least significant.
*
* These changes are:
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
*
* 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.
*/
/* D3DES (V5.09) -
*
* A portable, public domain, version of the Data Encryption Standard.
*
* Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
* Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
* code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
* Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
* for humouring me on.
*
* Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
* (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
*/
#include "d3des.h"
static void scrunch(unsigned char *, unsigned long *);
static void unscrun(unsigned long *, unsigned char *);
static void desfunc(unsigned long *, unsigned long *);
static void cookey(unsigned long *);
static unsigned long KnL[32] = { 0L };
static unsigned short bytebit[8] = {
01, 02, 04, 010, 020, 040, 0100, 0200 };
static unsigned long bigbyte[24] = {
0x800000L, 0x400000L, 0x200000L, 0x100000L,
0x80000L, 0x40000L, 0x20000L, 0x10000L,
0x8000L, 0x4000L, 0x2000L, 0x1000L,
0x800L, 0x400L, 0x200L, 0x100L,
0x80L, 0x40L, 0x20L, 0x10L,
0x8L, 0x4L, 0x2L, 0x1L };
/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
static unsigned char pc1[56] = {
56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 };
static unsigned char totrot[16] = {
1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };
static unsigned char pc2[48] = {
13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
void deskey(key, edf) /* Thanks to James Gillogly & Phil Karn! */
unsigned char *key;
int edf;
{
register int i, j, l, m, n;
unsigned char pc1m[56], pcr[56];
unsigned long kn[32];
for ( j = 0; j < 56; j++ ) {
l = pc1[j];
m = l & 07;
pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
}
for( i = 0; i < 16; i++ ) {
if( edf == DE1 ) m = (15 - i) << 1;
else m = i << 1;
n = m + 1;
kn[m] = kn[n] = 0L;
for( j = 0; j < 28; j++ ) {
l = j + totrot[i];
if( l < 28 ) pcr[j] = pc1m[l];
else pcr[j] = pc1m[l - 28];
}
for( j = 28; j < 56; j++ ) {
l = j + totrot[i];
if( l < 56 ) pcr[j] = pc1m[l];
else pcr[j] = pc1m[l - 28];
}
for( j = 0; j < 24; j++ ) {
if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j];
}
}
cookey(kn);
return;
}
static void cookey(raw1)
register unsigned long *raw1;
{
register unsigned long *cook, *raw0;
unsigned long dough[32];
register int i;
cook = dough;
for( i = 0; i < 16; i++, raw1++ ) {
raw0 = raw1++;
*cook = (*raw0 & 0x00fc0000L) << 6;
*cook |= (*raw0 & 0x00000fc0L) << 10;
*cook |= (*raw1 & 0x00fc0000L) >> 10;
*cook++ |= (*raw1 & 0x00000fc0L) >> 6;
*cook = (*raw0 & 0x0003f000L) << 12;
*cook |= (*raw0 & 0x0000003fL) << 16;
*cook |= (*raw1 & 0x0003f000L) >> 4;
*cook++ |= (*raw1 & 0x0000003fL);
}
usekey(dough);
return;
}
void cpkey(into)
register unsigned long *into;
{
register unsigned long *from, *endp;
from = KnL, endp = &KnL[32];
while( from < endp ) *into++ = *from++;
return;
}
void usekey(from)
register unsigned long *from;
{
register unsigned long *to, *endp;
to = KnL, endp = &KnL[32];
while( to < endp ) *to++ = *from++;
return;
}
void des(inblock, outblock)
unsigned char *inblock, *outblock;
{
unsigned long work[2];
scrunch(inblock, work);
desfunc(work, KnL);
unscrun(work, outblock);
return;
}
static void scrunch(outof, into)
register unsigned char *outof;
register unsigned long *into;
{
*into = (*outof++ & 0xffL) << 24;
*into |= (*outof++ & 0xffL) << 16;
*into |= (*outof++ & 0xffL) << 8;
*into++ |= (*outof++ & 0xffL);
*into = (*outof++ & 0xffL) << 24;
*into |= (*outof++ & 0xffL) << 16;
*into |= (*outof++ & 0xffL) << 8;
*into |= (*outof & 0xffL);
return;
}
static void unscrun(outof, into)
register unsigned long *outof;
register unsigned char *into;
{
*into++ = (*outof >> 24) & 0xffL;
*into++ = (*outof >> 16) & 0xffL;
*into++ = (*outof >> 8) & 0xffL;
*into++ = *outof++ & 0xffL;
*into++ = (*outof >> 24) & 0xffL;
*into++ = (*outof >> 16) & 0xffL;
*into++ = (*outof >> 8) & 0xffL;
*into = *outof & 0xffL;
return;
}
static unsigned long SP1[64] = {
0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
static unsigned long SP2[64] = {
0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
static unsigned long SP3[64] = {
0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
static unsigned long SP4[64] = {
0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
static unsigned long SP5[64] = {
0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
static unsigned long SP6[64] = {
0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
static unsigned long SP7[64] = {
0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
static unsigned long SP8[64] = {
0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
static void desfunc(block, keys)
register unsigned long *block, *keys;
{
register unsigned long fval, work, right, leftt;
register int round;
leftt = block[0];
right = block[1];
work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
right ^= work;
leftt ^= (work << 4);
work = ((leftt >> 16) ^ right) & 0x0000ffffL;
right ^= work;
leftt ^= (work << 16);
work = ((right >> 2) ^ leftt) & 0x33333333L;
leftt ^= work;
right ^= (work << 2);
work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
leftt ^= work;
right ^= (work << 8);
right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
work = (leftt ^ right) & 0xaaaaaaaaL;
leftt ^= work;
right ^= work;
leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
for( round = 0; round < 8; round++ ) {
work = (right << 28) | (right >> 4);
work ^= *keys++;
fval = SP7[ work & 0x3fL];
fval |= SP5[(work >> 8) & 0x3fL];
fval |= SP3[(work >> 16) & 0x3fL];
fval |= SP1[(work >> 24) & 0x3fL];
work = right ^ *keys++;
fval |= SP8[ work & 0x3fL];
fval |= SP6[(work >> 8) & 0x3fL];
fval |= SP4[(work >> 16) & 0x3fL];
fval |= SP2[(work >> 24) & 0x3fL];
leftt ^= fval;
work = (leftt << 28) | (leftt >> 4);
work ^= *keys++;
fval = SP7[ work & 0x3fL];
fval |= SP5[(work >> 8) & 0x3fL];
fval |= SP3[(work >> 16) & 0x3fL];
fval |= SP1[(work >> 24) & 0x3fL];
work = leftt ^ *keys++;
fval |= SP8[ work & 0x3fL];
fval |= SP6[(work >> 8) & 0x3fL];
fval |= SP4[(work >> 16) & 0x3fL];
fval |= SP2[(work >> 24) & 0x3fL];
right ^= fval;
}
right = (right << 31) | (right >> 1);
work = (leftt ^ right) & 0xaaaaaaaaL;
leftt ^= work;
right ^= work;
leftt = (leftt << 31) | (leftt >> 1);
work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
right ^= work;
leftt ^= (work << 8);
work = ((leftt >> 2) ^ right) & 0x33333333L;
right ^= work;
leftt ^= (work << 2);
work = ((right >> 16) ^ leftt) & 0x0000ffffL;
leftt ^= work;
right ^= (work << 16);
work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
leftt ^= work;
right ^= (work << 4);
*block++ = right;
*block = leftt;
return;
}
/* Validation sets:
*
* Single-length key, single-length plaintext -
* Key : 0123 4567 89ab cdef
* Plain : 0123 4567 89ab cde7
* Cipher : c957 4425 6a5e d31d
*
* Double-length key, single-length plaintext -
* Key : 0123 4567 89ab cdef fedc ba98 7654 3210
* Plain : 0123 4567 89ab cde7
* Cipher : 7f1d 0a77 826b 8aff
*
* Double-length key, double-length plaintext -
* Key : 0123 4567 89ab cdef fedc ba98 7654 3210
* Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
* Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
*
* Triple-length key, single-length plaintext -
* Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
* Plain : 0123 4567 89ab cde7
* Cipher : de0b 7c06 ae5e 0ed5
*
* Triple-length key, double-length plaintext -
* Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
* Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
* Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
*
* d3des V5.0a rwo 9208.07 18:44 Graven Imagery
**********************************************************************/
vnckvm: vnckvm.o d3des.o
gcc -Wall -g $^ -o $@ \
`pkg-config --libs gtk+-2.0`
vnckvm.o: vnckvm.c d3des.h
gcc -c -Wall -g vnckvm.c -o vnckvm.o `pkg-config --cflags gtk+-2.0`
d3des.o: d3des.c d3des.h
gcc -c -Wall -g d3des.c -o d3des.o
clean:
$(RM) vnckvm vnckvm.o d3des.o
/*
* vnckvm.c: GTK+ program to use the input devices (keyboard, mouse) of one
* computer as the input devices of a remote computer, via VNC.
*/
#include <errno.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "d3des.h"
#define INFOTEXT "vnckvm"
#define DEFAULTHOST "example:5900"
#define DEFAULTPORT "5900"
/* #define LOG_EVENTS 1 */
/* VNC-related functions. */
static int vnc_socket = 0;
static int vnc_width = 0, vnc_height = 0;
static int vnc_mx = 0, vnc_my = 0;
static unsigned char vnc_mbuttons = 0;
static int read_n(int s, unsigned char *buf, size_t count) {
while (count) {
ssize_t n = read(s, buf, count);
if (n <= 0) {
if (n == 0) errno = ENODATA;
return 0;
}
buf += n;
count -= n;
}
return 1;
}
static size_t read_32(int s) {
unsigned char buf[4];
if (!read_n(s, buf, 4)) return 0;
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}
static int vnc_connect(char *host, const char *pass) {
int i;
char *port;
struct addrinfo *ai2, *ai, hai;
unsigned char buf[128];
size_t len;
/* Parse arguments. */
if ((port = strchr(host, ':')))
*port++ = 0;
else {
fprintf(stderr, "[vnc] port not specified\n");
return 0;
}
/* Locate the host. */
memset(&hai, sizeof hai, 0);
hai.ai_family = AF_INET;
hai.ai_socktype = SOCK_STREAM;
hai.ai_protocol = IPPROTO_TCP;
if ((i = getaddrinfo(host, port, &hai, &ai)) != 0) {
fprintf(stderr, "[vnc] unable to find \"%s:%s\": %s\n",
host, port,
gai_strerror(i));
return 0;
}
/* Open a socket. */
vnc_socket = -1;
for (ai2 = ai; ai2; ai2 = ai2->ai_next) {
fprintf(stderr, "[vnc] connecting to %s:%s...\n", host, port);
vnc_socket = socket(ai2->ai_family, ai2->ai_socktype, ai2->ai_protocol);
if (vnc_socket == -1) {
perror("[vnc] socket");
continue;
}
i = 1;
setsockopt(vnc_socket, IPPROTO_TCP, TCP_NODELAY, &i, sizeof i);
if (connect(vnc_socket, ai2->ai_addr, ai2->ai_addrlen) == -1) {
perror("[vnc] connect");
close(vnc_socket);
vnc_socket = -1;
continue;
}
}
freeaddrinfo(ai);
if (vnc_socket == -1) return 0;
/* Do the VNC handshakes. */
if (!read_n(vnc_socket, buf, 12)) {
perror("[vnc] read (ProtocolVersion)");
close(vnc_socket);
return 0;
}
// if (memcmp(buf, "RFB 003.007\x0a", 12) != 0) {
if (memcmp(buf, "RFB 003.008\x0a", 12) != 0) {
fprintf(stderr, "[vnc] unsupported ProtocolVersion: %.11s\n", buf);
close(vnc_socket);
return 0;
}
// write(vnc_socket, buf, 12); /* bounce back */
write(vnc_socket, "RFB 003.007\x0a", 12); /* bounce back */
if (!read_n(vnc_socket, buf, 1)) {
perror("[vnc] read (number of security types)");
close(vnc_socket);
return 0;
}
i = buf[0];
if (i == 0) {
size_t len = read_32(vnc_socket);
if (!read_n(vnc_socket, buf, len)) {
buf[len] = 0;
fprintf(stderr, "[vnc] connection failed: %s\n", buf);
} else fprintf(stderr, "[vnc] connection failed: <unknown reason>\n");
close(vnc_socket);
return 0;
}
if (!read_n(vnc_socket, buf, i)) {
perror("[vnc] read (security types)");
close(vnc_socket);
return 0;
}
if (memchr(buf, 1, i)) { /* no authentication is ok */
unsigned char byte = 1;
write(vnc_socket, &byte, 1);
} else if (memchr(buf, 2, i)) { /* vnc password auth */
unsigned char byte = 2;
unsigned char des_key[8];
for (i = 0; i < 8; i++) if (*pass) des_key[i] = *pass++; else des_key[i] = 0;
write(vnc_socket, &byte, 1);
if (!read_n(vnc_socket, buf, 16)) {
perror("[vnc] read (password challenge)");
close(vnc_socket);
return 0;
}
deskey(des_key, EN0);
des(buf, buf); des(buf+8, buf+8);
write(vnc_socket, buf, 16);
if (read_32(vnc_socket) != 0) {
fprintf(stderr, "[vnc] authentication failure\n");
close(vnc_socket);
return 0;
}
} else {
fprintf(stderr, "[vnc] no supported authentication types\n");
close(vnc_socket);
return 0;
}
buf[0] = 1;
write(vnc_socket, buf, 1);
if (!read_n(vnc_socket, buf, 20)) {
perror("[vnc] read (ServerInit)");
close(vnc_socket);
return 0;
}
vnc_width = (buf[0] << 8) | buf[1];
vnc_height = (buf[2] << 8) | buf[3];
vnc_mx = vnc_width/2;
vnc_my = vnc_height/2;
len = read_32(vnc_socket);
if (!read_n(vnc_socket, buf, len)) {
perror("[vnc] read (ServerInit; desktop name)");
close(vnc_socket);
return 0;
}
fprintf(stderr, "[vnc] connected to %s:%s (\"%.*s\"), %dx%d\n",
host, port, (int)len, buf, vnc_width, vnc_height);
/* Let the server know that we support desktop resizing. */
memcpy(buf, "\x02\x00\x00\x01\xff\xff\xff\x21", 8);
write(vnc_socket, buf, 8);
return 1;
}
static void vnc_disconnect(void) {
close(vnc_socket);
vnc_socket = -1;
}
static void vnc_key(int press, guint key) {
static unsigned char msg[8] = {4};
msg[1] = press;
msg[4] = key >> 24;
msg[5] = key >> 16;
msg[6] = key >> 8;
msg[7] = key;
write(vnc_socket, msg, 8);
}
static void vnc_motion(int dx, int dy) {
static unsigned char msg[6] = {5};
vnc_mx += dx;
if (vnc_mx < 0) vnc_mx = 0;
else if (vnc_mx >= vnc_width) vnc_mx = vnc_width-1;
vnc_my += dy;
if (vnc_my < 0) vnc_my = 0;
else if (vnc_my >= vnc_height) vnc_my = vnc_height-1;
#ifdef LOG_EVENTS
fprintf(stderr, "[dbg] delta (%d, %d) pos (%d, %d)\n", dx, dy, vnc_mx, vnc_my);
#endif
msg[1] = vnc_mbuttons;
msg[2] = vnc_mx >> 8;
msg[3] = vnc_mx & 0xff;
msg[4] = vnc_my >> 8;
msg[5] = vnc_my & 0xff;
write(vnc_socket, msg, 6);
}
static void vnc_button(int press, guint button) {
if (press)
vnc_mbuttons |= (1 << (button-1));
else
vnc_mbuttons &= ~(1 << (button-1));
vnc_motion(0, 0);
}
#define INBUFSZ 1024
static gboolean vnc_input(GIOChannel* src, GIOCondition cond, gpointer data) {
gsize i, j, len, tmp, nrects;
static unsigned char buf[INBUFSZ];
fprintf(stderr, "[vnc] THERE IS INPUT!!!\n");
if (g_io_channel_read_chars(src, (gchar*)buf, INBUFSZ, &len, NULL) != G_IO_STATUS_NORMAL) {
fprintf(stderr, "[vnc] error reading input\n");
vnc_disconnect();
return FALSE;
}
for (j = 0; j < len; j++) {
if (buf[j] == 2) continue; /* bell req */
if (buf[j] != 0) {
fprintf(stderr, "[vnc] can't handle msg: %02x\n", buf[j]);
vnc_disconnect();
return FALSE;
}
j += 2; /* skip fbupdate header */
if (j+2 > len) break; /* bad thing, shouldn't happen */
nrects = (buf[j] << 8) | buf[j+1];
j += 2;
/* make sure we have all the rectangles in the buffer for this update */
len -= j;
memmove(buf, buf+j, len);
j = 0;
if (nrects*12 > INBUFSZ) {
fprintf(stderr, "[vnc] buf too small to handle all\n");
vnc_disconnect();
return FALSE;
}
while (len < nrects*12) {
if (g_io_channel_read_chars(src, (gchar*)buf+len, INBUFSZ-len, &tmp, NULL) != G_IO_STATUS_NORMAL) {
fprintf(stderr, "[vnc] error reading further input\n");
vnc_disconnect();
return FALSE;
}
len += tmp;
}
for (i = 0; i < nrects && j+12 <= len; i++, j += 12) {
int wid = (buf[j+4] << 8) | buf[j+5];
int hei = (buf[j+6] << 8) | buf[j+7];
unsigned int enc = (buf[j+8] << 24) | (buf[j+9] << 16) | (buf[j+1] << 8) | buf[j+11];
if (enc == 0xffffff21u) { /* desktop resize */
fprintf(stderr, "[vnc] desktop resize to %dx%d\n", wid, hei);
vnc_width = wid;
vnc_height = hei;
vnc_mx = wid/2;
vnc_my = hei/2;
} else if (enc == 0) { /* raw pixels */
fprintf(stderr, "[vnc] TODO FIXME discard raw\n");
vnc_disconnect();
return FALSE;
} else {
fprintf(stderr, "[vnc] can't handle encoding: %08x\n", enc);
vnc_disconnect();
return FALSE;
}
}
j--; /* compensate for the j++ in the loop */
}
return TRUE;
}
/* GTK+ mess. */
static int connected = 0;
static int grabbed = 0;
static int release_ctrll = 0, release_ctrlr = 0;
static GdkDisplay *grabdisplay = NULL;
static GdkScreen *grabscreen = NULL;
static gint grab_center_x, grab_center_y;
static GtkWidget *hostentry, *pwentry;
static GtkWidget *vncbutton;
static char *vncbuttonmsg[2] = {
"Connect",
"Disconnect"
};
static GtkWidget *grabbutton;
static char *grabbuttonmsg[2] = {
"Grab (ctrl-ctrl-esc to release)",
"Grabbed, ctrl-ctrl-esc to release"
};
static GIOChannel *vncchan = NULL;
static void dograb(GtkWidget *widget, gpointer data) {
GdkWindow *window;
GtkWidget *toplevel;
GdkGrabStatus status;
window = gtk_widget_get_parent_window(widget);
toplevel = gtk_widget_get_toplevel(widget);
grabdisplay = gtk_widget_get_display(toplevel);
grabscreen = gtk_widget_get_screen(toplevel);
if (!window || !grabdisplay || !grabscreen || !toplevel || !GTK_WIDGET_TOPLEVEL(toplevel))
return;
if (!grabbed) {
GdkPixmap *empty;
GdkCursor *pointer;
GdkColor black = {0, 0, 0, 0};
gint x, y;
empty = gdk_pixmap_new(NULL, 1, 1, 1);
pointer = gdk_cursor_new_from_pixmap(empty, empty, &black, &black, 0, 0);
status = gdk_pointer_grab(window, FALSE,
GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_SCROLL_MASK,
window, pointer, GDK_CURRENT_TIME);
if (status != GDK_GRAB_SUCCESS) {
return;
}
status = gdk_keyboard_grab(window, FALSE, GDK_CURRENT_TIME);
if (status != GDK_GRAB_SUCCESS) {
gdk_pointer_ungrab(GDK_CURRENT_TIME);
return;
}
gtk_window_get_position(GTK_WINDOW(toplevel), &grab_center_x, &grab_center_y);
gtk_window_get_size(GTK_WINDOW(toplevel), &x, &y);
grab_center_x += x/2;
grab_center_y += y/2;
gdk_display_warp_pointer(grabdisplay, grabscreen, grab_center_x, grab_center_y);
grabbed = 1;
} else {
gdk_pointer_ungrab(GDK_CURRENT_TIME);
gdk_keyboard_ungrab(GDK_CURRENT_TIME);
grabbed = 0;
}
gtk_button_set_label(GTK_BUTTON(grabbutton), grabbuttonmsg[grabbed]);
}
static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) {
int press;
guint key;
if (!grabbed) return FALSE;
press = (event->type == GDK_KEY_PRESS);
key = event->keyval;
#ifdef LOG_EVENTS
fprintf(stderr, "key_%s: key = %08x\n", press ? "press" : "release", key);
#endif /* LOG_EVENTS */
/* Special handling for the 'release' key combination. */
if (key == GDK_Control_L)
release_ctrll = press;
else if (key == GDK_Control_R)
release_ctrlr = press;
else if (key == GDK_Escape && release_ctrll && release_ctrlr) {
gdk_pointer_ungrab(GDK_CURRENT_TIME);
gdk_keyboard_ungrab(GDK_CURRENT_TIME);
grabbed = 0;
gtk_button_set_label(GTK_BUTTON(grabbutton), grabbuttonmsg[0]);
vnc_key(0, GDK_Control_L);
vnc_key(0, GDK_Control_R);
release_ctrll = release_ctrlr = 0;
return TRUE;
}
vnc_key(press, key);
return TRUE;
}
static gint button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) {
int press;
guint button;
if (!grabbed) return FALSE;
press = (event->type != GDK_BUTTON_RELEASE);
button = event->button;
/* What we get here are buttons 1-3, 8-10. */
#ifdef LOG_EVENTS
fprintf(stderr, "button_%s: button = %u, x = %.2f, y = %.2f, x_root = %.2f, y_root = %.2f\n",
press ? "press" : "release", button,
event->x, event->y, event->x_root, event->y_root);
#endif /* LOG_EVENTS */
if (button > 3) return FALSE;
vnc_button(press, button);
return TRUE;
}
static gint scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data) {
guint button;
if (!grabbed) return FALSE;
/* Scroll directions up/down are the wheel, left/right the back/fwd buttons. */
button = 4 + event->direction;
#ifdef LOG_EVENTS
fprintf(stderr, "scroll: x = %.2f, y = %.2f, dir = %d\n", event->x, event->y, event->direction);
#endif /* LOG_EVENTS */
vnc_button(1, button);
vnc_button(0, button);
return TRUE;
}
static gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) {
gint x, y;
if (!grabbed) return FALSE;
x = (gint)event->x_root - grab_center_x;
y = (gint)event->y_root - grab_center_y;
if (x == 0 && y == 0) return TRUE;
#ifdef LOG_EVENTS
fprintf(stderr, "motion: x = %d, y = %d\n", x, y);
#endif /* LOG_EVENTS */
vnc_motion(x, y);
gdk_display_warp_pointer(grabdisplay, grabscreen, grab_center_x, grab_center_y);
return TRUE;
}
static void doconnect(GtkWidget *widget, gpointer data) {
char *host;
const char *pw;
if (!connected) {
host = strdup(gtk_entry_get_text(GTK_ENTRY(hostentry)));
pw = gtk_entry_get_text(GTK_ENTRY(pwentry));
if (vnc_connect(host, pw)) {
vncchan = g_io_channel_unix_new(vnc_socket);
g_io_channel_set_encoding(vncchan, NULL, NULL);
g_io_add_watch(vncchan, G_IO_IN, vnc_input, NULL);
connected = 1;
}
free(host);
} else {
g_io_channel_unref(vncchan);
vnc_disconnect();
connected = 0;
}
gtk_button_set_label(GTK_BUTTON(vncbutton), vncbuttonmsg[connected]);
}
static void destroy(GtkWidget *widget, gpointer data) {
gtk_main_quit();
}
static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) {
return FALSE;
}
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *table;
GtkWidget *infolabel;
GtkWidget *hostlabel, *pwlabel;
GtkWidget *vnclabel, *grablabel;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(key_event), NULL);
g_signal_connect(G_OBJECT(window), "key_release_event", G_CALLBACK(key_event), NULL);
g_signal_connect(G_OBJECT(window), "button_press_event", G_CALLBACK(button_event), NULL);
g_signal_connect(G_OBJECT(window), "button_release_event", G_CALLBACK(button_event), NULL);
g_signal_connect(G_OBJECT(window), "scroll_event", G_CALLBACK(scroll_event), NULL);
g_signal_connect(G_OBJECT(window), "motion_notify_event", G_CALLBACK(motion_event), NULL);
g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL);
table = gtk_table_new(5, 2, FALSE);
infolabel = gtk_label_new(INFOTEXT);
gtk_table_attach(GTK_TABLE(table), infolabel, 0, 2, 0, 1, GTK_EXPAND, 0, 0, 0);
hostlabel = gtk_label_new("Host:");
gtk_table_attach(GTK_TABLE(table), hostlabel, 0, 1, 1, 2, 0, 0, 0, 0);
hostentry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(hostentry), DEFAULTHOST);
gtk_table_attach(GTK_TABLE(table), hostentry, 1, 2, 1, 2, GTK_FILL, 0, 0, 0);
pwlabel = gtk_label_new("Password:");
gtk_table_attach(GTK_TABLE(table), pwlabel, 0, 1, 2, 3, 0, 0, 0, 0);
pwentry = gtk_entry_new();
gtk_entry_set_visibility(GTK_ENTRY(pwentry), FALSE);
gtk_table_attach(GTK_TABLE(table), pwentry, 1, 2, 2, 3, GTK_FILL, 0, 0, 0);
vnclabel = gtk_label_new("VNC:");
gtk_table_attach(GTK_TABLE(table), vnclabel, 0, 1, 3, 4, 0, 0, 0, 0);
vncbutton = gtk_button_new_with_label(vncbuttonmsg[0]);
g_signal_connect(G_OBJECT(vncbutton), "clicked", G_CALLBACK(doconnect), NULL);
gtk_table_attach(GTK_TABLE(table), vncbutton, 1, 2, 3, 4, GTK_FILL, 0, 0, 0);
grablabel = gtk_label_new("Grab:");
gtk_table_attach(GTK_TABLE(table), grablabel, 0, 1, 4, 5, 0, 0, 0, 0);
grabbutton = gtk_button_new_with_label(grabbuttonmsg[0]);
g_signal_connect(G_OBJECT(grabbutton), "clicked", G_CALLBACK(dograb), NULL);
gtk_table_attach(GTK_TABLE(table), grabbutton, 1, 2, 4, 5, GTK_FILL, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(window), table);
gtk_window_set_default_size(GTK_WINDOW(window), 512, 512);
gtk_widget_show(infolabel);
gtk_widget_show(hostlabel);
gtk_widget_show(hostentry);
gtk_widget_show(pwlabel);
gtk_widget_show(pwentry);
gtk_widget_show(vnclabel);
gtk_widget_show(vncbutton);
gtk_widget_show(grablabel);
gtk_widget_show(grabbutton);
gtk_widget_show(table);
gtk_widget_show(window);
gtk_widget_grab_focus(pwentry);
gtk_main();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment