Browse Source

Add self-contained server written in rust

Thomas Dy 8 years ago
parent
commit
8e00fde391

+ 1 - 0
rust-server/.gitignore

@@ -0,0 +1 @@
+target

+ 301 - 0
rust-server/Cargo.lock

@@ -0,0 +1,301 @@
+[root]
+name = "cloudflare-webui"
+version = "0.1.0"
+dependencies = [
+ "hyper 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "bitflags"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cookie"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "openssl 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gcc"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "gdi32-sys"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "hpack"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "httparse"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "hyper"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cookie 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
+ "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "kernel32-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "language-tags"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "lazy_static"
+version = "0.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libressl-pnacl-sys"
+version = "2.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "pnacl-build-helper 1.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "log"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "matches"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "mime"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num_cpus"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "openssl"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-sys 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-sys-extras 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "openssl-sys"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "gdi32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "openssl-sys-extras"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "gcc 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl-sys 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "pnacl-build-helper"
+version = "1.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rustc-serialize"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "rustc_version"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "semver"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "solicit"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tempdir"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "time"
+version = "0.1.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "traitobject"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "typeable"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicase"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "url"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "uuid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "user32-sys"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "uuid"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+

+ 9 - 0
rust-server/Cargo.toml

@@ -0,0 +1,9 @@
+[package]
+name = "cloudflare-webui"
+version = "0.1.0"
+authors = ["Thomas Dy <thatsmydoing@gmail.com>"]
+
+[dependencies]
+hyper = "0.8.0"
+rustc-serialize = "0.3.0"
+url = "0.5.0"

+ 18 - 0
rust-server/src/config.rs

@@ -0,0 +1,18 @@
+use rustc_serialize::json;
+
+use std::fs::File;
+use std::io::prelude::*;
+
+#[derive(RustcDecodable, Debug)]
+pub struct Config {
+    pub email: String,
+    pub token: String,
+    pub whitelist: Vec<String>
+}
+
+pub fn load(path: &str) -> Config {
+    let mut text = String::new();
+    let mut f = File::open(path).unwrap();
+    f.read_to_string(&mut text).ok().expect("Failed to load config");
+    json::decode(&text).unwrap()
+}

+ 125 - 0
rust-server/src/handler.rs

@@ -0,0 +1,125 @@
+use config::Config;
+use std::io;
+use std::io::Read;
+use hyper::{Get, Post};
+use hyper::Client;
+use hyper::client;
+use hyper::header::{ContentType, Headers};
+use hyper::server::{Handler, Request, Response};
+use hyper::status::StatusCode::{NotFound, Unauthorized};
+use hyper::uri::RequestUri::AbsolutePath;
+use rustc_serialize::json;
+use rustc_serialize::json::Json;
+use url::form_urlencoded;
+
+const API_ENDPOINT: &'static str = "https://www.cloudflare.com/api_json.html";
+
+const INDEX_HTML: &'static str = include_str!("../../index.html");
+const BUNDLE_JS: &'static str = include_str!("../../assets/bundle.js");
+
+pub struct SiteHandler {
+    cfg: Config,
+    client: Client
+}
+
+impl SiteHandler {
+    fn post(&self, body: &str) -> client::Response {
+        let mut headers = Headers::new();
+        headers.set(ContentType::form_url_encoded());
+        self.client
+            .post(API_ENDPOINT)
+            .headers(headers)
+            .body(body)
+            .send()
+            .unwrap()
+    }
+}
+
+pub fn new(cfg: Config) -> SiteHandler {
+    SiteHandler {
+        cfg: cfg,
+        client: Client::new()
+    }
+}
+
+fn get_param<'a>(params: &'a Vec<(String, String)>, key: &str) -> &'a str {
+    params.into_iter().find(|tuple| tuple.0 == key).map(|tuple| tuple.1.as_ref()).unwrap_or("")
+}
+
+impl Handler for SiteHandler {
+    fn handle(&self, mut req: Request, mut res: Response) {
+        let mut text = String::new();
+        req.read_to_string(&mut text).ok().expect("Failed to get request body");
+        match req.uri {
+            AbsolutePath(ref path) => match (&req.method, &path[..]) {
+                (&Get, "/") => {
+                    res.send(INDEX_HTML.as_bytes()).unwrap();
+                },
+                (&Get, "/assets/bundle.js") => {
+                    res.send(BUNDLE_JS.as_bytes()).unwrap();
+                },
+                (&Post, "/api") => {
+                    let mut params = form_urlencoded::parse(text.as_bytes());
+                    params.push(("email".to_string(), self.cfg.email.clone()));
+                    params.push(("tkn".to_string(), self.cfg.token.clone()));
+
+                    let a = get_param(&params, "a");
+                    let z = get_param(&params, "z");
+                    let whitelist = &self.cfg.whitelist;
+                    let valid = whitelist.into_iter().any(|domain| domain == z);
+                    if a == "zone_load_multi" {
+                        let form_data = form_urlencoded::serialize(&params);
+                        let mut proxy_res = self.post(&form_data);
+                        let mut body = Json::from_reader(&mut proxy_res).unwrap();
+                        {
+                            let root = body.as_object_mut().unwrap();
+                            let response = root.get_mut("response")
+                                .unwrap()
+                                .as_object_mut()
+                                .unwrap();
+                            let zones = response.get_mut("zones")
+                                .unwrap()
+                                .as_object_mut()
+                                .unwrap();
+                            let count = {
+                                let objs = zones.get_mut("objs")
+                                    .unwrap()
+                                    .as_array_mut()
+                                    .unwrap();
+                                objs.retain(|zone| {
+                                    let zone_name = zone.find("zone_name").and_then(|name| name.as_string()).unwrap();
+                                    whitelist.into_iter().any(|domain| domain == zone_name)
+                                });
+                                objs.len()
+                            };
+                            zones.insert("count".to_string(), Json::U64(count as u64));
+                        };
+
+                        res.headers_mut().set(ContentType::json());
+                        res.send(json::encode(&body).unwrap().as_bytes()).unwrap();
+                    }
+                    else if valid {
+                        let form_data = form_urlencoded::serialize(&params);
+                        let mut proxy_res = self.post(&form_data);
+                        res.headers_mut().extend(proxy_res.headers.iter());
+                        let mut res = res.start().unwrap();
+                        io::copy(&mut proxy_res, &mut res).ok().expect("Failed to proxy");
+                        res.end().unwrap();
+                    }
+                    else {
+                        *res.status_mut() = Unauthorized;
+                        res.send(b"Unauthorized").unwrap();
+                    }
+                },
+                _ => {
+                    *res.status_mut() = NotFound;
+                    res.send(b"Not Found").unwrap();
+                }
+            },
+            _ => {
+                *res.status_mut() = NotFound;
+                res.send(b"Not Found").unwrap();
+            }
+        }
+    }
+}

+ 17 - 0
rust-server/src/main.rs

@@ -0,0 +1,17 @@
+extern crate hyper;
+extern crate rustc_serialize;
+extern crate url;
+mod config;
+mod handler;
+
+use std::env;
+use hyper::Server;
+
+fn main() {
+    let config_path = env::args().nth(1).unwrap_or("./config.json".to_string());
+    let cfg = config::load(&config_path);
+    let site = handler::new(cfg);
+
+    Server::http("127.0.0.1:8000").unwrap()
+        .handle(site).unwrap();
+}