Giter Club home page Giter Club logo

rdp-rs's People

Contributors

citronneur avatar karonl 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

rdp-rs's Issues

Invalid TPKT size was detected

Hi @citronneur ,

Thanks for your amazing work to bringing us this crate.
I'm trying to build a wasm application based on your work.
But got some unexpected error "Invalid minimal size for TPKT"
I tried to ignore it and it seems can restore itself.
But when looking through your mstsc-rs code, it seems the program shall be terminated when the error occurs

rdp-rs/src/bin/mstsc-rs.rs

Lines 325 to 344 in 7ac880d

while wait_for_fd(handle as usize) && sync.load(Ordering::Relaxed) {
let mut guard = rdp_client.lock().unwrap();
if let Err(Error::RdpError(e)) = guard.read(|event| {
match event {
RdpEvent::Bitmap(bitmap) => {
bitmap_channel.send(bitmap).unwrap();
},
_ => println!("{}: ignore event", APPLICATION_NAME)
}
}) {
match e.kind() {
RdpErrorKind::Disconnect => {
println!("{}: Server ask for disconnect", APPLICATION_NAME);
},
_ => println!("{}: {:?}", APPLICATION_NAME, e)
}
break;
}
}
}))

See the screenshot which shows it continues to work when the error shows up.
Worked but lots of error

Do you have any idea about this error? 'cuz I cannot observe it when using the mstsc-rs which makes me confused.

BTW, the errors happens at line 142 #1, 159 #2, 166 #3 with the tuple (action, length) shown in the screenshot

rdp-rs/src/core/tpkt.rs

Lines 124 to 172 in 7ac880d

pub fn read(&mut self) -> RdpResult<Payload> {
let mut buffer = Cursor::new(self.transport.read(2)?);
let mut action: u8 = 0;
action.read(&mut buffer)?;
if action == Action::FastPathActionX224 as u8 {
// read padding
let mut padding: u8 = 0;
padding.read(&mut buffer)?;
// now wait extended header
buffer = Cursor::new(self.transport.read(2)?);
let mut size = U16::BE(0);
size.read(&mut buffer)?;
// Minimal size must be 7
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/18a27ef9-6f9a-4501-b000-94b1fe3c2c10
if size.inner() < 4 {
Err(Error::RdpError(RdpError::new(RdpErrorKind::InvalidSize, "Invalid minimal size for TPKT")))
}
else {
// now wait for body
Ok(Payload::Raw(Cursor::new(self.transport.read(size.inner() as usize - 4)?)))
}
} else {
// fast path
let sec_flag = (action >> 6) & 0x3;
let mut short_length: u8 = 0;
short_length.read(&mut buffer)?;
if short_length & 0x80 != 0 {
let mut hi_length: u8 = 0;
hi_length.read(&mut Cursor::new(self.transport.read(1)?))?;
let length: u16 = ((short_length & !0x80) as u16) << 8;
let length = length | hi_length as u16;
if length < 3 {
Err(Error::RdpError(RdpError::new(RdpErrorKind::InvalidSize, "Invalid minimal size for TPKT")))
} else {
Ok(Payload::FastPath(sec_flag, Cursor::new(self.transport.read(length as usize - 3)?)))
}
}
else {
if short_length < 2 {
Err(Error::RdpError(RdpError::new(RdpErrorKind::InvalidSize, "Invalid minimal size for TPKT")))
} else {
Ok(Payload::FastPath(sec_flag, Cursor::new(self.transport.read(short_length as usize - 2)?)))
}
}
}
}

Thanks in advance

windows 2012 server not supported

I found when i connect to my windows server,some error pop up ,it's like

[2022-07-22T09:42:04Z ERROR rdp_client] Rdp(Io(Os { code: 54, kind: ConnectionReset, message: "Connection reset by peer" }))
[2022-07-22T09:42:04Z ERROR rdp_client] invalid Rust client pointer
WARN[0017] failed to close the RDP client rdp-addr="192.168.1.202:3389"

this is my code and
program crashes atx224::Client::connect(tpkt::Client::new(tcp),protocols,false,Some(&mut authentication),false,false)?;

let mut authentication = Ntlm::new(
        addr.to_string().clone(),
        params.username.clone(),
        params.password.clone(),
    );
    println!("addr:{}",addr.to_string());
    println!("username:{}",params.username);
    println!("password:{}",params.password);
    // From rdp-rs/src/core/client.rs
    let tcp = Link::new(Stream::Raw(tcp));
    let protocols  = x224::Protocols::ProtocolHybrid as u32|x224::Protocols::ProtocolSSL as u32;
    let x224 = x224::Client::connect(
        tpkt::Client::new(tcp),protocols,false,Some(&mut authentication),false,false)?;

