Giter VIP home page Giter VIP logo

guacamole-lite's People

Contributors

dependabot[bot] avatar dnapier avatar dngadelha avatar fredericosilva avatar luiso1979 avatar thelamer avatar vadimpronin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

guacamole-lite's Issues

Express issues?

I am attempting to run the client using express but it seems that I am missing something. I am following your example to the letter and cannot get any connections established. Ive got an http server running on 8080; however, its not accepting connections. Can you help?

wss Error

Hi,

I have the following code --

I run this on the remote machine which I want to access using - VNC

#!/usr/bin/env node

const GuacamoleLite = require('guacamole-lite');

const websocketOptions = {
    port: 8080 // we will accept connections to this port
};

const guacdOptions = {
    port: 4822 // port of guacd
};

const clientOptions = {
    log: {
        level: 'VERBOSE'
    },
    crypt: {
        cypher: 'AES-256-CBC',
        key: 'MySuperSecretKeyForParamsToken12'
    }
};

const guacServer = new GuacamoleLite(websocketOptions, guacdOptions, clientOptions);

And then

I try to connect to it using below code

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Page Title</title>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <link rel="stylesheet" href="">
        <style></style>

        <!-- Guacamole -->
        <script type="text/javascript"
            src="https://unpkg.com/browse/[email protected]/dist/guacamole-common.js"></script>
    </head>
    
    
    <body>
    
        <!-- Display -->
        <div id="display"></div>
    
        <!-- Init -->
        <script type="text/javascript"> /* <![CDATA[ */
    
            // Get display div from document
            var display = document.getElementById("display");
    
            // Instantiate client, using an HTTP tunnel for communications.
            var guac = new Guacamole.Client(
                new Guacamole.WebSocketTunnel("ws://<PUBLIC_IP_OF_MACHINE>:8080")
            );
    
            // Add client to display div
            display.appendChild(guac.getDisplay().getElement());
            
            // Error handler
            guac.onerror = function(error) {
                console.log("Error :: ", error);
                alert(error);
            };
    
            // Connect
           guac.connect('token=<generated_token>');
                                
    
            // Disconnect on close
            window.onunload = function() {
                guac.disconnect();
            }
    
        /* ]]> */ </script>


        <!-- Init -->
        <script type="text/javascript"> /* <![CDATA[ */

            // Mouse
            var mouse = new Guacamole.Mouse(guac.getDisplay().getElement());

            mouse.onmousedown = 
            mouse.onmouseup   =
            mouse.onmousemove = function(mouseState) {
                guac.sendMouseState(mouseState);
            };

            // Keyboard
            var keyboard = new Guacamole.Keyboard(document);

            keyboard.onkeydown = function (keysym) {
                guac.sendKeyEvent(1, keysym);
            };

            keyboard.onkeyup = function (keysym) {
                guac.sendKeyEvent(0, keysym);
            };

        /* ]]> */ </script>
    
    </body>

</html>

In above code - <PUBLIC_IP_OF_MACHINE> - this is public IP of the machine which I want to access using VNC
and - <generated_token> - this is a token generated using the below script ---

const crypto = require('crypto');

const clientOptions = {
    cypher: 'AES-256-CBC',
    key: 'MySuperSecretKeyForParamsToken12'
}

const encrypt = (value) => {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv(clientOptions.cypher, clientOptions.key, iv);

    let crypted = cipher.update(JSON.stringify(value), 'utf8', 'base64');
    crypted += cipher.final('base64');

    const data = {
        iv: iv.toString('base64'),
        value: crypted
    };

    return new Buffer(JSON.stringify(data)).toString('base64');
};

console.log("Token : " + encrypt({
    "connection": {
        "type": "vnc",
        "settings": {
            "hostname": "<PUBLIC_IP_OF_MACHINE>",
            "port": "5900",
            "username": "pawan",
            "password": "pawan"           
        }
    }
}));

It works when -- above index.html file is served over http.

When I serve the above - index.html file over https and modify
this

            var guac = new Guacamole.Client(
                new Guacamole.WebSocketTunnel("ws://<PUBLIC_IP_OF_MACHINE>:8080")
            );

as

            var guac = new Guacamole.Client(
                new Guacamole.WebSocketTunnel("ws://<PUBLIC_IP_OF_MACHINE>:8080")
            );

I get the below error in Browser console -

wss -- guacamole-common.js:13108 WebSocket connection to 'wss://<PUBLIC_IP_OF_MACHINE>:8080/?token=<token>' failed: [email protected]:[email protected]:3336(anonymous)@?userId=9had87:50

Any help is much appreciated.

No typescritp support

In order to make guacamole more TS friendly we've created the following declaration file based on how the guacamole-lite app has been built.

// guacamole-lite.d.ts

// See: https://stackoverflow.com/a/49936686/7931540
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends Array<infer U>
    ? Array<DeepPartial<U>>
    : T[P] extends ReadonlyArray<infer U>
    ? ReadonlyArray<DeepPartial<U>>
    : DeepPartial<T[P]>;
};

declare module "guacamole-lite" {
  // See packages/client/models/guacamole.ts for other guacamole related types which should be served by "guacamole-lite"

  /**
   * This should have next type structure: https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback [1]
   * But guacamole-lite is using a really old version(1.1.2) of ws(now 8.13.0)
   */
  export interface WsServerOptions {
    port?: number;
  }

  /*
   * This configuration object is used inside the guacamole-lite server to establish the connection with the guacd using the following overload of `Net.connect`
   * function connect(port: number, host?: string, connectionListener?: () => void): Socket;
   * See: https://github.com/vadimpronin/guacamole-lite/blob/d2f4ce1ec1847781ca3667066c9e286624d8e432/lib/GuacdClient.js#L23
   */
  export interface GuacdConnectOptions {
    port?: number;
    host?: string;
  }

