Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace deprecated net2 crate in favor of the socket2 crate (fix #231) #233

Merged
merged 3 commits into from
Jun 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ io-enum = "0.2.1"
lru = "0.4.3"
mysql_common = "0.22.0"
native-tls = "0.2.3"
net2 = "~0.2"
socket2 = "0.3.12"
percent-encoding = "2.1.0"
serde = "1"
serde_json = "1"
Expand All @@ -47,7 +47,6 @@ url = "2.1"

[target.'cfg(target_os = "windows")'.dependencies]
named_pipe = "~0.4"
winapi = "~0.3"

[target.'cfg(unix)'.dependencies]
libc = "0.2"
Expand Down
3 changes: 3 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ jobs:
--ssl-cert=/root/tests/server-cert.pem \
--ssl-key=/root/tests/server-key.pem
while ! nc -W 1 localhost 3307 | grep -q -P '.+'; do sleep 1; done
docker logs container
docker exec container bash -l -c "mysql -uroot -ppassword -e 'SHOW VARIABLES LIKE \"%ssl%\"'"
docker exec container bash -l -c "ls -la /root/tests"
displayName: Run MariaDb in Docker
- bash: |
docker exec container bash -l -c "apt-get update"
Expand Down
181 changes: 15 additions & 166 deletions src/io/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,13 @@
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use net2::{TcpBuilder, TcpStreamExt};
#[cfg(unix)]
use nix::{
errno::Errno,
poll::{self, PollFlags},
sys::socket,
};
#[cfg(target_os = "windows")]
use winapi::um::winsock2::*;
use socket2::{Domain, Socket, Type};

#[cfg(unix)]
use std::os::unix::prelude::*;
use std::{
io, mem,
io,
net::{SocketAddr, TcpStream, ToSocketAddrs},
time::Duration,
};
#[cfg(target_os = "windows")]
use std::{os::raw::*, os::windows::prelude::*, ptr};

pub struct MyTcpBuilder<T> {
address: T,
Expand Down Expand Up @@ -102,169 +90,30 @@ impl<T: ToSocketAddrs> MyTcpBuilder<T> {
.to_socket_addrs()?
.fold(Err(err), |prev, sock_addr| {
prev.or_else(|_| {
let builder = if sock_addr.is_ipv4() {
TcpBuilder::new_v4()?
let socket = if sock_addr.is_ipv4() {
Socket::new(Domain::ipv4(), Type::stream(), None)?
} else {
TcpBuilder::new_v6()?
Socket::new(Domain::ipv6(), Type::stream(), None)?
};
if let Some(bind_address) = bind_address {
if bind_address.is_ipv4() == sock_addr.is_ipv4() {
builder.bind(bind_address)?;
socket.bind(&bind_address.into())?;
}
}
if let Some(connect_timeout) = connect_timeout {
#[cfg(unix)]
connect_fd_timeout(builder.as_raw_fd(), &sock_addr, connect_timeout)?;
#[cfg(target_os = "windows")]
connect_fd_timeout(builder.as_raw_socket(), &sock_addr, connect_timeout)?;
builder.to_tcp_stream()
socket.connect_timeout(&sock_addr.into(), connect_timeout)?;
} else {
builder.connect(sock_addr)
socket.connect(&sock_addr.into())?;
}
Ok(socket)
})
})
.and_then(|stream| {
stream.set_read_timeout(read_timeout)?;
stream.set_write_timeout(write_timeout)?;
stream.set_keepalive_ms(keepalive_time_ms)?;
stream.set_nodelay(nodelay)?;
Ok(stream)
.and_then(|socket| {
socket.set_read_timeout(read_timeout)?;
socket.set_write_timeout(write_timeout)?;
socket.set_keepalive(keepalive_time_ms.map(|x| Duration::from_millis(x as u64)))?;
socket.set_nodelay(nodelay)?;
Ok(socket.into_tcp_stream())
})
}
}

#[cfg(unix)]
fn set_non_blocking(fd: RawFd, non_blocking: bool) -> io::Result<()> {
let stream = unsafe { TcpStream::from_raw_fd(fd) };
let result = stream.set_nonblocking(non_blocking);
mem::forget(stream);
result
}

#[cfg(target_os = "windows")]
fn set_non_blocking(socket: RawSocket, non_blocking: bool) -> io::Result<()> {
let stream = unsafe { TcpStream::from_raw_socket(socket) };
let result = stream.set_nonblocking(non_blocking);
mem::forget(stream);
result
}

#[cfg(unix)]
fn connect_fd_timeout(fd: RawFd, sock_addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
set_non_blocking(fd, true)?;

let inet_addr = socket::InetAddr::from_std(sock_addr);
let sock_addr = socket::SockAddr::Inet(inet_addr);
match socket::connect(fd, &sock_addr) {
Ok(_) => (),
Err(err) => match err {
::nix::Error::Sys(Errno::EALREADY) | ::nix::Error::Sys(Errno::EINPROGRESS) => (),
::nix::Error::Sys(errno) => return Err(io::Error::from_raw_os_error(errno as i32)),
_ => return Err(io::Error::new(io::ErrorKind::Other, err)),
},
}

let socket_fd = fd;
let mut poll_fds = [poll::PollFd::new(socket_fd, PollFlags::POLLIN)];
let timeout_millis = timeout.as_secs() as i32 * 1000;

let poll_res = poll::poll(&mut poll_fds, timeout_millis);

let poll_res = poll_res.map_err(|err| match err {
::nix::Error::Sys(errno) => io::Error::from_raw_os_error(errno as i32),
_ => io::Error::new(io::ErrorKind::Other, err),
})?;

if poll_res == -1 {
return Err(io::Error::last_os_error());
}

if poll_res != 1 {
return Err(io::ErrorKind::TimedOut.into());
}

let socket_error_code = socket::getsockopt(socket_fd, socket::sockopt::SocketError);

let socket_error_code = socket_error_code.map_err(|err| match err {
::nix::Error::Sys(errno) => io::Error::from_raw_os_error(errno as i32),
_ => io::Error::new(io::ErrorKind::Other, err),
})?;

if socket_error_code != 0 {
return Err(io::Error::from_raw_os_error(socket_error_code));
}

set_non_blocking(fd, false)
}

#[cfg(target_os = "windows")]
fn connect_fd_timeout(
socket: RawSocket,
sock_addr: &SocketAddr,
timeout: Duration,
) -> io::Result<()> {
set_non_blocking(socket, true)?;
let (name, name_len) = match *sock_addr {
SocketAddr::V4(ref a) => (a as *const _ as *const _, mem::size_of_val(a) as c_int),
SocketAddr::V6(ref a) => (a as *const _ as *const _, mem::size_of_val(a) as c_int),
};
let result = unsafe { connect(socket as usize, name, name_len) };
if result == SOCKET_ERROR {
let err = io::Error::last_os_error();
match err.raw_os_error() {
Some(WSAEWOULDBLOCK) => {
let mut write_fds = fd_set {
fd_count: 1,
fd_array: [0; FD_SETSIZE],
};
write_fds.fd_array[0] = socket as usize;
let mut err_fds = write_fds.clone();
let timeout = timeval {
tv_sec: timeout.as_secs() as c_long,
tv_usec: 0,
};

let result =
unsafe { select(0, ptr::null_mut(), &mut write_fds, &mut err_fds, &timeout) };

if result == 0 {
return Err(io::ErrorKind::TimedOut.into());
} else if result == SOCKET_ERROR {
return Err(io::Error::last_os_error());
} else {
let mut error = None;
for i in 0..(err_fds.fd_count as usize) {
if err_fds.fd_array[i] == socket as usize {
error = Some(true);
}
}
for i in 0..(write_fds.fd_count as usize) {
if write_fds.fd_array[i] == socket as usize {
error = Some(false);
}
}
match error {
Some(false) => (),
Some(true) => {
let mut opt_val = 0i32;
let mut opt_len = mem::size_of::<i32>() as c_int;
let result = unsafe {
getsockopt(
socket as usize,
SOL_SOCKET,
SO_ERROR,
&mut opt_val as *mut _ as *mut _,
&mut opt_len,
)
};
return Err(io::Error::from_raw_os_error(result));
}
None => unreachable!(),
}
}
}
_ => return Err(err),
}
}
set_non_blocking(socket, false)
}
Binary file removed tests/ca-cert.cer
Binary file not shown.
40 changes: 18 additions & 22 deletions tests/ca-cert.pem
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
-----BEGIN CERTIFICATE-----
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
+eCxbV7UxLkjC5z6ljpxpLlnoYFDRAepCjKvXg==
-----END CERTIFICATE-----
36 changes: 18 additions & 18 deletions tests/client-cert.pem
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDOjCCAiICCQC6TCSkjdX4rzANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJS
VTEPMA0GA1UECBMGTW9zY293MQ8wDQYDVQQHEwZNb3Njb3cxEDAOBgNVBAoTB0ZT
SSBSU0wxIjAgBgNVBAMTGVJ1c3QgbXlzcWwgZHJpdmVyIHRlc3QgQ0EwHhcNMTYx
MTExMTgxMzMyWhcNMzAwNzIxMTgxMzMyWjBZMQswCQYDVQQGEwJBVTETMBEGA1UE
CBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
MRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCgcITynCAG+50LjeUPc/5BuqUfrouuNvUwnyuRL7gkgVJooi9XfYch5caG
TehpDoIaj4TydKJOg/juz0a82TzoAYUum8xtBVKWI/GUQauaJVw/LKyYL0WYdoRb
rxdApIzG3B/j80MLWCwjCdPiN+bMXxUtpNAIsP0o+v6j/BpGXdwcvc/WoyeC61iw
aayWljlf1Fb0LQDCQ//51Jea5wVbsFq4r5/06Vc4qWhkrDXcFjRqNPevPoMn4sr+
pgC8AzQWOXnCN9VEaqg5M3POLGZsoup0B23y/NeaoBCYzL6DpbgLZmv6ChDmrmY+
u89JxLi67CItvukJSo+Br32vKJCLAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAIyU
/65G9gaeaILqqe9phUHvqERDhgKIyMxAUzXvKaaZwLDOvKNyhZMjSA1/RDeRU+Y1
L2FcsEpMW9m9pcn5QhCao1XlgX278RAE2vbH8FA9UvbI89hQdozUhTkF+mR33xka
0nUSzefGeeIqnLwG90tTrDFASWrhLeO9/e0+Je5//8sHAfkT1l60RGNBGo0Du5ue
k4k6cazRf46X4po41Dd06SBuWkohRyn2736islZ4UMKVSZEOE1Hd8EPKcoVl9sQh
xkRH6DXj3b9wS77Y6H8e1BhNFRFW9Lyizsgb5PAYfbaLnM5FeX0GRqR3oa7YDTdH
RRQcyNoDMZQ5+LhOMV0=
MIIDTDCCAjQCCQDTbgJSsvM7zTANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJS
VTEPMA0GA1UECAwGTW9zY293MQ8wDQYDVQQHDAZNb3Njb3cxEDAOBgNVBAoMB0ZT
SSBSU0wxIjAgBgNVBAMMGVJ1c3QgbXlzcWwgZHJpdmVyIHRlc3QgQ0EwIBcNMjAw
NjI5MDk1MjIwWhgPMjE5OTEyMDQwOTUyMjBaMGkxCzAJBgNVBAYTAlJVMQ8wDQYD
VQQIDAZNb3Njb3cxDzANBgNVBAcMBk1vc2NvdzEQMA4GA1UECgwHRlNJIFJTTDEm
MCQGA1UEAwwdUnVzdCBteXNxbCBkcml2ZXIgdGVzdCBDbGllbnQwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgcITynCAG+50LjeUPc/5BuqUfrouuNvUw
nyuRL7gkgVJooi9XfYch5caGTehpDoIaj4TydKJOg/juz0a82TzoAYUum8xtBVKW
I/GUQauaJVw/LKyYL0WYdoRbrxdApIzG3B/j80MLWCwjCdPiN+bMXxUtpNAIsP0o
+v6j/BpGXdwcvc/WoyeC61iwaayWljlf1Fb0LQDCQ//51Jea5wVbsFq4r5/06Vc4
qWhkrDXcFjRqNPevPoMn4sr+pgC8AzQWOXnCN9VEaqg5M3POLGZsoup0B23y/Nea
oBCYzL6DpbgLZmv6ChDmrmY+u89JxLi67CItvukJSo+Br32vKJCLAgMBAAEwDQYJ
KoZIhvcNAQELBQADggEBAFP5LUeIn8SLfDxUs3QR5FDyFJJ2vaYrKZ+psp/3OtrP
uPLIaSoKqcEXRzBI5giKaVslYQQSYLkfp4K8u8A7KoNJQaPDCrNSnAqCppjnkJQy
HhC6ZxeCM+ctZWG5SDoIMH4088lOO/x4rIFDJwVWkksnRDKzI7VzYfolxbg4Y4ty
U+q1uh7v2P4mTQnpxuCLqTHpymrN5ygky1XsbyKyNHO7pDrEbAAghNyA2tWm21JQ
/+CeCWI34nN5Db3aSLNCd3a5Qj/ZblzU4efMmoNsupk7AS79IaLa+LmtQhUufUe2
UJpbnECyCFdQjDbgOccLe3GhaSYJXKnSSlC7YLlyZjk=
-----END CERTIFICATE-----
Binary file modified tests/client.p12
Binary file not shown.
36 changes: 18 additions & 18 deletions tests/server-cert.pem
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDOjCCAiICCQC6TCSkjdX4sDANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJS
VTEPMA0GA1UECBMGTW9zY293MQ8wDQYDVQQHEwZNb3Njb3cxEDAOBgNVBAoTB0ZT
SSBSU0wxIjAgBgNVBAMTGVJ1c3QgbXlzcWwgZHJpdmVyIHRlc3QgQ0EwHhcNMTYx
MTExMTgxNDIwWhcNMzAwNzIxMTgxNDIwWjBZMQswCQYDVQQGEwJBVTETMBEGA1UE
CBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
MRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDbuLksnUS8wLLjJsdekXQfYU/YTuQCEdcSBL4d1eVFwGUW5no9RdqarS03
rRZOhEBwKhPSwJKiy7ARmIN1+UcMT3U1j5zUiq6g4668tI3ZkbcUKdgjhmqNd6HT
eD3yveM+JpN5fuo4Gk/0TGgZ5VWLvFzYjwuU6npkcM2khsoFo49ubWTgcanF+y30
lIPPO8Yax/Qr31P0CkxQEFudRRf0hi4uUi2xWJVCRkV0j8Vnza2OQkx8o6TOecgL
pc4+lNk37vwYPdqSdG7M5DoLZGmf1X+4mvvZ7fN/OYTAr+M/4HfW2bLjVmKlG4fU
YHzGiFMQp3KVHKTJ3ZoJ5uplKz5TAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAJ6L
jbAJhUr1v4FofRX9imRR7VJ4Jph5mGNcwK19RUTzsYPx8arYgaxT4HRtHm9cbuRR
Ds799bENYBYydgqsucg3Im7aLTuVZbMtzmg1EQ+P/cVBlaKemqPwUOi4IUqSBNjN
3nOJr09j0CfFM2WETk1fXYP6JlDxGhfrNhWX5YfF9lqSvJT0E0E4s2OjCQRehiQl
r58osTXz44LFBBT6Rw846Gw2rutKDNq8A8ik9PWu66dN/MTULiIufzDmxwxFlnkA
0RW5Nw16K+YPrlKA2OyNxdURbo98FXW2WqghEqFL+KRwCE7fK2xUIffPX+Y5fb/q
6TKPOdcyFb/xn12NUv8=
MIIDTDCCAjQCCQDTbgJSsvM7zjANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJS
VTEPMA0GA1UECAwGTW9zY293MQ8wDQYDVQQHDAZNb3Njb3cxEDAOBgNVBAoMB0ZT
SSBSU0wxIjAgBgNVBAMMGVJ1c3QgbXlzcWwgZHJpdmVyIHRlc3QgQ0EwIBcNMjAw
NjI5MDk1MzQwWhgPMjE5OTEyMDQwOTUzNDBaMGkxCzAJBgNVBAYTAlJVMQ8wDQYD
VQQIDAZNb3Njb3cxDzANBgNVBAcMBk1vc2NvdzEQMA4GA1UECgwHRlNJIFJTTDEm
MCQGA1UEAwwdUnVzdCBteXNxbCBkcml2ZXIgdGVzdCBTZXJ2ZXIwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDbuLksnUS8wLLjJsdekXQfYU/YTuQCEdcS
BL4d1eVFwGUW5no9RdqarS03rRZOhEBwKhPSwJKiy7ARmIN1+UcMT3U1j5zUiq6g
4668tI3ZkbcUKdgjhmqNd6HTeD3yveM+JpN5fuo4Gk/0TGgZ5VWLvFzYjwuU6npk
cM2khsoFo49ubWTgcanF+y30lIPPO8Yax/Qr31P0CkxQEFudRRf0hi4uUi2xWJVC
RkV0j8Vnza2OQkx8o6TOecgLpc4+lNk37vwYPdqSdG7M5DoLZGmf1X+4mvvZ7fN/
OYTAr+M/4HfW2bLjVmKlG4fUYHzGiFMQp3KVHKTJ3ZoJ5uplKz5TAgMBAAEwDQYJ
KoZIhvcNAQELBQADggEBAKTnuJXuDMwezrF5ktLj6CSxQJWBkcuTOivYwjL062lD
fn57Rgpt16uAVbstI+f3BlMSIruaGHpYFetIx47TeJrSfjZvlqGU1D6Mc8Y+D8Iw
XvuAnQmDxZB8VRJvk1W+UkfYdq+Vkao6eKBcZNkpVIn5OGEUggjPxusvnR1kbfBh
x2rtU6nzkzFr48RRfadmGmbj85q/dDOe3o7rzZIJEAwARql1G5yWQb34poyGg0EC
lndzkVJ/Xw+obrYkyzlgjuDQNLcc+EFSwyNcXvzaO+XKNw+/mo4PaAa39DMSnmPZ
6saUFJ8obDZN6YP2WEat+zurKCl+o8Tbo1EKqPh0pus=
-----END CERTIFICATE-----