summaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
authorYves Fischer <yvesf-git@xapek.org>2016-09-04 00:05:26 +0200
committerYves Fischer <yvesf-git@xapek.org>2016-09-14 21:15:14 +0200
commit9416cd3d62f15f5d59f3bff94299f690e69a230d (patch)
tree4972cacce2dcbaa58d4a434f3ea7bce0616e11bc /rust
parent53b5c00e625bf700a21be1a3e2070d3b23f1bef4 (diff)
downloadauth-xmppmessage-9416cd3d62f15f5d59f3bff94299f690e69a230d.tar.gz
auth-xmppmessage-9416cd3d62f15f5d59f3bff94299f690e69a230d.zip
switch to using tiny_http
civet fails on FreeBSD because the mongoose Makefile requires gmake.
Diffstat (limited to 'rust')
-rw-r--r--rust/Cargo.toml11
-rw-r--r--rust/build.rs21
-rw-r--r--rust/src/apachelog.rs24
-rw-r--r--rust/src/handler.rs101
-rw-r--r--rust/src/main.rs43
5 files changed, 123 insertions, 77 deletions
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 50dfdaf..708256b 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -5,13 +5,14 @@ authors = ["Yves Fischer <yvesf+git@xapek.org>"]
build = "build.rs"
[dependencies]
-conduit = "0.7.4"
-civet = "0.8.3"
-time = "0.1.35"
-base64 = "0.2.1"
-rust-crypto = "0.2.36"
+tiny_http = "0.5"
+rustc-serialize = "0.3"
+time = "0.1"
+rust-crypto = "0.2"
rand = "0.3"
getopts = "0.2"
+env_logger = "0.3"
+log = "0.3"
[build-dependencies]
gcc = "0.3.35" \ No newline at end of file
diff --git a/rust/build.rs b/rust/build.rs
index 3e9c14f..10c0d21 100644
--- a/rust/build.rs
+++ b/rust/build.rs
@@ -1,6 +1,21 @@
extern crate gcc;
+use std::env;
+
fn main() {
- gcc::compile_library("libsendxmpp.a", &["clib/sendxmpp.c"]);
- println!("cargo:rustc-link-lib=strophe")
-} \ No newline at end of file
+ let target = env::var("TARGET").unwrap();
+ println!("cargo:rustc-link-lib=strophe");
+
+ let mut config = gcc::Config::new();
+ config.file("clib/sendxmpp.c");
+ if target.contains("freebsd") {
+ println!("cargo:rustc-link-search=native=/usr/local/lib");
+ config.include("/usr/local/include");
+ } else if target.contains("linux") {
+ // ok pass
+ } else {
+ println!("Unknown OS, need to adapt build.rs");
+ std::process::exit(1);
+ }
+ config.compile("libsendxmpp.a");
+}
diff --git a/rust/src/apachelog.rs b/rust/src/apachelog.rs
index 2517979..f502d99 100644
--- a/rust/src/apachelog.rs
+++ b/rust/src/apachelog.rs
@@ -1,7 +1,9 @@
///! Prints logging similar to apache http access.log
use std::net::IpAddr;
-use conduit::{Request, Response};
+use std::io::Read;
+
use time;
+use tiny_http::{Request, Response};
pub struct LogEntry {
remote_ip_address: IpAddr,
@@ -17,7 +19,7 @@ impl LogEntry {
let entry = LogEntry {
remote_ip_address: req.remote_addr().ip(),
remote_user: String::new(),
- request_path: String::from(req.path()),
+ request_path: String::from(req.url()),
time: time::now(),
status: 0,
response_size: 0,
@@ -25,21 +27,19 @@ impl LogEntry {
return entry
}
- pub fn done(&mut self, response: Response) -> Response {
- let (status_code, _) = response.status;
- self.status = status_code;
+ pub fn done<R>(&mut self, _: &Response<R>) where R: Read {
+ self.status = 0; // not accessible
self.print();
- return response;
}
#[inline(always)]
fn print(&self) {
println!("{} - {} - [{}] \"{}\" {} {}",
- self.remote_ip_address,
- self.remote_user,
- time::strftime("%d/%b/%Y %T %z", &self.time).unwrap(),
- self.request_path,
- self.status,
- self.response_size);
+ self.remote_ip_address,
+ self.remote_user,
+ time::strftime("%d/%b/%Y %T %z", &self.time).unwrap(),
+ self.request_path,
+ self.status,
+ self.response_size);
}
}
diff --git a/rust/src/handler.rs b/rust/src/handler.rs
index e16899b..5fb7a03 100644
--- a/rust/src/handler.rs
+++ b/rust/src/handler.rs
@@ -1,22 +1,19 @@
-use std::thread;
-use std::collections::{HashMap, HashSet};
-use std::sync::{Arc, RwLock};
-use std::io::empty;
-use std::error::Error;
use std::cell::Cell;
+use std::collections::{HashMap, HashSet};
+use std::io;
use std::marker::Sync;
use std::ops::Add;
+use std::str::from_utf8;
+use std::sync::{Arc, RwLock};
+use std::thread;
use std::time::Duration;
+use sendxmpp;
use time;
-use base64;
-use civet::response;
-use conduit::{Request, Response, Handler};
-
use token;
-use sendxmpp;
-use apachelog;
+use tiny_http::{Request, Response, StatusCode, Header};
+use rustc_serialize::base64::FromBase64;
pub struct AuthHandler {
bot_jid: String,
@@ -24,10 +21,18 @@ pub struct AuthHandler {
usernames: HashSet<String>,
valid_tokens_cache: Arc<RwLock<HashMap<String, i64>>>,
tg: token::TokenGenerator,
- last_interactive_request: Cell<i64>,
- headers_authenticate: HashMap<String, Vec<String>>,
+ last_interactive_request: Cell<i64>
}
+type EmptyResponse = Response<io::Empty>;
+
+macro_rules! get_header {
+ ($headers:expr, $name:expr) => ($headers.iter()
+ .filter(|h| h.field.equiv($name))
+ .next().ok_or(stringify!(Header not found: $name)));
+}
+
+
impl AuthHandler {
pub fn make(bot_jid: String, bot_password: String,
usernames: HashSet<String>, validity: time::Duration, secret: Vec<u8>) -> AuthHandler {
@@ -38,9 +43,6 @@ impl AuthHandler {
valid_tokens_cache: Arc::new(RwLock::new(HashMap::new())),
tg: token::TokenGenerator::new(validity.num_seconds(), secret),
last_interactive_request: Cell::new(0),
- headers_authenticate: vec!(("WWW-Authenticate".to_string(),
- vec!("Basic realm=\"xmppmessage auth\"".to_string())))
- .into_iter().collect(),
}
}
@@ -55,24 +57,37 @@ impl AuthHandler {
}
// Result<(method, username, password), error-message>
- fn _get_username_password(request: &Request) -> Result<(String, String, String), &'static str> {
+ #[inline(always)]
+ fn _get_username_password<'a>(request: &'a Request) -> Result<(String, String, String), &'static str> {
let headers = request.headers();
- let mut auth_header = try!(headers.find("Authorization").ok_or("No Authorization header found"));
- let authorization = try!(auth_header.pop().ok_or("No Authorization header value"));
+ let auth_header: &Header = { try!(get_header!(headers, "Authorization")) };
+ let authorization: &str = auth_header.value.as_str();
let mut authorization_split = authorization.split(' ');
let method_value = try!(authorization_split.next().ok_or("No method in header value"));
let value = try!(authorization_split.next().ok_or("No username/password value in header value"));
- let decoded_value = try!(base64::decode(value).or(Err("Fail base64 decode")));
- let utf8_decoded_value = try!(String::from_utf8(decoded_value).or(Err("Failed to utf-8 decode username/password")));
+ let decoded_value = try!(value.from_base64().or(Err("Fail base64 decode")));
+ let utf8_decoded_value = try!(from_utf8(&decoded_value).or(Err("fail to decode utf-8")));
let mut username_password_split = utf8_decoded_value.split(':');
let username = try!(username_password_split.next().ok_or("No username in header"));
let password = try!(username_password_split.next().ok_or("No password in header"));
- Ok((method_value.to_string(), username.to_string(), password.to_string()))
+ Ok((String::from(method_value), String::from(username), String::from(password)))
}
- fn _call_internal(&self, req: &Request) -> Result<(), (u32, &'static str)> {
+ fn authenticate_response(status_code: u16) -> io::Result<EmptyResponse> {
+ Ok(Response::new(
+ StatusCode(status_code),
+ vec![
+ Header::from_bytes(&b"WWW-Authenticate"[..], &b"Basic realm=\"xmppmessage auth\""[..]).unwrap()
+ ],
+ io::empty(),
+ Some(0),
+ None,
+ ))
+ }
+
+ fn _call_internal(&self, request: &Request) -> io::Result<EmptyResponse> {
let current_time = time::now().to_timespec().sec;
- return match AuthHandler::_get_username_password(req) {
+ return match AuthHandler::_get_username_password(request) {
Ok((_, username, password)) => {
let is_known_user = self.usernames.contains(&username);
if username.len() > 0 && password.len() == 0 {
@@ -80,29 +95,29 @@ impl AuthHandler {
if current_time - self.last_interactive_request.get() < 2 {
// If last error was not longer then 2 second ago then sleep
thread::sleep(Duration::from_secs(5));
- return Err((429, "Too many requests"))
+ return AuthHandler::authenticate_response(429) // Too many requests
} else {
self.last_interactive_request.set(current_time);
if is_known_user {
self.send_message(&username);
}
- return Err((401, "Token sent, retry now"))
+ return AuthHandler::authenticate_response(401) //Token sent, retry now
}
} else {
match self.verify(&username, &password) {
Ok(true) => {
if is_known_user {
- return Ok(());
+ return Ok(Response::empty(200)) // Ok
} else {
self.last_interactive_request.set(current_time);
- Err((401, "Token sent, retry"))
+ return AuthHandler::authenticate_response(401) // invalid password
}
},
Ok(false) => {
if current_time - self.last_interactive_request.get() < 2 {
// If last error was not longer then 2 seconds ago then sleep 5 seconds
thread::sleep(Duration::from_secs(5));
- return Err((429, "Too Many Requests"))
+ return Ok(Response::empty(429)) // Too Many Requests
} else {
self.last_interactive_request.set(current_time);
// in this case we use the chance to delete outdated cache entries
@@ -110,19 +125,19 @@ impl AuthHandler {
Ok(num) => println!("Removed {} cache entries", num),
Err(e) => println!("{}", e),
};
- return Err((401, "Authentication failed, username or password wrong"));
+ return AuthHandler::authenticate_response(401) // Authentication failed, username or password wrong
}
},
Err(msg) => {
println!("verify failed: {}", msg);
- Err((500, "Server Error"))
+ return Err(io::Error::new(io::ErrorKind::Other, "Server Error")) // Server Error
}
}
}
},
Err(e) => {
- println!("Failed: {}", e);
- return Err((401, e))
+ info!("{}. Request Authentication", e);
+ return AuthHandler::authenticate_response(401) // No Authorization header
},
};
}
@@ -181,19 +196,15 @@ impl AuthHandler {
}
}
}
-}
-unsafe impl Sync for AuthHandler {
+ #[inline(always)]
+ pub fn call(&self, request: &Request) -> Response<io::Empty> {
+ self._call_internal(request).unwrap_or_else(|err: io::Error| {
+ error!("{}", err);
+ Response::empty(500)
+ })
+ }
}
-impl Handler for AuthHandler {
- fn call(&self, req: &mut Request) -> Result<Response, Box<Error + Send>> {
- let mut logentry = apachelog::LogEntry::start(req);
- return match self._call_internal(req) {
- Ok(_) => Ok(response((200, "OK, go ahead"), HashMap::new(), empty())),
- Err((code, message)) => {
- Ok(response((code, message), self.headers_authenticate.clone(), empty()))
- }
- }.map(|r| logentry.done(r))
- }
+unsafe impl Sync for AuthHandler {
} \ No newline at end of file
diff --git a/rust/src/main.rs b/rust/src/main.rs
index 9537bd7..6338557 100644
--- a/rust/src/main.rs
+++ b/rust/src/main.rs
@@ -1,17 +1,18 @@
use std::env;
use std::collections::HashSet;
use std::iter::repeat;
-use std::sync::mpsc::channel;
+use std::sync::Arc;
+use std::thread;
-extern crate base64;
extern crate crypto;
-extern crate civet;
-extern crate conduit;
+extern crate env_logger;
extern crate getopts;
+#[macro_use] extern crate log;
+extern crate tiny_http;
extern crate time;
extern crate rand;
+extern crate rustc_serialize;
-use civet::{Config, Server};
use crypto::digest::Digest;
use crypto::sha1::Sha1;
use getopts::Options;
@@ -28,6 +29,8 @@ fn print_usage(program: &str, opts: Options) {
}
fn main() {
+ env_logger::init().unwrap();
+
let args: Vec<String> = env::args().collect();
let program = args[0].clone();
let mut opts = Options::new();
@@ -50,7 +53,6 @@ fn main() {
panic!("Missing jid or password");
}
- let mut server_config = Config::new();
let usernames = matches.opt_strs("u").into_iter().collect::<HashSet<String>>();
let mut hasher = Sha1::new();
let mut secret: Vec<u8> = repeat(0).take((hasher.output_bits() + 7) / 8).collect();
@@ -62,18 +64,35 @@ fn main() {
println!("No secret (-s/--secret) given, using random value");
thread_rng().fill_bytes(&mut secret);
});
+ let secret = secret.into_iter().take(16).collect::<Vec<u8>>();
let validity: i64 = matches.opt_str("t").unwrap_or(String::from("48")).parse()
.unwrap_or_else(|_| { panic!("Failed to parse time") });
- server_config.port(matches.opt_str("o").unwrap_or(String::from("8080")).parse()
- .unwrap_or_else(|_| { panic!("Failed to parse port number") }));
-
+ let port = matches.opt_str("o").unwrap_or(String::from("8080")).parse()
+ .unwrap_or_else(|_| { panic!("Failed to parse port number") });
let handler = handler::AuthHandler::make(matches.opt_str("j").unwrap(),
matches.opt_str("p").unwrap(),
usernames,
time::Duration::hours(validity),
secret);
- let _a = Server::start(server_config, handler);
- let (_tx, rx) = channel::<()>();
- rx.recv().unwrap();
+ let handler = Arc::new(handler);
+ let server = Arc::new(tiny_http::Server::http(("0.0.0.0", port)).unwrap());
+
+ let mut handles = Vec::new();
+
+ for _ in 0..2 {
+ let server = server.clone();
+ let handler = handler.clone();
+ handles.push(thread::spawn(move || {
+ for request in server.incoming_requests() {
+ let mut log = apachelog::LogEntry::start(&request);
+ let response = handler.call(&request);
+ log.done(&response);
+ let _ = request.respond(response);
+ }
+ }));
+ }
+ for h in handles {
+ h.join().unwrap();
+ }
} \ No newline at end of file