  enum GuacamoleLogLevel {
    QUIET = 0,
    ERRORS = 10,
    NORMAL = 20,
    VERBOSE = 30,
    DEBUG = 40,
  }

  /**
   * This configuration object comes from this lines of code used inside the Server.js
   * See: https://github.com/vadimpronin/guacamole-lite/blob/d2f4ce1ec1847781ca3667066c9e286624d8e432/lib/Server.js#L34-L107
   */
  interface __GuacamoleClientOptions {
    maxInactivityTime: number;
    log: {
      level: GuacamoleLogLevel;
      stdLog: typeof console.log;
      errorLog: typeof console.error;
    };

    crypt: {
      cypher: string;
    };

    connectionDefaultSettings: {
      rdp: {
        args: string;
        port: string;
        width: number;
        height: number;
        dpi: number;
      };
      vnc: {
        args: string;
        port: string;
        width: number;
        height: number;
        dpi: number;
      };
      ssh: {
        args: string;
        port: number;
        width: number;
        height: number;
        dpi: number;
      };
      telnet: {
        args: string;
        port: number;
        width: number;
        height: number;
        dpi: number;
      };
    };

    allowedUnencryptedConnectionSettings: {
      rdp: string[];
      vnc: string[];
      ssh: string[];
      telnet: string[];
    };
  }

  export type GuacamoleClientOptions = DeepPartial<__GuacamoleClientOptions> & {
    crypt: {
      key: string;
    };
  };

  export default class GuacamoleLite {
    constructor(
      websocketOptions: WsServerOptions,
      guacdOptions: GuacdConnectOptions,
      clientOptions: GuacamoleClientOptions
    );
  }
}

Decrypt method in Crypt.js fails

I was always getting a failure in the Decrypt method due to character issues. I had to replace one line to get it to work.

This:
let encoded = JSON.parse(this.constructor.base64decode(encodedString));

Became this:
let tempJSON = this.constructor.base64decode(encodedString) let encoded = JSON.parse(tempJSON.substr(0, tempJSON.indexOf('}')+1));

Linux machine renders bottom up

Greetings!
I am working with Ubuntu 22.04 with xrdp on it with security type changed to rdp.
And my gucomole settings are set to RDP. This makes a successful connect but client screen renders bottom up in every resizing action. Even application inside machine renders in bottom up.
How to fix this ?

How to use guacamole-lite from the frontend ?

I'm trying to use this from my angular app , running on port 4200 in order to connect to an external desktop running on a given IP address. Here's the code I could achieve for that purpose so far :

import { Component, OnInit } from '@angular/core';
import { RemoteDesktopManager } from '@illgrenoble/ngx-remote-desktop';
import { WebSocketTunnel } from '@illgrenoble/guacamole-common-js';
import { Guacamole } from 'guacamole-common-js';
import { GuacamoleLite } from 'guacamole-lite';
  @Component({
   selector: 'app-rdp',
   templateUrl: './rdp.component.html',
   styleUrls: ['./rdp.component.scss']
   })
  export class RDPComponent implements OnInit {

  private manager: RemoteDesktopManager;
  ngOnInit() {
    // Setup tunnel. The tunnel can be either: WebsocketTunnel, HTTPTunnel or ChainedTunnel
    const tunnel = new WebSocketTunnel('ws://localhost:4200');
    const client = new Guacamole.Client(tunnel);
    document.body.appendChild(client.getDisplay().getElement());
    /**
     *  Create an instance of the remote desktop manager by 
     *  passing in the tunnel and parameters
     */
    this.manager = new RemoteDesktopManager(tunnel);
          // URL parameters (image, audio and other query parameters you want to send to the tunnel.)
    const parameters = {
        ip: '120.160.10.149',
        port : 5000,
        type: 'rdp',
        image: 'image/png',
        width: window.screen.width,
        height: window.screen.height,
        username:'username',
        password :'0000'

    };
    this.manager.connect(parameters);
    console.log('tunnel state'+this.manager.getState());
    console.log('Disconnected state'+RemoteDesktopManager.STATE.DISCONNECTED);
}

}

In my console I keep getting this error :
eror

I'm new to this and I don't know what am I missing . any help provided will be good. thank you

the desktop jam

When I copy text on the desktop of the RDP protocol, the desktop will jam.Have you ever met such a problem?Thank you!

Send error code and message back to guacamole-common-js

I am currently using nodejs and gaucamole lite but i would like to send an error back to the client / browser where I initiate the connection from guacamole-common-js?

How can i send an error code / code and message back to the client?

Audio input unsupported

Hello,

I got this error on log. I am using guacamole-lite v0.7.1 and guacamole-common.js v1.4.0.

 [Connection 2]  <<<W2G< 5.audio,1.1,31.audio/L16;rate=44100,channels=2;***
 [Connection 2]  >>>G2W> 3.ack,1.1,23.Audio input unsupported,3.256;4.sync,11.10696423475;###

I added GUAC_AUDIO parameter (GUAC_AUDIO=audio/L8&GUAC_AUDIO=audio/L16) on token. So, output audio works.
Does anyone know how to authorize access to the microphone?

Any help would be appreciated.

Guacamole protocol violation. Perhaps the version of guacamole-client is incompatible with this version of guacd?

I am having above error when connecting to gucamole .I am trying to connect using vnc protcol and flowing is my setup

`const GuacamoleLite = require('guacamole-lite');
 
const websocketOptions = {
    port: 8080 // we will accept connections to this port 
};
 
const guacdOptions = {
    port: 4822 // port of guacd 
};
 
const clientOptions = {
    crypt: {
        cypher: 'AES-256-CBC',
        key: 'MySuperSecretKeyForParamsToken12'
    }
};
 
const guacServer = new GuacamoleLite(websocketOptions, guacdOptions, clientOptions); `

