Validating Host headers in RPC requests (#1658)
* Validating Host headers in RPC requests * Fixing convention [ci skip] * Remove extra indent. [ci:skip]
This commit is contained in:
parent
615fca5dc5
commit
6e79a36ef6
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -276,7 +276,7 @@ dependencies = [
|
|||||||
"ethcore-util 1.3.0",
|
"ethcore-util 1.3.0",
|
||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-http-server 5.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
@ -357,7 +357,7 @@ dependencies = [
|
|||||||
"ethsync 1.3.0",
|
"ethsync 1.3.0",
|
||||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
|
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
|
||||||
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-http-server 5.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -629,8 +629,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-http-server"
|
name = "jsonrpc-http-server"
|
||||||
version = "5.1.0"
|
version = "6.1.0"
|
||||||
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#f16b956c61e60b3a530ad4bac82112a8974cf505"
|
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#517a0d7b8c7fd099995ce4cc93f52789e83f2cdc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -38,6 +38,15 @@ impl ContentHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn forbidden(content: String, mimetype: String) -> Self {
|
||||||
|
ContentHandler {
|
||||||
|
code: StatusCode::Forbidden,
|
||||||
|
content: content,
|
||||||
|
mimetype: mimetype,
|
||||||
|
write_pos: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn not_found(content: String, mimetype: String) -> Self {
|
pub fn not_found(content: String, mimetype: String) -> Self {
|
||||||
ContentHandler {
|
ContentHandler {
|
||||||
code: StatusCode::NotFound,
|
code: StatusCode::NotFound,
|
||||||
|
@ -129,6 +129,7 @@ impl Server {
|
|||||||
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
||||||
special
|
special
|
||||||
});
|
});
|
||||||
|
let bind_address = format!("{}", addr);
|
||||||
|
|
||||||
try!(hyper::Server::http(addr))
|
try!(hyper::Server::http(addr))
|
||||||
.handle(move |_| router::Router::new(
|
.handle(move |_| router::Router::new(
|
||||||
@ -136,6 +137,7 @@ impl Server {
|
|||||||
endpoints.clone(),
|
endpoints.clone(),
|
||||||
special.clone(),
|
special.clone(),
|
||||||
authorization.clone(),
|
authorization.clone(),
|
||||||
|
bind_address.clone(),
|
||||||
))
|
))
|
||||||
.map(|(l, srv)| {
|
.map(|(l, srv)| {
|
||||||
|
|
||||||
|
42
dapps/src/router/host_validation.rs
Normal file
42
dapps/src/router/host_validation.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
use DAPPS_DOMAIN;
|
||||||
|
use hyper::server;
|
||||||
|
use hyper::net::HttpStream;
|
||||||
|
|
||||||
|
use jsonrpc_http_server::{is_host_header_valid};
|
||||||
|
use handlers::ContentHandler;
|
||||||
|
|
||||||
|
|
||||||
|
pub fn is_valid(request: &server::Request<HttpStream>, bind_address: &str, endpoints: Vec<String>) -> bool {
|
||||||
|
let mut endpoints = endpoints.into_iter()
|
||||||
|
.map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN))
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
endpoints.push(bind_address.into());
|
||||||
|
is_host_header_valid(request, &endpoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn host_invalid_response() -> Box<server::Handler<HttpStream> + Send> {
|
||||||
|
Box::new(ContentHandler::forbidden(
|
||||||
|
r#"
|
||||||
|
<h1>Request with disallowed <code>Host</code> header has been blocked.</h1>
|
||||||
|
<p>Check the URL in your browser address bar.</p>
|
||||||
|
"#.into(),
|
||||||
|
"text/html".into()
|
||||||
|
))
|
||||||
|
}
|
@ -18,6 +18,7 @@
|
|||||||
//! Processes request handling authorization and dispatching it to proper application.
|
//! Processes request handling authorization and dispatching it to proper application.
|
||||||
|
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
|
mod host_validation;
|
||||||
|
|
||||||
use DAPPS_DOMAIN;
|
use DAPPS_DOMAIN;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -44,40 +45,46 @@ pub struct Router<A: Authorization + 'static> {
|
|||||||
endpoints: Arc<Endpoints>,
|
endpoints: Arc<Endpoints>,
|
||||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||||
authorization: Arc<A>,
|
authorization: Arc<A>,
|
||||||
|
bind_address: String,
|
||||||
handler: Box<server::Handler<HttpStream> + Send>,
|
handler: Box<server::Handler<HttpStream> + Send>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
||||||
|
|
||||||
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
||||||
|
// Validate Host header
|
||||||
|
if !host_validation::is_valid(&req, &self.bind_address, self.endpoints.keys().cloned().collect()) {
|
||||||
|
self.handler = host_validation::host_invalid_response();
|
||||||
|
return self.handler.on_request(req);
|
||||||
|
}
|
||||||
|
|
||||||
// Check authorization
|
// Check authorization
|
||||||
let auth = self.authorization.is_authorized(&req);
|
let auth = self.authorization.is_authorized(&req);
|
||||||
|
if let Authorized::No(handler) = auth {
|
||||||
|
self.handler = handler;
|
||||||
|
return self.handler.on_request(req);
|
||||||
|
}
|
||||||
|
|
||||||
// Choose proper handler depending on path / domain
|
// Choose proper handler depending on path / domain
|
||||||
self.handler = match auth {
|
let url = extract_url(&req);
|
||||||
Authorized::No(handler) => handler,
|
let endpoint = extract_endpoint(&url);
|
||||||
Authorized::Yes => {
|
|
||||||
let url = extract_url(&req);
|
|
||||||
let endpoint = extract_endpoint(&url);
|
|
||||||
|
|
||||||
match endpoint {
|
self.handler = match endpoint {
|
||||||
// First check special endpoints
|
// First check special endpoints
|
||||||
(ref path, ref endpoint) if self.special.contains_key(endpoint) => {
|
(ref path, ref endpoint) if self.special.contains_key(endpoint) => {
|
||||||
self.special.get(endpoint).unwrap().to_handler(path.clone().unwrap_or_default())
|
self.special.get(endpoint).unwrap().to_handler(path.clone().unwrap_or_default())
|
||||||
},
|
},
|
||||||
// Then delegate to dapp
|
// Then delegate to dapp
|
||||||
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
|
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
|
||||||
self.endpoints.get(&path.app_id).unwrap().to_handler(path.clone())
|
self.endpoints.get(&path.app_id).unwrap().to_handler(path.clone())
|
||||||
},
|
},
|
||||||
// Redirection to main page
|
// Redirection to main page
|
||||||
_ if *req.method() == hyper::method::Method::Get => {
|
_ if *req.method() == hyper::method::Method::Get => {
|
||||||
Redirection::new(self.main_page)
|
Redirection::new(self.main_page)
|
||||||
},
|
},
|
||||||
// RPC by default
|
// RPC by default
|
||||||
_ => {
|
_ => {
|
||||||
self.special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default())
|
self.special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default())
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -106,7 +113,9 @@ impl<A: Authorization> Router<A> {
|
|||||||
main_page: &'static str,
|
main_page: &'static str,
|
||||||
endpoints: Arc<Endpoints>,
|
endpoints: Arc<Endpoints>,
|
||||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||||
authorization: Arc<A>) -> Self {
|
authorization: Arc<A>,
|
||||||
|
bind_address: String,
|
||||||
|
) -> Self {
|
||||||
|
|
||||||
let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default());
|
let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default());
|
||||||
Router {
|
Router {
|
||||||
@ -114,6 +123,7 @@ impl<A: Authorization> Router<A> {
|
|||||||
endpoints: endpoints,
|
endpoints: endpoints,
|
||||||
special: special,
|
special: special,
|
||||||
authorization: authorization,
|
authorization: authorization,
|
||||||
|
bind_address: bind_address,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,19 +23,22 @@ pub fn rpc(handler: Arc<IoHandler>, panic_handler: Arc<Mutex<Option<Box<Fn() ->
|
|||||||
Box::new(RpcEndpoint {
|
Box::new(RpcEndpoint {
|
||||||
handler: handler,
|
handler: handler,
|
||||||
panic_handler: panic_handler,
|
panic_handler: panic_handler,
|
||||||
cors_domain: vec![AccessControlAllowOrigin::Null],
|
cors_domain: Some(vec![AccessControlAllowOrigin::Null]),
|
||||||
|
// NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
|
||||||
|
allowed_hosts: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RpcEndpoint {
|
struct RpcEndpoint {
|
||||||
handler: Arc<IoHandler>,
|
handler: Arc<IoHandler>,
|
||||||
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
|
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
|
||||||
cors_domain: Vec<AccessControlAllowOrigin>,
|
cors_domain: Option<Vec<AccessControlAllowOrigin>>,
|
||||||
|
allowed_hosts: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Endpoint for RpcEndpoint {
|
impl Endpoint for RpcEndpoint {
|
||||||
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
|
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
|
||||||
let panic_handler = PanicHandler { handler: self.panic_handler.clone() };
|
let panic_handler = PanicHandler { handler: self.panic_handler.clone() };
|
||||||
Box::new(ServerHandler::new(self.handler.clone(), self.cors_domain.clone(), panic_handler))
|
Box::new(ServerHandler::new(self.handler.clone(), self.cors_domain.clone(), self.allowed_hosts.clone(), panic_handler))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ Operating Options:
|
|||||||
--mode MODE Set the operating mode. MODE can be one of:
|
--mode MODE Set the operating mode. MODE can be one of:
|
||||||
active - Parity continuously syncs the chain.
|
active - Parity continuously syncs the chain.
|
||||||
passive - Parity syncs initially, then sleeps and
|
passive - Parity syncs initially, then sleeps and
|
||||||
wakes regularly to resync.
|
wakes regularly to resync.
|
||||||
dark - Parity syncs only when an external interface
|
dark - Parity syncs only when an external interface
|
||||||
is active. [default: active].
|
is active. [default: active].
|
||||||
--mode-timeout SECS Specify the number of seconds before inactivity
|
--mode-timeout SECS Specify the number of seconds before inactivity
|
||||||
@ -107,6 +107,11 @@ API and Console Options:
|
|||||||
name. Possible name are web3, eth, net, personal,
|
name. Possible name are web3, eth, net, personal,
|
||||||
ethcore, ethcore_set, traces.
|
ethcore, ethcore_set, traces.
|
||||||
[default: web3,eth,net,ethcore,personal,traces].
|
[default: web3,eth,net,ethcore,personal,traces].
|
||||||
|
--jsonrpc-hosts HOSTS List of allowed Host header values. This option will
|
||||||
|
validate the Host header sent by the browser, it
|
||||||
|
is additional security against some attack
|
||||||
|
vectors. Special options: "all", "none",
|
||||||
|
[default: none].
|
||||||
|
|
||||||
--no-ipc Disable JSON-RPC over IPC service.
|
--no-ipc Disable JSON-RPC over IPC service.
|
||||||
--ipc-path PATH Specify custom path for JSON-RPC over IPC service
|
--ipc-path PATH Specify custom path for JSON-RPC over IPC service
|
||||||
@ -118,8 +123,8 @@ API and Console Options:
|
|||||||
--dapps-port PORT Specify the port portion of the Dapps server
|
--dapps-port PORT Specify the port portion of the Dapps server
|
||||||
[default: 8080].
|
[default: 8080].
|
||||||
--dapps-interface IP Specify the hostname portion of the Dapps
|
--dapps-interface IP Specify the hostname portion of the Dapps
|
||||||
server, IP should be an interface's IP address, or
|
server, IP should be an interface's hostname / IP
|
||||||
all (all interfaces) or local [default: local].
|
or local (localhost) [default: local].
|
||||||
--dapps-user USERNAME Specify username for Dapps server. It will be
|
--dapps-user USERNAME Specify username for Dapps server. It will be
|
||||||
used in HTTP Basic Authentication Scheme.
|
used in HTTP Basic Authentication Scheme.
|
||||||
If --dapps-pass is not specified you will be
|
If --dapps-pass is not specified you will be
|
||||||
@ -141,11 +146,11 @@ Sealing/Mining Options:
|
|||||||
own - reseal only on a new local transaction;
|
own - reseal only on a new local transaction;
|
||||||
ext - reseal only on a new external transaction;
|
ext - reseal only on a new external transaction;
|
||||||
all - reseal on all new transactions [default: own].
|
all - reseal on all new transactions [default: own].
|
||||||
--reseal-min-period MS Specify the minimum time between reseals from
|
--reseal-min-period MS Specify the minimum time between reseals from
|
||||||
incoming transactions. MS is time measured in
|
incoming transactions. MS is time measured in
|
||||||
milliseconds [default: 2000].
|
milliseconds [default: 2000].
|
||||||
--work-queue-size ITEMS Specify the number of historical work packages
|
--work-queue-size ITEMS Specify the number of historical work packages
|
||||||
which are kept cached lest a solution is found for
|
which are kept cached lest a solution is found for
|
||||||
them later. High values take more memory but result
|
them later. High values take more memory but result
|
||||||
in fewer unusable solutions [default: 20].
|
in fewer unusable solutions [default: 20].
|
||||||
--tx-gas-limit GAS Apply a limit of GAS as the maximum amount of gas
|
--tx-gas-limit GAS Apply a limit of GAS as the maximum amount of gas
|
||||||
@ -311,6 +316,7 @@ pub struct Args {
|
|||||||
pub flag_jsonrpc_interface: String,
|
pub flag_jsonrpc_interface: String,
|
||||||
pub flag_jsonrpc_port: u16,
|
pub flag_jsonrpc_port: u16,
|
||||||
pub flag_jsonrpc_cors: Option<String>,
|
pub flag_jsonrpc_cors: Option<String>,
|
||||||
|
pub flag_jsonrpc_hosts: String,
|
||||||
pub flag_jsonrpc_apis: String,
|
pub flag_jsonrpc_apis: String,
|
||||||
pub flag_no_ipc: bool,
|
pub flag_no_ipc: bool,
|
||||||
pub flag_ipc_path: String,
|
pub flag_ipc_path: String,
|
||||||
|
@ -424,9 +424,22 @@ impl Configuration {
|
|||||||
self.args.flag_rpcapi.clone().unwrap_or(self.args.flag_jsonrpc_apis.clone())
|
self.args.flag_rpcapi.clone().unwrap_or(self.args.flag_jsonrpc_apis.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rpc_cors(&self) -> Vec<String> {
|
pub fn rpc_cors(&self) -> Option<Vec<String>> {
|
||||||
let cors = self.args.flag_jsonrpc_cors.clone().or(self.args.flag_rpccorsdomain.clone());
|
let cors = self.args.flag_jsonrpc_cors.clone().or(self.args.flag_rpccorsdomain.clone());
|
||||||
cors.map_or_else(Vec::new, |c| c.split(',').map(|s| s.to_owned()).collect())
|
cors.map(|c| c.split(',').map(|s| s.to_owned()).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rpc_hosts(&self) -> Option<Vec<String>> {
|
||||||
|
let hosts = self.args.flag_jsonrpc_hosts.split(',').collect::<Vec<&str>>();
|
||||||
|
// look for special values
|
||||||
|
for h in &hosts {
|
||||||
|
match *h {
|
||||||
|
"none" => return Some(Vec::new()),
|
||||||
|
"all" => return None,
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(hosts.into_iter().map(|h| h.into()).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn geth_ipc_path(&self) -> String {
|
fn geth_ipc_path(&self) -> String {
|
||||||
@ -541,8 +554,7 @@ impl Configuration {
|
|||||||
|
|
||||||
pub fn dapps_interface(&self) -> String {
|
pub fn dapps_interface(&self) -> String {
|
||||||
match self.args.flag_dapps_interface.as_str() {
|
match self.args.flag_dapps_interface.as_str() {
|
||||||
"all" => "0.0.0.0",
|
"local" => "localhost",
|
||||||
"local" => "127.0.0.1",
|
|
||||||
x => x,
|
x => x,
|
||||||
}.into()
|
}.into()
|
||||||
}
|
}
|
||||||
@ -597,7 +609,7 @@ mod tests {
|
|||||||
assert_eq!(net.rpc_enabled, true);
|
assert_eq!(net.rpc_enabled, true);
|
||||||
assert_eq!(net.rpc_interface, "all".to_owned());
|
assert_eq!(net.rpc_interface, "all".to_owned());
|
||||||
assert_eq!(net.rpc_port, 8000);
|
assert_eq!(net.rpc_port, 8000);
|
||||||
assert_eq!(conf.rpc_cors(), vec!["*".to_owned()]);
|
assert_eq!(conf.rpc_cors(), Some(vec!["*".to_owned()]));
|
||||||
assert_eq!(conf.rpc_apis(), "web3,eth".to_owned());
|
assert_eq!(conf.rpc_apis(), "web3,eth".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,5 +631,22 @@ mod tests {
|
|||||||
assert(conf1);
|
assert(conf1);
|
||||||
assert(conf2);
|
assert(conf2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_rpc_hosts() {
|
||||||
|
// given
|
||||||
|
|
||||||
|
// when
|
||||||
|
let conf0 = parse(&["parity"]);
|
||||||
|
let conf1 = parse(&["parity", "--jsonrpc-hosts", "none"]);
|
||||||
|
let conf2 = parse(&["parity", "--jsonrpc-hosts", "all"]);
|
||||||
|
let conf3 = parse(&["parity", "--jsonrpc-hosts", "ethcore.io,something.io"]);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(conf0.rpc_hosts(), Some(Vec::new()));
|
||||||
|
assert_eq!(conf1.rpc_hosts(), Some(Vec::new()));
|
||||||
|
assert_eq!(conf2.rpc_hosts(), None);
|
||||||
|
assert_eq!(conf3.rpc_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,6 +280,7 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig,
|
|||||||
port: network_settings.rpc_port,
|
port: network_settings.rpc_port,
|
||||||
apis: conf.rpc_apis(),
|
apis: conf.rpc_apis(),
|
||||||
cors: conf.rpc_cors(),
|
cors: conf.rpc_cors(),
|
||||||
|
hosts: conf.rpc_hosts(),
|
||||||
}, &dependencies);
|
}, &dependencies);
|
||||||
|
|
||||||
// setup ipc rpc
|
// setup ipc rpc
|
||||||
|
@ -32,7 +32,8 @@ pub struct HttpConfiguration {
|
|||||||
pub interface: String,
|
pub interface: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub apis: String,
|
pub apis: String,
|
||||||
pub cors: Vec<String>,
|
pub cors: Option<Vec<String>>,
|
||||||
|
pub hosts: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IpcConfiguration {
|
pub struct IpcConfiguration {
|
||||||
@ -66,7 +67,7 @@ pub fn new_http(conf: HttpConfiguration, deps: &Dependencies) -> Option<RpcServe
|
|||||||
let url = format!("{}:{}", conf.interface, conf.port);
|
let url = format!("{}:{}", conf.interface, conf.port);
|
||||||
let addr = SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid JSONRPC listen host/port given.", url));
|
let addr = SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid JSONRPC listen host/port given.", url));
|
||||||
|
|
||||||
Some(setup_http_rpc_server(deps, &addr, conf.cors, apis))
|
Some(setup_http_rpc_server(deps, &addr, conf.cors, conf.hosts, apis))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_rpc_server(apis: Vec<&str>, deps: &Dependencies) -> Server {
|
fn setup_rpc_server(apis: Vec<&str>, deps: &Dependencies) -> Server {
|
||||||
@ -78,21 +79,17 @@ fn setup_rpc_server(apis: Vec<&str>, deps: &Dependencies) -> Server {
|
|||||||
pub fn setup_http_rpc_server(
|
pub fn setup_http_rpc_server(
|
||||||
dependencies: &Dependencies,
|
dependencies: &Dependencies,
|
||||||
url: &SocketAddr,
|
url: &SocketAddr,
|
||||||
cors_domains: Vec<String>,
|
cors_domains: Option<Vec<String>>,
|
||||||
|
allowed_hosts: Option<Vec<String>>,
|
||||||
apis: Vec<&str>,
|
apis: Vec<&str>,
|
||||||
) -> RpcServer {
|
) -> RpcServer {
|
||||||
let server = setup_rpc_server(apis, dependencies);
|
let server = setup_rpc_server(apis, dependencies);
|
||||||
let start_result = server.start_http(url, cors_domains);
|
|
||||||
let ph = dependencies.panic_handler.clone();
|
let ph = dependencies.panic_handler.clone();
|
||||||
|
let start_result = server.start_http(url, cors_domains, allowed_hosts, ph);
|
||||||
match start_result {
|
match start_result {
|
||||||
Err(RpcServerError::IoError(err)) => die_with_io_error("RPC", err),
|
Err(RpcServerError::IoError(err)) => die_with_io_error("RPC", err),
|
||||||
Err(e) => die!("RPC: {:?}", e),
|
Err(e) => die!("RPC: {:?}", e),
|
||||||
Ok(server) => {
|
Ok(server) => server,
|
||||||
server.set_panic_handler(move || {
|
|
||||||
ph.notify_all("Panic in RPC thread.".to_owned());
|
|
||||||
});
|
|
||||||
server
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,9 +41,10 @@ extern crate ethcore_devtools as devtools;
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use util::panics::PanicHandler;
|
||||||
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
use self::jsonrpc_core::{IoHandler, IoDelegate};
|
||||||
|
|
||||||
pub use jsonrpc_http_server::{Server, RpcServerError};
|
pub use jsonrpc_http_server::{ServerBuilder, Server, RpcServerError};
|
||||||
pub mod v1;
|
pub mod v1;
|
||||||
pub use v1::{SigningQueue, ConfirmationsQueue};
|
pub use v1::{SigningQueue, ConfirmationsQueue};
|
||||||
|
|
||||||
@ -74,15 +75,31 @@ impl RpcServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
|
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
|
||||||
pub fn start_http(&self, addr: &SocketAddr, cors_domains: Vec<String>) -> Result<Server, RpcServerError> {
|
pub fn start_http(
|
||||||
let cors_domains = cors_domains.into_iter()
|
&self,
|
||||||
.map(|v| match v.as_str() {
|
addr: &SocketAddr,
|
||||||
"*" => jsonrpc_http_server::AccessControlAllowOrigin::Any,
|
cors_domains: Option<Vec<String>>,
|
||||||
"null" => jsonrpc_http_server::AccessControlAllowOrigin::Null,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
v => jsonrpc_http_server::AccessControlAllowOrigin::Value(v.into()),
|
panic_handler: Arc<PanicHandler>,
|
||||||
|
) -> Result<Server, RpcServerError> {
|
||||||
|
|
||||||
|
let cors_domains = cors_domains.map(|domains| {
|
||||||
|
domains.into_iter()
|
||||||
|
.map(|v| match v.as_str() {
|
||||||
|
"*" => jsonrpc_http_server::AccessControlAllowOrigin::Any,
|
||||||
|
"null" => jsonrpc_http_server::AccessControlAllowOrigin::Null,
|
||||||
|
v => jsonrpc_http_server::AccessControlAllowOrigin::Value(v.into()),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
ServerBuilder::new(self.handler.clone())
|
||||||
|
.cors(cors_domains.into())
|
||||||
|
.allowed_hosts(allowed_hosts.into())
|
||||||
|
.panic_handler(move || {
|
||||||
|
panic_handler.notify_all("Panic in RPC thread.".to_owned());
|
||||||
})
|
})
|
||||||
.collect();
|
.start_http(addr)
|
||||||
Server::start(addr, self.handler.clone(), cors_domains)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start ipc server asynchronously and returns result with `Server` handle on success or an error.
|
/// Start ipc server asynchronously and returns result with `Server` handle on success or an error.
|
||||||
|
Loading…
Reference in New Issue
Block a user