Added SSL support, but it's broken because of the duplicate SslStream creations.

This commit is contained in:
Aaron Weiss 2014-11-08 17:35:19 -05:00
parent a79a1fc033
commit a903dd9571
8 changed files with 141 additions and 12 deletions

View file

@ -9,3 +9,12 @@ keywords = ["irc", "client", "thread-safe"]
documentation = "http://www.rust-ci.org/aaronweiss74/irc/doc/irc/" documentation = "http://www.rust-ci.org/aaronweiss74/irc/doc/irc/"
repository = "https://github.com/aaronweiss74/irc" repository = "https://github.com/aaronweiss74/irc"
readme = "README.md" readme = "README.md"
[features]
ssl = ["openssl"]
[dependencies.openssl]
git = "https://github.com/sfackler/rust-openssl.git"
optional = true

View file

@ -16,6 +16,7 @@ fn main() {
password: "".into_string(), password: "".into_string(),
server: "irc.fyrechat.net".into_string(), server: "irc.fyrechat.net".into_string(),
port: 6667, port: 6667,
use_ssl: false,
channels: vec!("#vana".into_string()), channels: vec!("#vana".into_string()),
options: HashMap::new(), options: HashMap::new(),
}; };

36
examples/simple_ssl.rs Normal file
View file

@ -0,0 +1,36 @@
#![feature(if_let)]
#![feature(slicing_syntax)]
extern crate irc;
use std::collections::HashMap;
use irc::data::config::Config;
use irc::server::{IrcServer, Server};
use irc::server::utils::Wrapper;
fn main() {
let config = Config {
owners: vec!("awe".into_string()),
nickname: "pickles".into_string(),
username: "pickles".into_string(),
realname: "pickles".into_string(),
password: "".into_string(),
server: "irc.fyrechat.net".into_string(),
port: 6697,
use_ssl: true,
channels: vec!("#vana".into_string()),
options: HashMap::new(),
};
let irc_server = IrcServer::from_config(config).unwrap();
let server = Wrapper::new(&irc_server);
server.identify().unwrap();
for message in server.iter() {
print!("{}", message.into_string());
if message.command[] == "PRIVMSG" {
if let Some(msg) = message.suffix {
if msg.contains("pickles") {
server.send_privmsg(message.args[0][], "Hi!").unwrap();
}
}
}
}
}

View file

@ -1 +1 @@
echo "{\"owners\": [\"test\"],\"nickname\": \"test\",\"username\": \"test\",\"realname\": \"test\",\"password\": \"\",\"server\": \"irc.test.net\",\"port\": 6667,\"channels\": [\"#test\", \"#test2\"],\"options\": {}}" > config.json echo "{\"owners\": [\"test\"],\"nickname\": \"test\",\"username\": \"test\",\"realname\": \"test\",\"password\": \"\",\"server\": \"irc.test.net\",\"port\": 6667,\"use_ssl\": false,\"channels\": [\"#test\", \"#test2\"],\"options\": {}}" > config.json

View file