and for web service

var express = require('express');
var app = express();
app.use('/', express.static(__dirname + '/public')); // โ† adjust
var fs = require("fs");
var url = require('url');


app.get('/control', function (req, res) {
   var id = req.query.username;
   res.sendfile("guac_control.html");
})


app.get('/listUsers', function (req, res) {
   var id = req.query.username;

	/*{
    	"connection": {
        "type": "rdp",
        "settings": {
            "hostname": "10.0.0.12",
            "username": "Administrator",
            "password": "pAsSwOrD",
            "enable-drive": true,
            "create-drive-path": true,
            "security": "any",
            "ignore-cert": true,
            "enable-wallpaper": false
        }
    }
}
     config.setProtocol("vnc");
	        config.setParameter("hostname", UserIP);
	        config.setParameter("port", UserPort);
	        config.setParameter("password", "23213");
		 
	
	      //config.setParameter("reverse-connect", "true");
	       config.setParameter("password", "123123");
	        config.setParameter("color-depth", "32");
	       // config.setParameter("encodings", "zrle");
	        //nearly 5 mins
	        //config.setParameter("listen-timeout", "50000");
*/
   var clientConnection= {
    	connection : {
         type: "vnc",
         settings : {
             hostname: "127.0.0.1",
	     username :  "root",
             password : "root123"
        }
    }
}
    
   res.send(encrypt(clientConnection));
})

const crypto = require('crypto');

const clientOptions = {
    cypher: 'AES-256-CBC',
    key: 'MySuperSecretKeyForParamsToken12'
}

const encrypt = (value) => {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv(clientOptions.cypher, clientOptions.key, iv);

    let crypted = cipher.update(JSON.stringify(value), 'utf8', 'base64');
    crypted += cipher.final('base64');

    const data = {
        iv: iv.toString('base64'),
        value: crypted
    };

    return new Buffer(JSON.stringify(data)).toString('base64');
};

