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