error[E0425]: cannot find function `wait_for_fd` in this scope

Does anyone know why I can't use the install command. I'm running it under macOS (ARM).

cargo install rdp-rs --features=mstsc-rs
Updating crates.io index
Installing rdp-rs v0.1.0
Compiling libc v0.2.137
Compiling autocfg v1.1.0
Compiling cfg-if v1.0.0
Compiling typenum v1.15.0
Compiling bitflags v1.3.2
Compiling proc-macro2 v1.0.47
Compiling semver v1.0.14
Compiling quote v1.0.21
Compiling version_check v0.9.4
Compiling memchr v2.5.0
Compiling lexical-core v0.7.6
Compiling unicode-ident v1.0.5
Compiling serde v1.0.148
Compiling num-traits v0.2.15
Compiling core-foundation-sys v0.8.3
Compiling nom v5.1.2
Compiling num-integer v0.1.45
Compiling static_assertions v1.1.0
Compiling arrayvec v0.5.2
Compiling getrandom v0.1.16
Compiling syn v1.0.104
Compiling ryu v1.0.11
Compiling rustc_version v0.4.0
Compiling generic-array v0.12.4
Compiling num-bigint v0.2.6
Compiling byte-tools v0.3.1
Compiling cast v0.2.7
Compiling block-padding v0.1.5
Compiling digest v0.8.1
Compiling time v0.1.45
Compiling byteorder v1.4.3
Compiling gcc v0.3.55
Compiling cc v1.0.77
Compiling cty v0.2.2
Compiling block-buffer v0.7.3
Compiling raw-window-handle v0.4.3
Compiling rand_core v0.5.1
Compiling rand v0.4.6
Compiling security-framework-sys v2.6.1
Compiling rust-crypto v0.2.36
Compiling core-foundation v0.9.3
Compiling indexmap v1.9.2
Compiling minifb v0.15.3
Compiling remove_dir_all v0.5.3
Compiling toml v0.5.9
Compiling ppv-lite86 v0.2.17
Compiling winapi v0.3.9
Compiling subtle v1.0.0
Compiling fastrand v1.8.0
Compiling opaque-debug v0.2.3
Compiling unicode-width v0.1.10
Compiling native-tls v0.2.11
Compiling rand_chacha v0.2.2
Compiling rusticata-macros v2.1.0
Compiling der-parser v3.0.4
Compiling textwrap v0.11.0
Compiling tempfile v3.3.0
Compiling proc-macro-crate v0.1.5
Compiling crypto-mac v0.7.0
Compiling rand v0.3.23
Compiling security-framework v2.7.0
Compiling raw-window-handle v0.3.4
Compiling atty v0.2.14
Compiling strsim v0.8.0
Compiling base64 v0.11.0
Compiling fake-simd v0.1.2
Compiling rustc-serialize v0.3.24
Compiling vec_map v0.8.2
Compiling lazy_static v1.4.0
Compiling ansi_term v0.12.1
Compiling hashbrown v0.12.3
Compiling clap v2.34.0
Compiling x509-parser v0.6.5
Compiling md4 v0.8.0
Compiling hmac v0.7.1
Compiling rand v0.7.3
Compiling md-5 v0.8.0
Compiling derivative v2.2.0
Compiling num_enum_derive v0.4.3
Compiling hex v0.4.3
Compiling bufstream v0.1.4
Compiling yasna v0.3.2
Compiling num_enum v0.4.3
Compiling rdp-rs v0.1.0
error[E0425]: cannot find function wait_for_fd in this scope
--> /Users/chxmvn/.cargo/registry/src/github.com-1ecc6299db9ec823/rdp-rs-0.1.0/src/bin/mstsc-rs.rs:325:15
|
325 | while wait_for_fd(handle as usize) && sync.load(Ordering::Relaxed) {
| ^^^^^^^^^^^ not found in this scope

error[E0425]: cannot find value handle in this scope
--> /Users/chxmvn/.cargo/registry/src/github.com-1ecc6299db9ec823/rdp-rs-0.1.0/src/bin/mstsc-rs.rs:543:9
|
543 | handle as usize,
| ^^^^^^ not found in this scope

For more information about this error, try rustc --explain E0425.
error: could not compile rdp-rs due to 2 previous errors
error: failed to compile rdp-rs v0.1.0, intermediate artifacts can be found at /var/folders/tv/x2qdcddj3zg_40kh6g2j8_6w0000gn/T/cargo-install5F6cjJ

Support to connect to VM through the host (preconnectPDU)

Is there any plan to support the Session Selection Extension https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeps/83aeefd1-c4a1-4807-8072-2a597c8cf19b so you can connect to a hyper-v virtual machine through the host instead of needing the IP address of the guest?

According to the protocol and the implementation in freerdp it looks quite straightforward.
Unfortunately my attempt ran into a wall of a not answering server after I issued the message described in the above document.

Man-in-the-middle detection using BigUInt compare in cssp.rs

First, thanks for writing this code. This is just an observation, no requests.

I work on MeshCentral and I am working on porting your NLA support from the Rust version to the NodeJS version. In working on that, I noticed this code in cssp.rs:

    // now server respond normally with the original public key incremented by one
	let r2 = &(link.read(0)?);
	println!("READ: read_ts_validate {}", hex::encode(&r1));
    let inc_pub_key = security_interface.gss_unwrapex(&(read_ts_validate(r2)?))?;

    // Check possible man in the middle using cssp
    if BigUint::from_bytes_le(&inc_pub_key) != BigUint::from_bytes_le(certificate.tbs_certificate.subject_pki.subject_public_key.data) + BigUint::new(vec![1]) {
        return Err(Error::RdpError(RdpError::new(RdpErrorKind::PossibleMITM, "Man in the middle detected")))
    }

Looking the the bits received, it seems like ASN1 encoded, not a BigUInt. I would send this challenge:

{
  tagClass: 0,
  type: 16,
  constructed: true,
  composed: true,
  value: [
    {
      tagClass: 0,
      type: 2,
      constructed: false,
      composed: false,
      value: '..........'
    },
    {
      tagClass: 0,
      type: 2,
      constructed: false,
      composed: false,
      value: '.....'
    }
  ]
}

and get this as a response:

{
  tagClass: 0,
  type: 17,
  constructed: true,
  composed: true,
  value: [
    {
      tagClass: 0,
      type: 2,
      constructed: false,
      composed: false,
      value: '..........'
    },
    {
      tagClass: 0,
      type: 2,
      constructed: false,
      composed: false,
      value: '.....'
    }
  ]
}

Only the first "type" changes from 16 to 17, everything else is the same. The BigUInt compare works, but instead, you could check that both prime and exponent are identical.

What about channels?

Hello,

I am new to Rust but have a great interest in RDP for a project that I am working on now that needs to also have channels to allow for file transfer, audio, video stream, etc.

In the C/C++ world, I know of FreeRDP (https://www.freerdp.com/) which has many useful features, but I would like to use RDP from Rust as my project is being developed in it.

In the recent past, I also ran across RustDesk (https://rustdesk.com/) but I do not think that they are using the MS RDP protocol.

I am wondering if your rdp-rs library supports channels like the standard Windows RDP client or, if not, then how challenging would it be to add some of these?

Thanks and have a great day

Insufficient length validation on TPKT packets

rdp-rs/src/core/tpkt.rs

Lines 124 to 158 in b4804d1

pub fn read(&mut self) -> RdpResult<Payload> {
let mut buffer = Cursor::new(self.transport.read(2)?);
let mut action: u8 = 0;
action.read(&mut buffer)?;
if action == Action::FastPathActionX224 as u8 {
// read padding
let mut padding: u8 = 0;
padding.read(&mut buffer)?;
// now wait extended header
buffer = Cursor::new(self.transport.read(2)?);
let mut size = U16::BE(0);
size.read(&mut buffer)?;
// now wait for body
Ok(Payload::Raw(Cursor::new(self.transport.read(size.inner() as usize - 4)?)))
} else {
// fast path
let sec_flag = (action >> 6) & 0x3;
let mut short_length: u8 = 0;
short_length.read(&mut buffer)?;
if short_length & 0x80 != 0 {
let mut hi_length: u8 = 0;
hi_length.read(&mut Cursor::new(self.transport.read(1)?))?;
let length :u16 = ((short_length & !0x80) as u16) << 8;
let length = length | hi_length as u16;
Ok(Payload::FastPath(sec_flag, Cursor::new(self.transport.read(length as usize - 3)?)))
}
else {
Ok(Payload::FastPath(sec_flag, Cursor::new(self.transport.read(short_length as usize - 2)?)))
}
}
}

Hi, I've found a couple of panics in the TPKT parsing code that happen when the TPKT header specifies a length of zero. I've observed this happening due to network errors over in nccgroup/scrying#26 and have come up with some local reproductions.

Here are the test cases I used:

#[cfg(test)]
mod repro {
    use rdp::core::tpkt::Client;
    use rdp::model::link::{Link, Stream};
    use std::io::Cursor;

    fn process(data: &[u8]) {
        let cur = Cursor::new(data.to_vec());
        let link = Link::new(Stream::Raw(cur));
        let mut client = Client::new(link);
        let _ = client.read();
    }

    #[test]
    fn case_1() {
        let buf = b"\x00\x00\x03\x00\x00\x00";
        process(buf);
    }

    #[test]
    fn case_2() {
        let buf = b"\x00\x80\x00\x00\x00\x00";
        process(buf);
    }

    #[test]
    fn case_3() {
        let buf = b"\x03\xe8\x00\x00\x80\x00";
        process(buf);
    }
}

Analysis

First case

0000 0300 0000

---- repro::case_1 stdout ----
thread 'repro::case_1' panicked at 'attempt to subtract with overflow', /home/david/gits/rdp-rs-sciguy16/src/core/tpkt.rs:155:80
stack backtrace:
   0: rust_begin_unwind
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/std/src/panicking.rs:495:5
   1: core::panicking::panic_fmt
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/panicking.rs:92:14
   2: core::panicking::panic
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/panicking.rs:50:5
   3: rdp::core::tpkt::Client<S>::read
             at /home/david/gits/rdp-rs-sciguy16/src/core/tpkt.rs:155:80
   4: rdp_afl::repro::process
             at ./src/main.rs:32:17
   5: rdp_afl::repro::case_1
             at ./src/main.rs:38:9
   6: rdp_afl::repro::case_1::{{closure}}
             at ./src/main.rs:36:5
   7: core::ops::function::FnOnce::call_once
             at /home/david/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
   8: core::ops::function::FnOnce::call_once
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/ops/function.rs:227:5
  • action = 0
  • -> fast path branch on line 142
  • short_length = 0
  • -> else branch on 154
  • Panic on line 155 short_length as usize - 2 subtraction with overflow. In release mode this attempts to allocate a 2^64-2 byte buffer to read into, which fails

Second case

0080 0000 0000

---- repro::case_3 stdout ----
thread 'repro::case_3' panicked at 'attempt to subtract with overflow', /home/david/gits/rdp-rs-sciguy16/src/core/tpkt.rs:152:80
stack backtrace:
   0: rust_begin_unwind
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/std/src/panicking.rs:495:5
   1: core::panicking::panic_fmt
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/panicking.rs:92:14
   2: core::panicking::panic
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/panicking.rs:50:5
   3: rdp::core::tpkt::Client<S>::read
             at /home/david/gits/rdp-rs-sciguy16/src/core/tpkt.rs:152:80
   4: rdp_afl::repro::process
             at ./src/main.rs:32:17
   5: rdp_afl::repro::case_3
             at ./src/main.rs:50:9
   6: rdp_afl::repro::case_3::{{closure}}
             at ./src/main.rs:48:5
   7: core::ops::function::FnOnce::call_once
             at /home/david/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
   8: core::ops::function::FnOnce::call_once
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/ops/function.rs:227:5
  • action = 0
  • -> fast path branch on line 142
  • short_length = 0x80
  • 0x80 & 0x80 != 0 (line 147)
  • hi_length = 0
  • length = (short_length & !0x80) | hi_length = 0
  • Panic on line 152 length as usize - 3 subtraction with overflow. In release mode this attempts to allocate a 2^64-3 byte buffer to read into, which fails

Third case

03e8 0000 8000

---- repro::case_4 stdout ----
thread 'repro::case_4' panicked at 'attempt to subtract with overflow', /home/david/gits/rdp-rs-sciguy16/src/core/tpkt.rs:140:61
stack backtrace:
   0: rust_begin_unwind
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/std/src/panicking.rs:495:5
   1: core::panicking::panic_fmt
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/panicking.rs:92:14
   2: core::panicking::panic
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/panicking.rs:50:5
   3: rdp::core::tpkt::Client<S>::read
             at /home/david/gits/rdp-rs-sciguy16/src/core/tpkt.rs:140:61
   4: rdp_afl::repro::process
             at ./src/main.rs:32:17
   5: rdp_afl::repro::case_4
             at ./src/main.rs:56:9
   6: rdp_afl::repro::case_4::{{closure}}
             at ./src/main.rs:54:5
   7: core::ops::function::FnOnce::call_once
             at /home/david/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227:5
   8: core::ops::function::FnOnce::call_once
             at /rustc/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/core/src/ops/function.rs:227:5
  • action = 0x03
  • FastPathActionX224 branch on line 128
  • padding = 0xe8
  • buffer = [0x00, 0x00]
  • size = 0
  • Panic on line 140 size.inner() as usize - 4 subtraction with overflow. In release mode this attempts to allocate a 2^64-4 byte buffer to read into, which fails

According to the MS documentation the minimum length of a TPKT packet should be 7, and adding a check for this should resolve the issue here.

Excerpt from [2], section 8:

Since an X.224 TPDU occupies at least 3 octets, the minimum value of TPKT length is 7. The maximum value is 65535. This permits a maximum TPDU size of 65531 octets.

Refs:
[1] https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/18a27ef9-6f9a-4501-b000-94b1fe3c2c10
[2] https://www.itu.int/rec/T-REC-T.123-200701-I/en

Serde support

What about to add optional serde support for event structs? I use your client in my project and have to write identical wrapper structs with (de)serialize, it's redundantly.

Unknown capabilities

Hello! I get these errors when I try to connect to my Windows 10 machine:

GLOBAL: RdpError(RdpError { kind: Unknown, message: "CAPABILITY: Unknown capability CapstypeShare" })
GLOBAL: RdpError(RdpError { kind: Unknown, message: "CAPABILITY: Unknown capability CapstypeColorcache" })
GLOBAL: RdpError(RdpError { kind: Unknown, message: "CAPABILITY: Unknown capability CapsettypeLargePointer" })
GLOBAL: RdpError(RdpError { kind: Unknown, message: "CAPABILITY: Unknown capability CapstypeFont" })
GLOBAL: RdpError(RdpError { kind: Unknown, message: "CAPABILITY: Unknown capability CapsettypeBitmapCodecs" })
GLOBAL: RdpError(RdpError { kind: Unknown, message: "CAPABILITY: Unknown capability CapstypeBitmapcacheHostsupport" })
GLOBAL: RdpError(RdpError { kind: Unknown, message: "CAPABILITY: Unknown capability CapstypeRail" })
GLOBAL: RdpError(RdpError { kind: Unknown, message: "CAPABILITY: Unknown capability CapstypeWindow" })
GLOBAL: RdpError(RdpError { kind: Unknown, message: "CAPABILITY: Unknown capability CapsettypeSurfaceCommands" })
GLOBAL: RdpError(RdpError { kind: Unknown, message: "CAPABILITY: Unknown capability CapssettypeFrameAcknowledge" })

using this code drawn from the examples in the README:

use std::net::{SocketAddr, TcpStream};
use rdp::core::client::Connector;
use rdp::core::event::{RdpEvent, PointerEvent, PointerButton};

fn main() {
    let addr = "<MY_IP>:3389".parse::<SocketAddr>().unwrap();
    let tcp = TcpStream::connect(&addr).unwrap();

    let mut connector = Connector::new()
        .screen(800, 600)
        .credentials("".to_string(), "Alistair".to_string(), "<MY_PASSWORD>".to_string());
    let mut client = connector.connect(tcp).unwrap();

    client.write(RdpEvent::Pointer(
        // Send a mouse click down at 100x100
        PointerEvent {
            x: 100 as u16,
            y: 100 as u16,
            button: PointerButton::Left,
            down: true
        }
    )).unwrap();

    client.read(|rdp_event| {
        match rdp_event {
            RdpEvent::Bitmap(bitmap) => {
                dbg!(bitmap.width);
            }
            _ => println!("Unhandled event")
        }
    }).unwrap();
}

Any ideas? Cheers!

Plain RDP mode

Hi, do you plan to support plain RDP mode? When I tried to connect to a couple of Server 2003 and XP boxes I get the following error:

RdpError(RdpError { kind: ProtocolNegFailure, message: "Error during negotiation step" })

When I connect with rdesktop it says:

Protocol(warning): Protocol negotiation failed with reason: SSL not allowed by server
Retrying with plain RDP.

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.