summaryrefslogtreecommitdiff
path: root/src/rexmpp_socks.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/rexmpp_socks.rs')
-rw-r--r--src/rexmpp_socks.rs180
1 files changed, 180 insertions, 0 deletions
diff --git a/src/rexmpp_socks.rs b/src/rexmpp_socks.rs
new file mode 100644
index 0000000..1828de1
--- /dev/null
+++ b/src/rexmpp_socks.rs
@@ -0,0 +1,180 @@
+// For rustc and libstd-rust version 1.48
+
+use std::os::raw::{c_int, c_char};
+use std::ffi::CStr;
+use std::net::TcpStream;
+use std::os::unix::io::{FromRawFd, IntoRawFd};
+use std::io::Write;
+use std::io::Read;
+use std::io::ErrorKind;
+use std::convert::TryFrom;
+
+const REXMPP_SOCKS_BUF_LEN: usize = 300;
+
+#[derive(PartialEq)]
+#[repr(C)]
+enum SocksIOState {
+ Writing,
+ Reading
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+enum SocksStage {
+ Auth,
+ Cmd,
+ Done
+}
+
+#[derive(PartialEq)]
+#[repr(C)]
+enum SocksErr {
+ Connected,
+ EAgain,
+ ETCP,
+ EReply,
+ EVersion,
+ ESocks,
+ EHost
+}
+
+#[repr(C)]
+pub struct RexmppSocks {
+ fd: c_int,
+ host: *const c_char,
+ port: u16,
+ stage: SocksStage,
+ io_state: SocksIOState,
+ socks_error: c_int,
+ buf: [u8; REXMPP_SOCKS_BUF_LEN],
+ buf_len: usize,
+ buf_sent: usize
+}
+
+#[no_mangle]
+extern "C" fn rexmpp_socks_proceed (s : &mut RexmppSocks) -> SocksErr {
+ if s.io_state == SocksIOState::Writing {
+ let mut stream : TcpStream = unsafe { TcpStream::from_raw_fd(s.fd) };
+ let ret = stream.write(&s.buf[s.buf_sent .. s.buf_len]);
+ // Make sure the connection is not closed by TcpStream.
+ TcpStream::into_raw_fd(stream);
+ match ret {
+ Ok(sent) => {
+ s.buf_sent += sent;
+ if s.buf_len == s.buf_sent {
+ s.buf_len = 0;
+ s.io_state = SocksIOState::Reading;
+ }
+ }
+ Err(error) => match error.kind() {
+ ErrorKind::WouldBlock => return SocksErr::EAgain,
+ _ => return SocksErr::ETCP
+ }
+ }
+ } else if s.io_state == SocksIOState::Reading {
+ let mut stream : TcpStream = unsafe { TcpStream::from_raw_fd(s.fd) };
+ let ret = stream.read(&mut s.buf[s.buf_len ..]);
+ // Make sure the connection is not closed by TcpStream.
+ TcpStream::into_raw_fd(stream);
+ match ret {
+ Ok(received) => {
+ s.buf_len += received;
+ if s.buf[0] != 5 {
+ return SocksErr::EVersion;
+ }
+ if s.buf_len >= 2 {
+ s.socks_error = s.buf[1].into();
+ }
+ if s.stage == SocksStage::Auth {
+ if s.buf_len > 2 {
+ return SocksErr::EReply;
+ }
+ if s.buf_len == 2 {
+ if s.socks_error != 0 {
+ return SocksErr::ESocks;
+ }
+ s.buf[0] = 5; // SOCKS version 5
+ s.buf[1] = 1; // Connect
+ s.buf[2] = 0; // Reserved
+ s.buf[3] = 3; // Domain name (todo: IP addresses)
+ let host_cstr : &CStr =
+ unsafe { CStr::from_ptr(s.host) };
+ let host_len = host_cstr.to_bytes().len();
+ match u8::try_from(host_len) {
+ Ok(u) => { s.buf[4] = u }
+ Err(_) => return SocksErr::EHost
+ }
+ s.buf[5 .. 5 + host_len].
+ copy_from_slice(&host_cstr.to_bytes());
+ s.buf[5 + host_len .. 7 + host_len].
+ copy_from_slice(&s.port.to_be_bytes());
+ s.buf_len = 7 + host_len;
+ s.buf_sent = 0;
+ s.stage = SocksStage::Cmd;
+ s.io_state = SocksIOState::Writing;
+ return rexmpp_socks_proceed(s);
+ }
+ } else if s.stage == SocksStage::Cmd {
+ if s.buf_len >= 5 {
+ let mut full_len : usize = 6;
+ match s.buf[3] {
+ // IPv4
+ 1 => full_len += 4,
+ // Domain name
+ 3 => full_len += usize::from(s.buf[4]) + 1,
+ // IPv6
+ 4 => full_len += 16,
+ _ => return SocksErr::EReply
+ }
+ if s.buf_len > full_len {
+ return SocksErr::EReply;
+ }
+ if s.buf_len == full_len {
+ if s.socks_error != 0 {
+ return SocksErr::ESocks;
+ }
+ // We are done
+ s.stage = SocksStage::Done;
+ return SocksErr::Connected;
+ }
+ }
+ }
+ }
+ Err(error) => match error.kind() {
+ ErrorKind::WouldBlock => return SocksErr::EAgain,
+ _ => return SocksErr::ETCP
+ }
+ }
+ }
+ return SocksErr::EAgain
+}
+
+#[no_mangle]
+extern "C" fn rexmpp_socks_init (
+ s : &mut RexmppSocks,
+ fd: c_int,
+ host: *const c_char,
+ port: u16
+)
+ -> SocksErr
+{
+ s.fd = fd;
+ s.host = host;
+ s.port = port;
+ s.socks_error = 0;
+
+ let host_cstr : &CStr = unsafe { CStr::from_ptr(host) };
+ if host_cstr.to_bytes().len() > 255 {
+ return SocksErr::EHost;
+ }
+
+ // Request authentication
+ s.stage = SocksStage::Auth;
+ s.io_state = SocksIOState::Writing;
+ s.buf[0] = 5; // SOCKS version 5
+ s.buf[1] = 1; // 1 supported method
+ s.buf[2] = 0; // No authentication required
+ s.buf_len = 3;
+ s.buf_sent = 0;
+ return rexmpp_socks_proceed(s);
+}