var server = app.listen(8081, function () {

  var host = server.address().address
  var port = server.address().port

  console.log("web at http://%s:%s", host, port)

})`

And following are the logs

ay 25 14:47:00 ubuntu guacd[32881]: Protocol "vnc" selected May 25 14:47:00 ubuntu guacd[32881]: Guacamole protocol violation. Perhaps the version of guacamole-client is incompatible with this version of guacd? May 25 14:47:00 ubuntu guacd[32881]: Error reading "connect": Instruction read did not have expected opcode
Please help me .

Thank You

How-to debug a WS connection

I find this project to be exceptionally fascinating, exemplifying an impressive achievement in engineering.

I've attempted to use it, but, truth be told, I've only succeeded in establishing an SSH connection. However, after a short period or upon pressing the enter key, the WebSocket connection terminates, accompanied by a server-launched message: "disconnect."

I'm curious to know if you're currently utilizing it and, if so, whether it's functioning well for you. If it is, could you please share the remote desktop servers you're using and the versions of guacd and guacamole-common-js?

Lastly, could someone provide some guidance on debugging the unexpected closure of websockets?

crypto token

I'm a bit confused regarding the use of the token.

I'm passing a json token to the guacamole-lite server listening at port 8080 from the client side javascript using guacamole-common-js.

It needs to be encrypted with the crypto package but the crypto package is only available on node (server side). The crypto example you gave is for encrypting the token to send to the guacamole-lite server, correct?

What am I missing here?

SSL support?

Does guacamole-lite have SSL support? Can the doc be updated with sample code for it. Thanks!

SSH

What about SSH? This connection type is not supported?

Logs in production

Can you disable log when NODE_ENV=production? Or console.log can be replaced to debug module?

Thank you.

File downloading broken?

I am unable to manipulate the InputStream from the onfile callback in the client. I do receive the mimetype and filename, but that's it. Has anyone had any luck downloading files (regardless of the method used)?

Im too stupid to follow your guide =(

I managed to install and configure Guacamole server on my Rasberry Pi (using apt-get).
I want to use it as a RDP/VNC gateway in my Tesla Model S webbrowser (i want to play monkey island 2 while supercharging) and make a guide that everyone can follow to do the same.
The webbrowser in my car will not even open the first guacamole login page, just shows a white screen.
I had lots of luck with using the opensource Myrtille RDP proxy, but i really want a Linux gateway.
I think Java is the root of the problem and i therefore want to try my luck with Guacamole-light.

But how can i install it in the simplest manner posible? (i dont need any fancy authentication, im just using it in my LAN) Im terrible at programming (i can only make really basic c programs).

My guacamole install resides in: /usr/share/guacamole/guacamole
I downloaded guacamole-light from github and unzipped it to /usr/share/guacamole/guacamole/guacamole-lite-master
From there i ran: sudo npm install --save

And now im stuck in my own incompetence, i dont know how to proceed to replace the local Guacamole java based loginpage with the guacamole-light version.
Can you please help me, if not all the way, give me a push in the right direction?

Guacamole Error: guacd was inactive for too long

Guacamole-lite and Guacamole-common-js Tunnel connection issue.

I am also facing the same issue ( Closing connection with error: Error: guacd was inactive for too long) . while connecting through WebSocket tunnel on the client side of application using Guacamole-common-js. I am exposing the WS endpoint using Guacamole -lite from Node-JS backend and trying to established the connection between client and WS so i can show the connected guacamole in side the web-application into the browsers.

Basically our requirement is to render the desktop applications of the user on the browser for that we are trying Guacamole RDP/SSH/VNC connection and want to display the respective connected remote server on the browsers to access desktop application.

Please help us its urgent.

Error => Logs

[GcLog] Starting guacamole-lite websocket server
ws guacamole runing on 4822
[GcLog] [2021-12-15 12:37:20] [Connection 1] Client connection open
[GcLog] [2021-12-15 12:37:20] [Connection 1] Opening guacd connection
[GcLog] [2021-12-15 12:37:20] [Connection 1] guacd connection open
[GcLog] [2021-12-15 12:37:20] [Connection 1] Selecting connection type: rdp
[GcLog] [2021-12-15 12:37:20] [Connection 1] Sending opCode: 6.select,3.rdp;
[GcError] [2021-12-15 12:37:30] [Connection 1] Closing connection with error: Error: guacd was inactive for too long
at GuacdClient.checkActivity (C:\Users\vsha8727\CNCX\coding\guacamole\bkgc\node_modules\guacamole-lite\lib\GuacdClient.js:35:41)
at listOnTimeout (node:internal/timers:557:17)
at processTimers (node:internal/timers:500:7)
[GcLog] [2021-12-15 12:37:30] [Connection 1] Closing guacd connection
[GcLog] [2021-12-15 12:37:30] [Connection 1] Client connection closed

Client Side Code ->
http://guacamole.incubator.apache.org/doc/gug/guacamole-common-js.html

import React from 'react';
import Guacamole from 'guacamole-common-js';
import { encrypt } from './service';

const connection = {
"connection": {
"type": "rdp",
"settings": {
port: use your port, // port of guacd
host: 'use your host',
username: 'use your username',
password: 'use your password',
}
}
}
const GuacamoleApp: React.FC = () => {
const openGc = async () => {
try {
const token = await encrypt(connection);
console.log("token", token)
const wsurl = ws://localhost:4822/guaclite?token=${token}&width=600&height:600px;
console.log("wsurl", wsurl)
const gc = await new Guacamole.Client(new Guacamole.WebSocketTunnel(wsurl));
console.log("gc", gc)
const display = document.getElementById('gcdisplay');
console.log("element", gc.getDisplay().getElement())
if (display) {
display?.appendChild(gc.getDisplay().getElement());
}

        gc.connect('');
        
    } catch (error: any) {
        console.log("GC Error", error);
    }
}
return <div style={{width: '100%'}}>
    <h1>Guacamole Connect RDP/SSH/VNC</h1>
    <button onClick={openGc}>Open Guacamole</button>
    
    <div id='gcdisplay' style={{minWidth: '800px', minHeight:'600px', backgroundColor:'grey'}}></div>
</div>

}
export default GuacamoleApp;

Backend Side Code ->

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const GuacamoleLite = require('guacamole-lite');
var app = express();
const http = require('http').Server(app);

const guacdOptions = {
port: 'use your port', // port of guacd
host: 'use your host',
username: 'use your username',
password: 'use your password',
};

const clientOptions = {
crypt: {
cypher: 'AES-256-CBC',
key: 'MySuperSecretKeyForParamsToken12'
},
connectionDefaultSetting: {
rdp: {
"security": "NLA (Network Level Authentication)",
"ignore-cert": true,
}
},
log: {
level: 'VERBOSE',
stdLog: (...args) => {
console.log('[GcLog]', ...args)
},
errorLog: (...args) => {
console.error('[GcError]', ...args)
}
}
};

const guacServer = new GuacamoleLite({server: http, path: '/guaclite'}, guacdOptions, clientOptions);

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};

// render the error page
res.status(err.status || 500);
res.render('error');
});

http.listen(4822, ()=> console.log('ws guacamole runing on 4822'));
module.exports = app;

Guacamole.WebSocketTunnel

Hello There !

I'm trying to use this module and I'm not able to establish the websocoket connection

var client = new Guacamole.Client(
	new Guacamole.WebSocketTunnel("tunnel"),
);
document.body.appendChild(client.getDisplay().getElement());
client.connect('token={{token}}')

This is how looks my json (decrypt token):

{ 
userId: 233, ( I don't know what to put here ?)
expires: 1597818775674,
connection:{ 
     type: 'vnc',
     settings: { 
        password: 'xxxxx',
        port: '5900',
        hostname: '26.0.0.203',
        autosize: 'false' 
} } }

guacd[2342]: ERROR: Guacamole protocol violation. Perhaps the version of guacamole-client is incompatible with this version of guacd?

My client options (I don't see any additional logs with verbose : true )

var clientOptions = {
    log: {
        verbose: true
    },
	crypt : {
		cypher	: 'AES-256-CBC',
		key		: 'MySuperSecretKeyForParamsToken12'
	}
}

I'm using guacd 1.0.0.

Is this module works with 1.0.0+ release?

I'm doing something wrong ?

Thanks for your help !

Is there any default location to put a file into remote server?

#9 In this issue, there is a function for uploading a file into a remote server working with type="input" but I could not see the location of the folder inside of the remote server? How can I declare a common path for such a purpose(also guacd user should have rwx right.) Or Am I missing something for uploading a file?

Getting Token validation failed, Closing connection with error: TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined

@fredericosilva @luiso1979 @thelamer @vadimpronin @dnapier I am getting below error in backend code: node index.js
[MyLog] Starting guacamole-lite websocket server
[MyLog] [2021-11-29 09:57:27] [Connection 1] Client connection open
[MyLog] [2021-11-29 09:57:27] [Connection 1] Token validation failed
[MyLog] [2021-11-29 09:57:27] [Connection 1] Closing connection with error: TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined
at Function.from (buffer.js:331:9)
at Function.base64decode (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/lib/Crypt.js:24:23)
at Crypt.decrypt (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/lib/Crypt.js:12:39)
at ClientConnection.decryptToken (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/lib/ClientConnection.js:62:22)
at new ClientConnection (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/lib/ClientConnection.js:25:44)
at Server.newConnection (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/lib/Server.js:149:59)
at WebSocketServer.emit (events.js:315:20)
at /Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/node_modules/ws/lib/WebSocketServer.js:91:14
at completeHybiUpgrade2 (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/node_modules/ws/lib/WebSocketServer.js:284:5)
at completeHybiUpgrade1 (/Users/manrajparmar/Documents/projects/alex/guacamole/server/node_modules/guacamole-lite/node_modules/ws/lib/WebSocketServer.js:306:13) {
code: 'ERR_INVALID_ARG_TYPE'
}
[MyLog] [2021-11-29 09:57:27] [Connection 1] Client connection closed

Backend code: ```const GuacamoleLite = require('guacamole-lite');
const express = require('express');
const http = require('http');
const app = express();
// const server = http.createServer(app);
const websocketOptions = {
port: 8080 // we will accept connections to this port
};