@ -2,8 +2,11 @@
#![experimental] #![experimental]
use std::sync::{Mutex, MutexGuard}; use std::sync::{Mutex, MutexGuard};
use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream};
#[cfg(feature = "ssl")] use std::io::{IoError, OtherIoError};
use data::kinds::{IrcWriter, IrcReader}; use data::kinds::{IrcWriter, IrcReader};
use data::message::Message; use data::message::Message;
#[cfg(feature = "ssl")] use openssl::ssl::{SslContext, SslStream, Tlsv1};
#[cfg(feature = "ssl")] use openssl::ssl::error::SslError;
/// A thread-safe connection. /// A thread-safe connection.
#[experimental] #[experimental]
@ -15,9 +18,74 @@ pub struct Connection<T, U> where T: IrcWriter, U: IrcReader {
impl Connection<BufferedWriter<TcpStream>, BufferedReader<TcpStream>> { impl Connection<BufferedWriter<TcpStream>, BufferedReader<TcpStream>> {
/// Creates a thread-safe TCP connection to the specified server. /// Creates a thread-safe TCP connection to the specified server.
#[experimental] #[experimental]
pub fn connect(host: &str, port: u16) -> IoResult<Connection<BufferedWriter<TcpStream>, BufferedReader<TcpStream>>> { pub fn connect(host: &str, port: u16) -> IoResult<Connection<BufferedWriter<NetStream>, BufferedReader<NetStream>>> {
let socket = try!(TcpStream::connect(format!("{}:{}", host, port)[])); let socket = try!(TcpStream::connect(format!("{}:{}", host, port)[]));
Ok(Connection::new(BufferedWriter::new(socket.clone()), BufferedReader::new(socket))) Ok(Connection::new(BufferedWriter::new(UnsecuredTcpStream(socket.clone())),
BufferedReader::new(UnsecuredTcpStream(socket))))
}
/// Creates a thread-safe TCP connection to the specified server over SSL.
/// If the library is compiled without SSL support, this method panics.
#[experimental]
#[cfg(feature = "ssl")]
pub fn connect_ssl(host: &str, port: u16) -> IoResult<Connection<BufferedWriter<NetStream>, BufferedReader<NetStream>>> {
let socket = try!(TcpStream::connect(format!("{}:{}", host, port)[]));
let ssl = try!(ssl_to_io(SslContext::new(Tlsv1)));
let input = try!(ssl_to_io(SslStream::new(&ssl, socket.clone())));
let output = try!(ssl_to_io(SslStream::new(&ssl, socket)));
Ok(Connection::new(BufferedWriter::new(SslTcpStream(input)),
BufferedReader::new(SslTcpStream(output))))
}
/// Creates a thread-safe TCP connection to the specified server over SSL.
/// If the library is compiled without SSL support, this method panics.
#[experimental]
#[cfg(not(feature = "ssl"))]
pub fn connect_ssl(host: &str, port: u16) -> IoResult<Connection<BufferedWriter<NetStream>, BufferedReader<NetStream>>> {
panic!("Cannot connect to {}:{} over SSL without compiling with SSL support.", host, port)
}
}
/// An abstraction over different networked streams.
#[experimental]
pub enum NetStream {
/// An unsecured TcpStream.
UnsecuredTcpStream(TcpStream),
/// An SSL-secured TcpStream.
/// This is only available when compiled with SSL support.
#[cfg(feature = "ssl")]
SslTcpStream(SslStream<TcpStream>),
}
impl Reader for NetStream {
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
match self {
&UnsecuredTcpStream(ref mut stream) => stream.read(buf),
#[cfg(feature = "ssl")]
&SslTcpStream(ref mut stream) => stream.read(buf),
}
}
}
impl Writer for NetStream {
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
match self {
&UnsecuredTcpStream(ref mut stream) => stream.write(buf),
#[cfg(feature = "ssl")]
&SslTcpStream(ref mut stream) => stream.write(buf),
}
}
}
#[cfg(feature = "ssl")]
fn ssl_to_io<T>(res: Result<T, SslError>) -> IoResult<T> {
match res {
Ok(x) => Ok(x),
Err(e) => Err(IoError {
kind: OtherIoError,
desc: "An SSL error occurred.",
detail: Some(format!("{}", e)),
}),
} }
} }

View file

@ -23,6 +23,8 @@ pub struct Config {
pub server: String, pub server: String,
/// The port to connect on. /// The port to connect on.
pub port: u16, pub port: u16,
/// Whether or not to use SSL.
pub use_ssl: bool,
/// A list of channels to join on connection. /// A list of channels to join on connection.
pub channels: Vec<String>, pub channels: Vec<String>,
/// A map of additional options to be stored in config. /// A map of additional options to be stored in config.
@ -70,6 +72,7 @@ mod test {
password: String::new(), password: String::new(),
server: format!("irc.test.net"), server: format!("irc.test.net"),
port: 6667, port: 6667,
use_ssl: false,
channels: vec![format!("#test"), format!("#test2")], channels: vec![format!("#test"), format!("#test2")],
options: HashMap::new(), options: HashMap::new(),
}; };
@ -86,6 +89,7 @@ mod test {
password: String::new(), password: String::new(),
server: format!("irc.test.net"), server: format!("irc.test.net"),
port: 6667, port: 6667,
use_ssl: false,
channels: vec![format!("#test"), format!("#test2")], channels: vec![format!("#test"), format!("#test2")],
options: HashMap::new(), options: HashMap::new(),
}; };
@ -102,6 +106,7 @@ mod test {
password: String::new(), password: String::new(),
server: format!("irc.test.net"), server: format!("irc.test.net"),
port: 6667, port: 6667,
use_ssl: false,
channels: Vec::new(), channels: Vec::new(),
options: HashMap::new(), options: HashMap::new(),
}; };

