diff --git a/osdep/rust-osdep.cpp b/osdep/rust-osdep.cpp index 605b438bc..1ae19b3ce 100644 --- a/osdep/rust-osdep.cpp +++ b/osdep/rust-osdep.cpp @@ -24,6 +24,7 @@ struct prf_ra { #include "../core/Mutex.hpp" #include "../core/Containers.hpp" #include "../core/SHA512.hpp" +#include "../core/AES.hpp" #include "OSUtils.hpp" #include "rust-osdep.h" @@ -139,4 +140,18 @@ void sha384(const void *in, unsigned int len, void *out) void sha512(const void *in, unsigned int len, void *out) { ZeroTier::SHA512(out, in, len); } +static ZT_INLINE ZeroTier::AES _makeHttpAuthCipher() noexcept +{ + uint8_t key[32]; + ZeroTier::Utils::getSecureRandom(key, 32); + return ZeroTier::AES(key); } +static const ZeroTier::AES HTTP_AUTH_CIPHER = _makeHttpAuthCipher(); + +void encryptHttpAuthNonce(void *block) +{ HTTP_AUTH_CIPHER.encrypt(block, block); } + +void decryptHttpAuthNonce(void *block) +{ HTTP_AUTH_CIPHER.decrypt(block, block); } + +} /* extern "C" */ diff --git a/osdep/rust-osdep.h b/osdep/rust-osdep.h index 1efd5ccab..bec9927ee 100644 --- a/osdep/rust-osdep.h +++ b/osdep/rust-osdep.h @@ -108,6 +108,8 @@ extern void lockDownFile(const char *path, int isDir); extern void getSecureRandom(void *buf, unsigned int len); extern void sha384(const void *in, unsigned int len, void *out); extern void sha512(const void *in, unsigned int len, void *out); +extern void encryptHttpAuthNonce(void *block); +extern void decryptHttpAuthNonce(void *block); #ifdef __cplusplus } diff --git a/rust-zerotier-core/src/locator.rs b/rust-zerotier-core/src/locator.rs index d18989dcc..353284bc8 100644 --- a/rust-zerotier-core/src/locator.rs +++ b/rust-zerotier-core/src/locator.rs @@ -12,7 +12,7 @@ /****/ use std::ffi::CString; -use std::os::raw::{c_char, c_int, c_uint}; +use std::os::raw::{c_int, c_uint}; use std::mem::MaybeUninit; use std::ptr::null; diff --git a/service/Cargo.lock b/service/Cargo.lock index 1400914ca..bb3556223 100644 --- a/service/Cargo.lock +++ b/service/Cargo.lock @@ -26,6 +26,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +dependencies = [ + "byteorder", +] + [[package]] name = "base64" version = "0.13.0" @@ -38,7 +47,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e964e3e0a930303c7c0bdb28ebf691dd98d9eee4b8b68019d2c995710b58a18" dependencies = [ - "base64", + "base64 0.13.0", "serde", ] @@ -48,6 +57,22 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + [[package]] name = "bytes" version = "1.0.1" @@ -256,13 +281,24 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" +[[package]] +name = "http" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" +dependencies = [ + "bytes 0.4.12", + "fnv", + "itoa", +] + [[package]] name = "http" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" dependencies = [ - "bytes", + "bytes 1.0.1", "fnv", "itoa", ] @@ -273,8 +309,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994" dependencies = [ - "bytes", - "http", + "bytes 1.0.1", + "http 0.2.3", ] [[package]] @@ -295,11 +331,11 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12219dc884514cb4a6a03737f4413c0e01c23a1b059b0156004b23f1e19dccbe" dependencies = [ - "bytes", + "bytes 1.0.1", "futures-channel", "futures-core", "futures-util", - "http", + "http 0.2.3", "http-body", "httparse", "httpdate", @@ -312,12 +348,56 @@ dependencies = [ "want", ] +[[package]] +name = "hyperx" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a94cbc2c6f63028e5736ca4e811ae36d3990059c384cbe68298c66728a9776" +dependencies = [ + "base64 0.10.1", + "bytes 0.4.12", + "http 0.1.21", + "httparse", + "language-tags", + "log", + "mime", + "percent-encoding", + "time", + "unicase 2.6.0", +] + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + [[package]] name = "itoa" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" + [[package]] name = "lazy_static" version = "1.4.0" @@ -339,12 +419,24 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + [[package]] name = "memchr" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + [[package]] name = "mio" version = "0.7.7" @@ -423,6 +515,12 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" + [[package]] name = "pin-project" version = "1.0.4" @@ -698,6 +796,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "tinyvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + [[package]] name = "tokio" version = "1.0.2" @@ -757,6 +870,42 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "unicase" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +dependencies = [ + "version_check 0.1.5", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check 0.9.3", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.8" @@ -769,12 +918,35 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +dependencies = [ + "idna", + "matches", + "percent-encoding", +] + [[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + [[package]] name = "want" version = "0.3.0" @@ -822,6 +994,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "www-authenticate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c62efb8259cda4e4c732287397701237b78daa4c43edcf3e613c8503a6c07dd" +dependencies = [ + "hyperx", + "unicase 1.4.2", + "url", +] + [[package]] name = "zeroize" version = "0.9.3" @@ -832,7 +1015,7 @@ checksum = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" name = "zerotier-core" version = "0.1.0" dependencies = [ - "base64", + "base64 0.13.0", "base64-serde", "hex", "num-derive", @@ -860,5 +1043,6 @@ dependencies = [ "socket2", "tokio", "winapi", + "www-authenticate", "zerotier-core", ] diff --git a/service/Cargo.toml b/service/Cargo.toml index 49db1ee9c..c719c140d 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -23,6 +23,7 @@ num-derive = "0" hyper = { version = "0", features = ["http1", "runtime", "server", "client", "tcp", "stream"] } socket2 = { version = "0", features = ["reuseport", "unix", "pair"] } dialoguer = "0" +www-authenticate = "0" [target."cfg(windows)".dependencies] winapi = { version = "0.3.9", features = ["handleapi", "ws2ipdef", "ws2tcpip"] } diff --git a/service/src/commands/status.rs b/service/src/commands/status.rs index e69de29bb..068be7b83 100644 --- a/service/src/commands/status.rs +++ b/service/src/commands/status.rs @@ -0,0 +1,32 @@ +/* + * Copyright (c)2013-2021 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2026-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +use std::str::FromStr; +use std::sync::Arc; + +use hyper::Uri; +use crate::store::Store; + +pub(crate) fn run(store: Arc) -> i32 { + crate::webclient::command(store.clone(), move |client, uri| { + async move { + let mut res = client.get(uri).await?; + println!("status: {}", res.status().as_str()); + let body = hyper::body::to_bytes(res.body_mut()).await?; + String::from_utf8(body.to_vec()).map(|body| { + println!("body: {}", body.as_str()); + }); + Ok(0) + } + }) +} diff --git a/service/src/fastudpsocket.rs b/service/src/fastudpsocket.rs index e261144e4..bb67d20a9 100644 --- a/service/src/fastudpsocket.rs +++ b/service/src/fastudpsocket.rs @@ -233,7 +233,7 @@ impl FastUDPSocket { let mut buf = Buffer::new(); let read_length = fast_udp_socket_recvfrom(&thread_socket, &mut buf, &mut from_address); if read_length > 0 { - unsafe { buf.set_len(read_length as usize); } + buf.set_len(read_length as usize); handler_copy(&thread_socket, &from_address, buf); } else if read_length < 0 { break; diff --git a/service/src/getifaddrs.rs b/service/src/getifaddrs.rs index 8838ac63f..b4d7086c5 100644 --- a/service/src/getifaddrs.rs +++ b/service/src/getifaddrs.rs @@ -48,12 +48,12 @@ pub(crate) fn for_each_address(mut f: F) { let mut netmask_bits: u16 = 0; if !(*i).ifa_netmask.is_null() { if sa_family == osdep::AF_INET as u8 { - let mut a = (*(*i).ifa_netmask.cast::()).sin_addr.s_addr as u32; + let a = (*(*i).ifa_netmask.cast::()).sin_addr.s_addr as u32; netmask_bits = a.leading_ones() as u16; } else if sa_family == osdep::AF_INET6 as u8 { let a = s6_addr_as_ptr(&((*(*i).ifa_netmask.cast::()).sin6_addr)).cast::(); for i in 0..16 as isize { - let mut b = *a.offset(i); + let b = *a.offset(i); if b == 0xff { netmask_bits += 8; } else { diff --git a/service/src/log.rs b/service/src/log.rs index 49a3ba99a..d68359af5 100644 --- a/service/src/log.rs +++ b/service/src/log.rs @@ -71,7 +71,7 @@ impl Log { let log_line = format!("{}[{}] {}{}\n", l.prefix.as_str(), chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(), pfx, s); if !l.path.is_empty() { if l.file.is_none() { - let mut f = OpenOptions::new().read(true).write(true).create(true).open(l.path.as_str()); + let f = OpenOptions::new().read(true).write(true).create(true).open(l.path.as_str()); if f.is_err() { return; } @@ -94,7 +94,7 @@ impl Log { let _ = std::fs::rename(l.path.as_str(), old_path.as_str()); let _ = std::fs::remove_file(l.path.as_str()); // should fail - let mut f = OpenOptions::new().read(true).write(true).create(true).open(l.path.as_str()); + let f = OpenOptions::new().read(true).write(true).create(true).open(l.path.as_str()); if f.is_err() { return; } diff --git a/service/src/main.rs b/service/src/main.rs index 6dac66de8..71a17150c 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -22,6 +22,7 @@ mod network; mod vnic; mod service; mod utils; +mod webclient; mod weblistener; #[allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, improper_ctypes)] @@ -291,9 +292,8 @@ fn main() { .get_matches_from_safe(std::env::args()); if args.is_err() { let e = args.err().unwrap(); - match e.kind { - ErrorKind::HelpDisplayed => {} - _ => { print_help(); } + if e.kind != ErrorKind::HelpDisplayed { + print_help(); } std::process::exit(1); } @@ -306,22 +306,22 @@ fn main() { }; std::process::exit(match cli_args.subcommand() { - ("help", None) => { + ("help", _) => { print_help(); 0 } - ("version", None) => { + ("version", _) => { let ver = zerotier_core::version(); println!("{}.{}.{}", ver.0, ver.1, ver.2); 0 } - ("status", None) => { 0 } + ("status", _) => crate::commands::status::run(make_store(&cli_args)), ("set", Some(sub_cli_args)) => { 0 } ("peer", Some(sub_cli_args)) => { 0 } ("network", Some(sub_cli_args)) => { 0 } ("join", Some(sub_cli_args)) => { 0 } ("leave", Some(sub_cli_args)) => { 0 } - ("service", None) => { + ("service", _) => { let store = make_store(&cli_args); drop(cli_args); // free memory service::run(store) diff --git a/service/src/service.rs b/service/src/service.rs index ab86248e0..8c7ff5f7c 100644 --- a/service/src/service.rs +++ b/service/src/service.rs @@ -221,7 +221,6 @@ async fn run_async(store: Arc, log: Arc, local_config: Arc, log: Arc, local_config: Arc, log: Arc, local_config: Arc, log: Arc, local_config: Arc std::io::Result<()> { - let ps = port.to_string(); - self.write_file("zerotier.port", ps.as_bytes()) - } - - /// Read zerotier.port and return port or 0 if not found or not readable. - pub fn read_port(&self) -> u16 { - self.read_file_str("zerotier.port").map_or_else(|_| { - 0_u16 - },|s| { - u16::from_str(s.trim()).unwrap_or(0_u16) - }) - } - /// Write zerotier.pid file with current process's PID. #[cfg(unix)] pub fn write_pid(&self) -> std::io::Result<()> { let pid = unsafe { crate::osdep::getpid() }.to_string(); - self.write_file(self.base_path.join("zerotier.pid").to_str().unwrap(), pid.as_bytes()) + self.write_file("zerotier.pid", pid.as_bytes()) } /// Erase zerotier.pid if present. @@ -276,6 +261,26 @@ impl Store { let _ = std::fs::remove_file(self.base_path.join("zerotier.pid")); } + /// Write a string to zerotier.uri + pub fn write_uri(&self, uri: &str) -> std::io::Result<()> { + self.write_file("zerotier.uri", uri.as_bytes()) + } + + /// Load zerotier.uri if present + pub fn load_uri(&self) -> std::io::Result { + let uri = String::from_utf8(self.read_file("zerotier.uri")?); + uri.map_or_else(|e| { + Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string())) + }, |uri| { + let uri = hyper::Uri::from_str(uri.trim()); + uri.map_or_else(|e| { + Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string())) + }, |uri| { + Ok(uri) + }) + }) + } + /// Load a ZeroTier core object. pub fn load_object(&self, obj_type: &StateObjectType, obj_id: &[u64]) -> std::io::Result> { let obj_path = self.make_obj_path_internal(&obj_type, obj_id); diff --git a/service/src/utils.rs b/service/src/utils.rs index 32cf0c10b..cc0c01d02 100644 --- a/service/src/utils.rs +++ b/service/src/utils.rs @@ -21,6 +21,7 @@ use std::path::Path; use zerotier_core::{Identity, Locator}; use crate::osdep; +use crate::osdep::time; #[inline(always)] pub(crate) fn sha512>(data: T) -> [u8; 64] { @@ -107,3 +108,33 @@ pub(crate) fn read_locator(input: &str) -> Result { parse_func(input) } } + +/// Create a new HTTP authorization nonce by encrypting the current time. +/// The key used to encrypt the current time is random and is re-created for +/// each execution of the process. By decrypting this nonce when it is returned, +/// the client and server may check the age of a digest auth exchange. +pub(crate) fn create_http_auth_nonce(timestamp: u64) -> String { + let mut nonce_plaintext: [u64; 2] = [timestamp, 12345]; // the second u64 is arbitrary and unused + unsafe { + osdep::encryptHttpAuthNonce(nonce_plaintext.as_mut_ptr().cast()); + hex::encode(*nonce_plaintext.as_ptr().cast::<[u8; 16]>()) + } +} + +/// Decrypt HTTP auth nonce encrypted by this process and return the timestamp. +/// This returns zero if the input was not valid. +pub(crate) fn decrypt_http_auth_nonce(nonce: &str) -> u64 { + let nonce = hex::decode(nonce.trim()); + if nonce.is_err() { + return 0; + } + let mut nonce = nonce.unwrap(); + if nonce.len() != 16 { + return 0; + } + unsafe { + osdep::decryptHttpAuthNonce(nonce.as_mut_ptr().cast()); + let nonce = *nonce.as_ptr().cast::<[u64; 2]>(); + nonce[0] + } +} diff --git a/service/src/webclient.rs b/service/src/webclient.rs new file mode 100644 index 000000000..133985c13 --- /dev/null +++ b/service/src/webclient.rs @@ -0,0 +1,44 @@ +/* + * Copyright (c)2013-2021 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2026-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +use std::str::FromStr; +use std::time::Duration; +use hyper::Uri; +use std::sync::Arc; +use crate::store::Store; +use std::future::Future; + +/// Launch the supplied function inside the tokio runtime. +/// The return value of this function should be the process exit code. +/// This is for implementation of commands that query the HTTP API, not HTTP +/// requests from a running server. +pub(crate) fn command<'a, R: Future>, F: FnOnce(Arc>, hyper::Uri) -> R>(store: Arc, func: F) -> i32 { + let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap(); + let code = rt.block_on(async move { + let uri = store.load_uri(); + if uri.is_err() { + println!("ERROR: unable to read 'zerotier.uri' to get local HTTP API address."); + 1 + } else { + let f = func(Arc::new(hyper::Client::new()), uri.unwrap()); + f.await.map_or_else(|e| { + println!("ERROR: HTTP request failed: {}", e.to_string()); + 1 + }, |code| { + code + }) + } + }); + rt.shutdown_timeout(Duration::from_millis(10)); + code +} diff --git a/service/src/weblistener.rs b/service/src/weblistener.rs index b802661b9..995c081f4 100644 --- a/service/src/weblistener.rs +++ b/service/src/weblistener.rs @@ -42,7 +42,7 @@ pub(crate) struct WebListener { impl WebListener { /// Create a new "background" TCP WebListener using the current tokio reactor async runtime. - pub fn new(_device_name: &str, addr: SocketAddr, service: &Service) -> Result> { + pub async fn new(_device_name: &str, addr: SocketAddr, service: &Service) -> Result> { let listener = if addr.is_ipv4() { let listener = socket2::Socket::new(socket2::Domain::ipv4(), socket2::Type::stream(), Some(socket2::Protocol::tcp())); if listener.is_err() {