const guacdOptions = {
port: 4822 // port of guacd
};

const clientOptions = {
crypt: {
cypher: 'AES-256-CBC',
key: 'MySuperSecretKeyForParamsToken12'
},
log: {
level: 'DEBUG',
stdLog: (...args) => {
console.log('[MyLog]', ...args)
},
errorLog: (...args) => {
console.error('[MyLog]', ...args)
}
}
};

// const guacServer = new GuacamoleLite({server}, guacdOptions, clientOptions);
const guacServer = new GuacamoleLite(websocketOptions, guacdOptions, clientOptions);

guacServer.on('open', (clientConnection) => {
console.error("Guac Server call open on event");
// const url = 'http://our-backend-server/api/connection/open?userId=' + clientConnection.connectionSettings.userId
// Http.request(url).end();
});
guacServer.on('close', (clientConnection) => {
console.error("Guac Server call close on event");
// const url = 'http://our-backend-server/api/connection/close?userId=' + clientConnection.connectionSettings.userId
// Http.request(url).end();
});
guacServer.on('error', (clientConnection, error) => {
console.error("Guac Server error: ",clientConnection, error);
});

// var serverPort = process.env.YOUR_PORT || process.env.PORT || 5000;
// server.listen(serverPort, () => {
// console.log("Started on : "+ serverPort);
// })```

Frontend code: `import logo from './logo.svg';
import './App.css';
import Guacamole from "guacamole-common-js";
import { useEffect } from 'react';

function App() {
useEffect(() => {
let guaca = new Guacamole.Client(new Guacamole.WebSocketTunnel('ws://localhost:8080/?token=eyJjb25uZWN0aW9uIjp7InR5cGUiOiJyZHAiLCJzZXR0aW5ncyI6eyJob3N0bmFtZSI6ImxvY2FsaG9zdCIsInVzZXJuYW1lIjoiTWFucmFqIFBhcm1hciIsInBhc3N3b3JkIjoiMTIzNCIsImVuYWJsZS1kcml2ZSI6dHJ1ZSwiY3JlYXRlLWRyaXZlLXBhdGgiOnRydWUsInNlY3VyaXR5IjoiYW55IiwiaWdub3JlLWNlcnQiOnRydWUsImVuYWJsZS13YWxscGFwZXIiOmZhbHNlfX19&width=1024&height=768&dpi=32'));
// let guaca = new Guacamole.Client(new Guacamole.WebSocketTunnel(ws://localhost:8080?token=${JSON.stringify({"connection":{"type":"rdp","settings":{"hostname":"localhost","username":"Manraj Parmar","password":"1234","enable-drive":true,"create-drive-path":true,"security":"any","ignore-cert":true,"enable-wallpaper":false}}})}));
guaca.onerror = function (error) {
alert(error);
};
guaca.connect();
// Disconnect on close
window.onunload = function () {
guaca.disconnect();
}
let display = document.getElementById("display");
display.appendChild(guaca.getDisplay().getElement());
}, [])

return (


Test

);
}

export default App;
`
Can you please have a look and help me on it.
Might be only a small mistake in socket url in frontend code, something missing or wrong in token, so please check and help. please drop a comment.

About size of screen

Hi:
When I try to resize the picture, but it not works. My params is ws://192.168.1.115:8081/?token=token&width=1080&height=1920&dpi=32. I can see the picture but can't resize it.
thank you.

Error: WS was inactive for too long

I created a simple server to test, and I get these 2 errors:

losing connection with error: 1000
[2024-06-19 03:18:21] [Connection 1] Closing connection with error: Error: WS was inactive for too long

No matter what configuration I do, the connection opens, and in a few seconds it drops, showing these errors.

RDP Audio Support

First off sorry for the wall of text.

Short and to the point question does anyone have a functional RDP client with audio support for guacamole-lite they could share?

On the surface I think maybe guacamole-lite might not be passing file/audio output and input streams from guacd to the javascript client through the websocket tunnel. Though I am not sure, thus why I am reaching out.

I have been trying to reverse engineer audio support from the stock java client, and as far as I can tell they do not do anything special and the base guacamole-common should negotiate the additional InputStream needed for the raw PCM audio supported by modern browsers.
In my custom app and the default java client I am using identical code for the client:
https://cdn.jsdelivr.net/npm/[email protected]/dist/guacamole-client.min.js

When negotiating the connection I am getting identical lines back from guacd with both my custom client/guacamole-lite and the stock java client, so I believe the audio= connectionstring parameters might be redundant as that happens regardless of if they are included or not:

guacd[8]: INFO:	Accepted format: 16-bit PCM with 2 channels at 44100 Hz
guacd[8]: INFO:	Accepted format: 16-bit PCM with 2 channels at 22050 Hz