View file

@ -7,6 +7,7 @@
#![feature(if_let)] #![feature(if_let)]
#![feature(slicing_syntax)] #![feature(slicing_syntax)]
extern crate serialize; extern crate serialize;
#[cfg(feature = "ssl")] extern crate openssl;
pub mod conn; pub mod conn;
pub mod data; pub mod data;

View file

@ -1,7 +1,7 @@
//! Interface for working with IRC Servers //! Interface for working with IRC Servers
#![experimental] #![experimental]
use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; use std::io::{BufferedReader, BufferedWriter, IoResult};
use conn::Connection; use conn::{Connection, NetStream};
use data::command::{Command, JOIN, PONG}; use data::command::{Command, JOIN, PONG};
use data::config::Config; use data::config::Config;
use data::kinds::{IrcReader, IrcWriter}; use data::kinds::{IrcReader, IrcWriter};
@ -29,20 +29,28 @@ pub struct IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader {
config: Config config: Config
} }
impl<'a> IrcServer<'a, BufferedWriter<TcpStream>, BufferedReader<TcpStream>> { impl<'a> IrcServer<'a, BufferedWriter<NetStream>, BufferedReader<NetStream>> {
/// Creates a new IRC Server connection from the configuration at the specified path, connecting immediately. /// Creates a new IRC Server connection from the configuration at the specified path, connecting immediately.
#[experimental] #[experimental]
pub fn new(config: &str) -> IoResult<IrcServer<'a, BufferedWriter<TcpStream>, BufferedReader<TcpStream>>> { pub fn new(config: &str) -> IoResult<IrcServer<'a, BufferedWriter<NetStream>, BufferedReader<NetStream>>> {
let config = try!(Config::load_utf8(config)); let config = try!(Config::load_utf8(config));
let conn = try!(Connection::connect(config.server[], config.port)); let conn = try!(if config.use_ssl {
Ok(IrcServer::from_connection(config, conn)) Connection::connect_ssl(config.server[], config.port)
} else {
Connection::connect(config.server[], config.port)
});
Ok(IrcServer { config: config, conn: conn })
} }
/// Creates a new IRC server connection from the specified configuration, connecting immediately. /// Creates a new IRC server connection from the specified configuration, connecting immediately.
#[experimental] #[experimental]
pub fn from_config(config: Config) -> IoResult<IrcServer<'a, BufferedWriter<TcpStream>, BufferedReader<TcpStream>>> { pub fn from_config(config: Config) -> IoResult<IrcServer<'a, BufferedWriter<NetStream>, BufferedReader<NetStream>>> {
let conn = try!(Connection::connect(config.server[], config.port)); let conn = try!(if config.use_ssl {
Ok(IrcServer::from_connection(config, conn)) Connection::connect_ssl(config.server[], config.port)
} else {
Connection::connect(config.server[], config.port)
});
Ok(IrcServer { config: config, conn: conn })
} }
} }
@ -139,6 +147,7 @@ mod test {
password: String::new(), password: String::new(),
server: format!("irc.test.net"), server: format!("irc.test.net"),
port: 6667, port: 6667,
use_ssl: false,
channels: vec![format!("#test"), format!("#test2")], channels: vec![format!("#test"), format!("#test2")],
options: HashMap::new(), options: HashMap::new(),
} }