From a debugging standpoint I have gone through and captured Guacamole.Tunnel.oninstruction events client side and have noticed that specifically there are no audio opcodes being sent to the client to process that are normally processed by this clientside:
https://github.com/apache/guacamole-client/blob/32b106b982dc74e71e1cbb3d2c5e0c5b9b9a8bab/guacamole-common-js/src/main/webapp/modules/Client.js#L884

From a due diligence standpoint I use Docker for development and deployment, if anyone is interested in helping out or debugging you can simply run this Docker container:

docker run --name taisun -d \
--restart always \
-p 3000:3000 \
-p 8000:8000 \
-v /var/run/docker.sock:/var/run/docker.sock \
taisun/webapp

If you hop on port 8000 the rdp client can be found under public/js/rdp.js

You can spinup an RDP client and a guacd instance from the web interface on port 3000 under VDI (no parameters are needed outside of the name)

Also just a side note I have functional audio in the VDI endpoints with xrdp and pulseaudio, I have tested them functional with multiple clients including stock guacamole client 1.0.0 and 1.1.0.

Another thing to mention is after this projects(guacamole-lite) inception Guacamole shifted all the audio tunneling code to raw PCM vs a mix of different mimetypes(ogg vorbis etc) in 0.9.9, not sure if that matters just bringing it up.

Facing: guacd was inactive for too long

Hi,

We were successfully able to setup guacamole-lite and guacd, the RDP connection was also happening, Though today we are facing "guacd was inactive for too long".

This is the log:
Starting guacamole-lite websocket server
[2020-08-20 12:19:05] [Connection 1] Client connection open
[2020-08-20 12:19:05] [Connection 1] Opening guacd connection
[2020-08-20 12:19:05] [Connection 1] guacd connection open
[2020-08-20 12:19:05] [Connection 1] Selecting connection type: rdp
[2020-08-20 12:19:05] [Connection 1] Sending opCode: 6.select,3.rdp;
[2020-08-20 12:19:05] [Connection 1] <<<W2G< 6.select,3.rdp;***
[2020-08-20 12:19:05] [Connection 1] Sending opCode: 4.size,6.1100px,6.500px?,2.96;
[2020-08-20 12:19:05] [Connection 1] <<<W2G< 4.size,6.1100px,6.500px?,2.96;***
[2020-08-20 12:19:05] [Connection 1] Sending opCode: 5.audio,9.audio/L16;
[2020-08-20 12:19:05] [Connection 1] <<<W2G< 5.audio,9.audio/L16;***
[2020-08-20 12:19:05] [Connection 1] Sending opCode: 5.video;
[2020-08-20 12:19:05] [Connection 1] <<<W2G< 5.video;***
[2020-08-20 12:19:05] [Connection 1] Sending opCode: 5.image;
[2020-08-20 12:19:05] [Connection 1] <<<W2G< 5.image;***
[2020-08-20 12:19:05] [Connection 1] Server sent handshake: .....
[2020-08-20 12:19:05] [Connection 1] Sending opCode: 7.......;
[2020-08-20 12:19:05] [Connection 1] <<<W2G< 7.connect,...,0.,.....;***
[2020-08-20 12:19:05] [Connection 1] >>>G2W> 5.ready,37......;###
[2020-08-20 12:19:05] [Connection 1] <<<W2G< 3.ack,1.1,2.OK,1.0;***
[2020-08-20 12:19:09] [Connection 1] <<<W2G< 3.nop;***
[2020-08-20 12:19:14] [Connection 1] <<<W2G< 3.nop;***
[2020-08-20 12:19:16] [Connection 1] Closing connection with error: Error: guacd was inactive for too long
at GuacdClient.checkActivity (/home/ubuntu/node_modules/guacamole-lite/lib/GuacdClient.js:35:41)
at ontimeout (timers.js:482:11)
at tryOnTimeout (timers.js:317:5)
at Timer.listOnTimeout (timers.js:277:5)
[2020-08-20 12:19:16] [Connection 1] Closing guacd connection
[2020-08-20 12:19:16] [Connection 1] Client connection closed

I am passing all params as mentioned here #8, omitted them in logs. Already tried restarting guacd and reinstalling guacamole-lite, no luck so far. What am I missing here?

Guacamole-lite configuration and permission management

Hi Team,
I am looking to implement Guacamole-lite for one of my client requirements to modify Guacamole client UI in a permission-based system.
Can anyone please guide us for the implementation in node js and on the client side for either Angular or Reactjs?

Unhandled Exception during errors in clientconnection.js

First off, awesome library. Love it.

I'm getting unhanded exceptions and crashing when (I think) users with high latency disconnect (I think). From what I can tell this occurs if the websocket is already disconnected before clientconnection.error fires a server.emit. Handling this would look something like:

ClientConnection.js:

...
error(error) {
        try {
            this.server.emit('error', this, error);
        } catch (err) {
            this.log(this.server.LOGLEVEL.ERRORS, 'Failed to send error to client: ', error);
        }
        this.close(error);
    }
...

I have not been able to reproduce this myself, but am working off stack traces and believe this to be the issue. Can you take a look at this and perhaps add in some catching/validating at this stage?

Is file uploading provided in this lite client?

It's great to find this project, I'm implementing my own guacamole client, but stuck in file uploading.

I have no idea about how to upload files as described in guacamole-client docs, is it provided in guacamole-common-js or I have to write every thing myself?

Do you get file uploading works? How? Thanks very much.

Not able to connect to VNC | Closing connection with error: Error: not opened

I am having above error when connecting to gucamole .I am trying to connect using vnc protcol and flowing is my setup

#!/usr/bin/env node

const GuacamoleLite = require('guacamole-lite');

const websocketOptions = {
    port: 8080 // we will accept connections to this port
};

const guacdOptions = {
    port: 4822 // port of guacd
};

const clientOptions = {
    log: {
        verbose: true
    },
    crypt: {
        cypher: 'AES-256-CBC',
        key: 'MySuperSecretKeyForParamsToken12'
    }
};

const guacServer = new GuacamoleLite(websocketOptions, guacdOptions, clientOptions);

To generate the Token I am using a script -

const crypto = require('crypto');

const clientOptions = {
    cypher: 'AES-256-CBC',
    key: 'MySuperSecretKeyForParamsToken12'
}

const encrypt = (value) => {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv(clientOptions.cypher, clientOptions.key, iv);

    let crypted = cipher.update(JSON.stringify(value), 'utf8', 'base64');
    crypted += cipher.final('base64');

    const data = {
        iv: iv.toString('base64'),
        value: crypted
    };

    return new Buffer(JSON.stringify(data)).toString('base64');
};

console.log("Token : " + encrypt({
    "connection": {
        "type": "vnc",
        "settings": {
            "hostname": "192.168.43.158",
            "port": "5900",
            "username": "pawan",
            "password": "pawan"           
        }
    }
}));

And my client side app looks like this --

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Page Title</title>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <link rel="stylesheet" href="">
        <style></style>

        <!-- Guacamole -->
        <script type="text/javascript"
            src="https://unpkg.com/browse/[email protected]/dist/guacamole-common.js"></script>

        <script type="text/javascript"
        src="/index.js"></script>

    </head>
    
    
    <body>
    
        <!-- Display -->
        <div id="display"></div>
    
        <!-- Init -->
        <script type="text/javascript"> /* <![CDATA[ */
    
            // Get display div from document
            var display = document.getElementById("display");
    
            // Instantiate client, using an HTTP tunnel for communications.
            var guac = new Guacamole.Client(
                new Guacamole.WebSocketTunnel("ws://192.168.43.158:8080?token=eyJpdiI6IjRXamp4VXl1ZTZMZ1BpUHVydjhzM1E9PSIsInZhbHVlIjoiV2QyQVVTbnF1Zk01eW1ERWlQYnNZUnFNK3FrNERFS05XNjkyZC9yM0c1MndxZXZBb3NGNVNqdUVFSUZkR0xOYXVoS2dJVkV6UHlUaXlvZWdoSG90dy9NQ0tFVWtJYWxEanEzRmlCQVFUaHZWRUpVTXhlVjZDRktwV3Q2WVVIeXIrME1kL3lXN2RFN2FwTHMweENjM2pIMU5xTWh1c3crRFNzNTh4Z0xNalJVPSJ9")
            );
    
            // Add client to display div
            display.appendChild(guac.getDisplay().getElement());
            
            // Error handler
            guac.onerror = function(error) {
                console.log("Error :: ", error);
                alert(error);
            };
    
            // Connect
            guac.connect('');
    
            // Disconnect on close
            window.onunload = function() {
                guac.disconnect();
            }
    
        /* ]]> */ </script>
    
    </body>

</html>

I have attached the logs from Server side --

`
[2022-03-08 17:20:23] [Connection 1] Client connection open
[2022-03-08 17:20:23] [Connection 1] Opening guacd connection
[2022-03-08 17:20:23] [Connection 1] guacd connection open
[2022-03-08 17:20:23] [Connection 1] Selecting connection type: vnc
[2022-03-08 17:20:23] [Connection 1] Sending opCode: 6.select,3.vnc;
[2022-03-08 17:20:23] [Connection 1] <<<W2G< 6.select,3.vnc;***
[2022-03-08 17:20:23] [Connection 1] Sending opCode: 4.size,4.1024,3.768,2.96;
[2022-03-08 17:20:23] [Connection 1] <<<W2G< 4.size,4.1024,3.768,2.96;***
[2022-03-08 17:20:23] [Connection 1] Sending opCode: 5.audio;
[2022-03-08 17:20:23] [Connection 1] <<<W2G< 5.audio;***
[2022-03-08 17:20:23] [Connection 1] Sending opCode: 5.video;
[2022-03-08 17:20:23] [Connection 1] <<<W2G< 5.video;***
[2022-03-08 17:20:23] [Connection 1] Sending opCode: 5.image;
[2022-03-08 17:20:23] [Connection 1] <<<W2G< 5.image;***
[2022-03-08 17:20:23] [Connection 1] Server sent handshake: 4.args,13.VERSION_1_3_0,8.hostname,4.port,9.read-only,9.encodings,8.username,8.password,13.swap-red-blue,11.color-depth,6.cursor,9.autoretry,18.clipboard-encoding,9.dest-host,9.dest-port,12.enable-audio,16.audio-servername,15.reverse-connect,14.listen-timeout,11.enable-sftp,13.sftp-hostname,13.sftp-host-key,9.sftp-port,13.sftp-username,13.sftp-password,16.sftp-private-key,15.sftp-passphrase,14.sftp-directory,19.sftp-root-directory,26.sftp-server-alive-interval,21.sftp-disable-download,19.sftp-disable-upload,14.recording-path,14.recording-name,24.recording-exclude-output,23.recording-exclude-mouse,22.recording-include-keys,21.create-recording-path,12.disable-copy,13.disable-paste,15.wol-send-packet,12.wol-mac-addr,18.wol-broadcast-addr,12.wol-udp-port,13.wol-wait-time,14.force-lossless
[2022-03-08 17:20:23] [Connection 1] Sending opCode: 7.connect,0.,14.192.168.43.158,4.5900,0.,0.,6.pawan,6.pawan,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.;
[2022-03-08 17:20:23] [Connection 1] <<<W2G< 7.connect,0.,14.192.168.43.158,4.5900,0.,0.,6.pawan,6.pawan,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.;***
[2022-03-08 17:20:23] [Connection 1] >>>G2W> 5.ready,37.$44c5724e-15d2-43ab-81b3-038379222a50;5.error,18.Aborted. See logs.,3.519;###
[2022-03-08 17:20:24] [Connection 1] <<<W2G< 10.disconnect;***
[2022-03-08 17:20:24] [Connection 1] >>>G2W> 10.disconnect;###
[2022-03-08 17:20:24] [Connection 1] Closing connection with error: Error: not opened
at WebSocket.send (/home/pawansingh/projects/guacamole/node_modules/ws/lib/WebSocket.js:218:38)
at ClientConnection.send (/home/pawansingh/projects/guacamole/node_modules/guacamole-lite/lib/ClientConnection.js:130:24)
at GuacdClient.sendBufferToWebSocket (/home/pawansingh/projects/guacamole/node_modules/guacamole-lite/lib/GuacdClient.js:172:35)
at GuacdClient.processReceivedData (/home/pawansingh/projects/guacamole/node_modules/guacamole-lite/lib/GuacdClient.js:163:14)
at Socket.emit (node:events:390:28)
at addChunk (node:internal/streams/readable:315:12)
at readableAddChunk (node:internal/streams/readable:289:9)
at Socket.Readable.push (node:internal/streams/readable:228:10)
at TCP.onStreamRead (node:internal/stream_base_commons:199:23)
[2022-03-08 17:20:24] [Connection 1] Closing guacd connection
[2022-03-08 17:20:24] [Connection 1] Client connection closed

`

Can somebody please help me to understand what I am doing wrong here?
Is My novnc connection param Wrong?
{ "connection": { "type": "vnc", "settings": { "hostname": "192.168.43.158", "port": "5900", "username": "pawan", "password": "pawan" } } }

CC: @vadimpronin / @luiso1979 Don't mean to spam you guys, just a bit desperate for a solution.
Any help is much appreciated!

Thanks

ECONNREFUSED 127.0.0.1:4822

Hello there,

I just updated guacd to 1.4 and now I'm not able to connectu guacamole-lite to the guacd server.

Guacamole proxy daemon (guacd) version 1.4.0 started
guacd[16907]: INFO:     Listening on host ::1, port 4822
[Connection 1]  Closing connection with error:  { Error: connect ECONNREFUSED 127.0.0.1:4822

Any idea ?

Thanks for your help !

newConnection usage

Hi, I'm trying to understand when newConnection gets called, if ever. Being outside of the constructor, I'm looking for a call or callback for it, but can't find one. Does it get exported as an event through EventEmitter?

TightVNC viewer and this server

Hello should I be able to just start this repo then connect for example by TightVNC by typing ip_address:port ?

I opened this repo in Visual code then installed node modules and did nodemon ./index.js

Node.js token generation example

Hello, congrats about the README it would have saved me some time :)

On my usage I had to generate the token from node.js.

const crypto = require('crypto');
const encrypt = data => {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv(clientOptions.cypher, clientOptions.key, iv);

    let crypted = cipher.update(JSON.stringify(data), 'utf8', 'base64');
    crypted += cipher.final('base64');

    const c = {
        iv: iv.toString('base64')
      , value: crypted
    };

    return new Buffer(JSON.stringify(c)).toString('base64');
};

What do you think about adding it on README?

Potential issue in process exiting

When the node process is aborted (ctrl+c) it hangs permanently at Closing all connections and exiting...

To fix I added process.exit() to the close method. Is this a known issue?

Whene I set config "enable-sftp":true,an error has occured

When I try to upload files and add parameters. Here are the logs:

[2018-05-28 18:27:18] [Connection 24]  Client connection open
[2018-05-28 18:27:18] [Connection 24]  Opening guacd connection
[2018-05-28 18:27:18] [Connection 24]  guacd connection open
[2018-05-28 18:27:18] [Connection 24]  Selecting connection type: vnc
[2018-05-28 18:27:18] [Connection 24]  Sending opCode: 6.select,3.vnc;
[2018-05-28 18:27:18] [Connection 24]  Sending opCode: 4.size,4.1850,3.850,2.96;
[2018-05-28 18:27:18] [Connection 24]  Sending opCode: 5.audio;
[2018-05-28 18:27:18] [Connection 24]  Sending opCode: 5.video;
[2018-05-28 18:27:18] [Connection 24]  Sending opCode: 5.image;
[2018-05-28 18:27:18] [Connection 24]  Server sent handshake: 4.args,8.hostname,4.port,9.read-only,9.encodings,8.password,13.swap-red-blue,11.color-depth,6.cursor,9.autoretry,18.clipboard-encoding,9.dest-host,9.dest-port,12.enable-audio,16.audio-servername,15.reverse-connect,14.listen-timeout,11.enable-sftp,13.sftp-hostname,9.sftp-port,13.sftp-username,13.sftp-password,16.sftp-private-key,15.sftp-passphrase,14.sftp-directory,19.sftp-root-directory,26.sftp-server-alive-interval,14.recording-path,14.recording-name,21.create-recording-path
[2018-05-28 18:27:18] [Connection 24]  Sending opCode: 7.connect,12.10.244.1.225,4.5901,0.,0.,6.123456,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,4.true,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.;
[2018-05-28 18:27:18] [Connection 24]  Closing connection with error:  1006
[2018-05-28 18:27:18] [Connection 24]  Closing guacd connection
[2018-05-28 18:27:18] [Connection 24]  Client connection closed

Thank you!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.