<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! The handshake module implements the handshake part of the protocol.
//! This module also implements additional anti-DoS mitigation,
//! by including a timestamp in each handshake initialization message.
//! Refer to the module's documentation for more information.
//! A successful handshake returns a `NoiseSession` which is defined in [socket] module.
//!
//! [socket]: network::noise_wrapper::socket

use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use once_cell::sync::Lazy;
use std::{
    collections::HashMap,
    io,
    sync::{Arc, RwLock},
    time,
};

use libra_config::config::NetworkPeerInfo;
use libra_crypto::{noise, x25519};
use libra_types::PeerId;
use netcore::{
    negotiate::{negotiate_inbound, negotiate_outbound_interactive},
    transport::ConnectionOrigin,
};

use crate::noise_wrapper::session::NoiseSession;

// Timestamp
// --------
// We parse the client message and then enforce two things:
// - the client sent us a timestamp in a range [-60s, +10s]
// - the client is not re-using an older timestamp from that range
//
// if these are invalid, we avoid computing crypto to perform the answer
// if these are valid, we store the new timestamp (even if it's older than the last received)
// the last point assumes that a real client would not do this.
// this is not a measure against real validators spamming us

/// we're willing to tolerate a client timestamp 10 seconds in the future
static MAX_FUTURE_TIMESTAMP: Lazy<u64> = Lazy::new(|| time::Duration::from_secs(10));

/// we store client timestamps (to prevent replay) for up to 60 seconds
static EXPIRATION_TIMESTAMP: Lazy<u64> = Lazy::new(|| time::Duration::from_secs(60));

/// hashmap to store client timestamps if a connection succeeds
pub type AntiReplayTimestamps = HashMap<x25519::PublicKey, u64>;

// Noise Wrapper
// -------------
// Noise by default is not aware of the above or lower protocol layers,
// We thus need to build this wrapper around Noise to both:
//
// - fragment messages that need to be encrypted by noise (due to its maximum 65535-byte messages)
// - understand how long noise messages we send and receive are,
//   in order to pass them to the noise implementaiton
//

/// TODO: why do we expose this to the outer protocol?
const NOISE_PROTOCOL: &[u8] = b"/noise_ik_25519_aesgcm_sha256/1.0.0";

/// The Noise configuration to be used to perform a protocol upgrade on an underlying socket.
pub struct NoiseWrapper(noise::NoiseConfig);

impl NoiseWrapper {
    /// Create a new NoiseConfig with the provided keypair
    pub fn new(key: x25519::PrivateKey) -> Self {
        Self(noise::NoiseConfig::new(key))
    }

    /// Perform a protocol upgrade on an underlying connection. In addition perform the noise IX
    /// handshake to establish a noise session and exchange static public keys. Upon success,
    /// returns the static public key of the remote as well as a NoiseSession.
    // TODO(mimoo, philp9): this code could be inlined in transport.rs once the monolithic network is done
    pub async fn upgrade_connection<TSocket>(
        &self,
        socket: TSocket,
        origin: ConnectionOrigin,
        anti_replay_timestamps: Arc<RwLock<AntiReplayTimestamps>>,
        remote_public_key: Option<x25519::PublicKey>,
        trusted_peers: Option<&Arc<RwLock<HashMap<PeerId, NetworkPeerInfo>>>>,
    ) -> io::Result<(x25519::PublicKey, NoiseSession<TSocket>)>
    where
        TSocket: AsyncRead + AsyncWrite + Unpin,
    {
        // Perform protocol negotiation
        // TODO: should we really be doing this here?
        let (socket, proto) = match origin {
            ConnectionOrigin::Outbound => {
                negotiate_outbound_interactive(socket, [NOISE_PROTOCOL]).await?
            }
            ConnectionOrigin::Inbound => negotiate_inbound(socket, [NOISE_PROTOCOL]).await?,
        };

        // check that the correct protocol was negotiated
        if proto != NOISE_PROTOCOL {
            if cfg!(test) {
                panic!("noise: incorrect protocol negotiated");
            } else {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::Other,
                    "noise: incorrect protocol negotiated",
                ));
            }
        }

        // perform the noise handshake
        let socket = match origin {
            ConnectionOrigin::Outbound => {
                let remote_public_key = match remote_public_key {
                    Some(key) => key,
                    None if cfg!(any(test, feature = "fuzzing")) => unreachable!(),
                    None => {
                        return Err(std::io::Error::new(
                            std::io::ErrorKind::Other,
                            "noise: SHOULD NOT HAPPEN: missing server's key when dialing",
                        ));
                    }
                };
                self.dial(socket, remote_public_key).await?
            }
            ConnectionOrigin::Inbound => {
                self.accept(socket, anti_replay_timestamps, trusted_peers)
                    .await?
            }
        };

        // return remote public key with a socket including the noise session
        let remote_public_key = socket.get_remote_static();
        Ok((remote_public_key, socket))
    }

    pub async fn dial<TSocket>(
        &self,
        mut socket: TSocket,
        remote_public_key: x25519::PublicKey,
    ) -> io::Result<NoiseSession<TSocket>>
    where
        TSocket: AsyncRead + AsyncWrite + Unpin,
    {
        // create prologue as current timestamp in seconds, and send it
        let now: u64 = time::SystemTime::now()
            .duration_since(time::UNIX_EPOCH)
            .expect("system clock should work")
            .as_secs();
        let prologue = now.to_le_bytes(); // 5 -> [0, 0, 0, 0, 0, 0, 0, 5]
        socket.write_all(&prologue).await?;

        // create first handshake message  (-> e, es, s, ss)
        let mut rng = rand::rngs::OsRng;
        let mut first_message = [0u8; noise::handshake_init_msg_len(0)];
        let initiator_state = self
            .0
            .initiate_connection(
                &mut rng,
                &prologue,
                remote_public_key,
                None,
                &mut first_message,
            )
            .map_err(|e| {
                io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("noise: wrong input passed {}", e),
                )
            })?;

        // write the first handshake message
        socket.write_all(&first_message).await?;

        // flush
        socket.flush().await?;

        // receive the server's response (<- e, ee, se)
        let mut server_response = [0u8; noise::handshake_resp_msg_len(0)];
        socket.read_exact(&mut server_response).await?;

        // parse the server's response
        // TODO: security logging here? (mimoo)
        let (_, session) = self
            .0
            .finalize_connection(initiator_state, &server_response)
            .map_err(|e| {
                io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!("noise: wrong message received {}", e),
                )
            })?;

        // finalize the connection
        Ok(NoiseSession::new(socket, session))
    }

    pub async fn accept<TSocket>(
        &self,
        mut socket: TSocket,
        anti_replay_timestamps: Arc<RwLock<AntiReplayTimestamps>>,
        trusted_peers: Option<&Arc<RwLock<HashMap<PeerId, NetworkPeerInfo>>>>,
    ) -> io::Result<NoiseSession<TSocket>>
    where
        TSocket: AsyncRead + AsyncWrite + Unpin,
    {
        // receives prologue as the client timestamp in seconds
        let mut prologue = [0u8; 8];
        socket.read_exact(&mut prologue).await?;
        let client_timestamp_u64 = u64::from_le_bytes(prologue);
        let client_timestamp = time::Duration::from_secs(client_timestamp_u64);

        // check the client timestamp
        // TODO: security logging (mimoo)
        let now = time::SystemTime::now()
            .duration_since(time::UNIX_EPOCH)
            .expect("system clock should work");

        match client_timestamp {
            t if t > now && t - now > MAX_FUTURE_TIMESTAMP => {
                // if the client timestamp is too far in the future, abort
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!(
                        "noise: client initiated connection with a timestamp too far in the future: {}",
                        client_timestamp_u64
                    ),
                ));
            }
            t if t < now && now - t > EXPIRATION_TIMESTAMP => {
                // if the client timestamp is expired, abort
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!(
                        "noise: client initiated connection with an expired timestamp: {}",
                        client_timestamp_u64
                    ),
                ));
            }
            _ => (),
        };

        // receive the initiation message
        let mut client_init_message = [0u8; noise::handshake_init_msg_len(0)];
        socket.read_exact(&mut client_init_message).await?;

        // parse it
        let (their_public_key, handshake_state, _) = self
            .0
            .parse_client_init_message(&prologue, &client_init_message)
            .map_err(|e| {
                io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!("noise: wrong message received {}", e),
                )
            })?;

        // make sure the public key is a validator before continuing (if we're in the validator network)
        if let Some(trusted_peers) = trusted_peers {
            let found = trusted_peers
                .read()
                .unwrap()
                .iter()
                .any(|(_peer_id, public_keys)| public_keys.identity_public_key == their_public_key);
            if !found {
                // TODO: security logging (mimoo)
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!(
                        "noise: client connecting to us with an unknown public key: {}",
                        their_public_key
                    ),
                ));
            }
        }

        // check the timestamp is not a replay
        {
            let timestamps = anti_replay_timestamps.read().unwrap();
            if let Some(timestamp) = timestamps.get(&their_public_key) {
                // TODO: security logging the ip + blocking the ip? (mimoo)
                if timestamp == &client_timestamp_u64 {
                    return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!(
                        "noise: client initiated connection with a timestamp already seen before: {}",
                        client_timestamp_u64
                    ),
                ));
                }
            }
        }

        // construct and send the response
        let mut rng = rand::rngs::OsRng;
        let mut server_response = [0u8; noise::handshake_resp_msg_len(0)];
        let session = self
            .0
            .respond_to_client(&mut rng, handshake_state, None, &mut server_response)
            .map_err(|e| {
                io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!("noise: wrong message received {}", e),
                )
            })?;
        socket.write_all(&server_response).await?;

        // the connection succeeded, store the client timestamp for replay prevention
        {
            let mut timestamps = anti_replay_timestamps.write().unwrap();
            timestamps
                .entry(their_public_key)
                .and_modify(|e| *e = client_timestamp_u64)
                .or_insert(client_timestamp_u64);
        }

        // finalize the connection
        Ok(NoiseSession::new(socket, session))
    }
}

//
// Tests
// -----
//

#[cfg(test)]
mod test {
    use super::*;

    use futures::{executor::block_on, future::join};
    use libra_crypto::test_utils::TEST_SEED;
    use memsocket::MemorySocket;
    use std::{
        collections::HashMap,
        io,
        sync::{Arc, RwLock},
    };

    use libra_crypto::traits::Uniform as _;
    use rand::SeedableRng as _;

    /// helper to setup two testing peers
    fn build_peers() -> (
        (NoiseWrapper, x25519::PublicKey),
        (NoiseWrapper, x25519::PublicKey),
    ) {
        let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED);

        let client_private = x25519::PrivateKey::generate(&mut rng);
        let client_public = client_private.public_key();

        let server_private = x25519::PrivateKey::generate(&mut rng);
        let server_public = server_private.public_key();

        let client = NoiseWrapper::new(client_private);
        let server = NoiseWrapper::new(server_private);

        ((client, client_public), (server, server_public))
    }

    /// helper to perform a noise handshake with two peers
    fn perform_handshake(
        client: NoiseWrapper,
        server_public_key: x25519::PublicKey,
        server: NoiseWrapper,
        trusted_peers: Option<&Arc<RwLock<HashMap<PeerId, NetworkPeerInfo>>>>,
    ) -> io::Result<(NoiseSession<MemorySocket>, NoiseSession<MemorySocket>)> {
        // create an in-memory socket for testing
        let (dialer_socket, listener_socket) = MemorySocket::new_pair();
        let anti_replay_timestamps = Arc::new(RwLock::new(AntiReplayTimestamps::new()));

        // perform the handshake
        let (client_session, server_session) = block_on(join(
            client.dial(dialer_socket, server_public_key),
            server.accept(listener_socket, anti_replay_timestamps, trusted_peers),
        ));

        //
        Ok((client_session?, server_session?))
    }

    #[test]
    fn test_handshake() {
        // perform handshake with two testing peers
        let ((client, client_public), (server, server_public)) = build_peers();
        let (client, server) = perform_handshake(client, server_public, server, None).unwrap();

        assert_eq!(client.get_remote_static(), server_public,);
        assert_eq!(server.get_remote_static(), client_public,);
    }
}
<commit_msg>[network] removing noise pre-negotiation protocol<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! The handshake module implements the handshake part of the protocol.
//! This module also implements additional anti-DoS mitigation,
//! by including a timestamp in each handshake initialization message.
//! Refer to the module's documentation for more information.
//! A successful handshake returns a `NoiseSession` which is defined in [socket] module.
//!
//! [socket]: network::noise_wrapper::socket

use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use once_cell::sync::Lazy;
use std::{
    collections::HashMap,
    io,
    sync::{Arc, RwLock},
    time,
};

use libra_config::config::NetworkPeerInfo;
use libra_crypto::{noise, x25519};
use libra_types::PeerId;
use netcore::transport::ConnectionOrigin;

use crate::noise_wrapper::session::NoiseSession;

// Timestamp
// --------
// We parse the client message and then enforce two things:
// - the client sent us a timestamp in a range [-60s, +10s]
// - the client is not re-using an older timestamp from that range
//
// if these are invalid, we avoid computing crypto to perform the answer
// if these are valid, we store the new timestamp (even if it's older than the last received)
// the last point assumes that a real client would not do this.
// this is not a measure against real validators spamming us

/// we're willing to tolerate a client timestamp 10 seconds in the future
static MAX_FUTURE_TIMESTAMP: Lazy<u64> = Lazy::new(|| time::Duration::from_secs(10));

/// we store client timestamps (to prevent replay) for up to 60 seconds
static EXPIRATION_TIMESTAMP: Lazy<u64> = Lazy::new(|| time::Duration::from_secs(60));

/// hashmap to store client timestamps if a connection succeeds
pub type AntiReplayTimestamps = HashMap<x25519::PublicKey, u64>;

// Noise Wrapper
// -------------
// Noise by default is not aware of the above or lower protocol layers,
// We thus need to build this wrapper around Noise to both:
//
// - fragment messages that need to be encrypted by noise (due to its maximum 65535-byte messages)
// - understand how long noise messages we send and receive are,
//   in order to pass them to the noise implementaiton
//

/// The Noise configuration to be used to perform a protocol upgrade on an underlying socket.
pub struct NoiseWrapper(noise::NoiseConfig);

impl NoiseWrapper {
    /// Create a new NoiseConfig with the provided keypair
    pub fn new(key: x25519::PrivateKey) -> Self {
        Self(noise::NoiseConfig::new(key))
    }

    /// Perform a protocol upgrade on an underlying connection. In addition perform the noise IX
    /// handshake to establish a noise session and exchange static public keys. Upon success,
    /// returns the static public key of the remote as well as a NoiseSession.
    // TODO(mimoo, philp9): this code could be inlined in transport.rs once the monolithic network is done
    pub async fn upgrade_connection<TSocket>(
        &self,
        socket: TSocket,
        origin: ConnectionOrigin,
        anti_replay_timestamps: Arc<RwLock<AntiReplayTimestamps>>,
        remote_public_key: Option<x25519::PublicKey>,
        trusted_peers: Option<&Arc<RwLock<HashMap<PeerId, NetworkPeerInfo>>>>,
    ) -> io::Result<(x25519::PublicKey, NoiseSession<TSocket>)>
    where
        TSocket: AsyncRead + AsyncWrite + Unpin,
    {
        // perform the noise handshake
        let socket = match origin {
            ConnectionOrigin::Outbound => {
                let remote_public_key = match remote_public_key {
                    Some(key) => key,
                    None if cfg!(any(test, feature = "fuzzing")) => unreachable!(),
                    None => {
                        return Err(std::io::Error::new(
                            std::io::ErrorKind::Other,
                            "noise: SHOULD NOT HAPPEN: missing server's key when dialing",
                        ));
                    }
                };
                self.dial(socket, remote_public_key).await?
            }
            ConnectionOrigin::Inbound => {
                self.accept(socket, anti_replay_timestamps, trusted_peers)
                    .await?
            }
        };

        // return remote public key with a socket including the noise session
        let remote_public_key = socket.get_remote_static();
        Ok((remote_public_key, socket))
    }

    pub async fn dial<TSocket>(
        &self,
        mut socket: TSocket,
        remote_public_key: x25519::PublicKey,
    ) -> io::Result<NoiseSession<TSocket>>
    where
        TSocket: AsyncRead + AsyncWrite + Unpin,
    {
        // create prologue as current timestamp in seconds, and send it
        let now: u64 = time::SystemTime::now()
            .duration_since(time::UNIX_EPOCH)
            .expect("system clock should work")
            .as_secs();
        let prologue = now.to_le_bytes(); // 5 -> [0, 0, 0, 0, 0, 0, 0, 5]
        socket.write_all(&prologue).await?;

        // create first handshake message  (-> e, es, s, ss)
        let mut rng = rand::rngs::OsRng;
        let mut first_message = [0u8; noise::handshake_init_msg_len(0)];
        let initiator_state = self
            .0
            .initiate_connection(
                &mut rng,
                &prologue,
                remote_public_key,
                None,
                &mut first_message,
            )
            .map_err(|e| {
                io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("noise: wrong input passed {}", e),
                )
            })?;

        // write the first handshake message
        socket.write_all(&first_message).await?;

        // flush
        socket.flush().await?;

        // receive the server's response (<- e, ee, se)
        let mut server_response = [0u8; noise::handshake_resp_msg_len(0)];
        socket.read_exact(&mut server_response).await?;

        // parse the server's response
        // TODO: security logging here? (mimoo)
        let (_, session) = self
            .0
            .finalize_connection(initiator_state, &server_response)
            .map_err(|e| {
                io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!("noise: wrong message received {}", e),
                )
            })?;

        // finalize the connection
        Ok(NoiseSession::new(socket, session))
    }

    pub async fn accept<TSocket>(
        &self,
        mut socket: TSocket,
        anti_replay_timestamps: Arc<RwLock<AntiReplayTimestamps>>,
        trusted_peers: Option<&Arc<RwLock<HashMap<PeerId, NetworkPeerInfo>>>>,
    ) -> io::Result<NoiseSession<TSocket>>
    where
        TSocket: AsyncRead + AsyncWrite + Unpin,
    {
        // receives prologue as the client timestamp in seconds
        let mut prologue = [0u8; 8];
        socket.read_exact(&mut prologue).await?;
        let client_timestamp_u64 = u64::from_le_bytes(prologue);
        let client_timestamp = time::Duration::from_secs(client_timestamp_u64);

        // check the client timestamp
        // TODO: security logging (mimoo)
        let now = time::SystemTime::now()
            .duration_since(time::UNIX_EPOCH)
            .expect("system clock should work");

        match client_timestamp {
            t if t > now && t - now > MAX_FUTURE_TIMESTAMP => {
                // if the client timestamp is too far in the future, abort
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!(
                        "noise: client initiated connection with a timestamp too far in the future: {}",
                        client_timestamp_u64
                    ),
                ));
            }
            t if t < now && now - t > EXPIRATION_TIMESTAMP => {
                // if the client timestamp is expired, abort
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!(
                        "noise: client initiated connection with an expired timestamp: {}",
                        client_timestamp_u64
                    ),
                ));
            }
            _ => (),
        };

        // receive the initiation message
        let mut client_init_message = [0u8; noise::handshake_init_msg_len(0)];
        socket.read_exact(&mut client_init_message).await?;

        // parse it
        let (their_public_key, handshake_state, _) = self
            .0
            .parse_client_init_message(&prologue, &client_init_message)
            .map_err(|e| {
                io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!("noise: wrong message received {}", e),
                )
            })?;

        // make sure the public key is a validator before continuing (if we're in the validator network)
        if let Some(trusted_peers) = trusted_peers {
            let found = trusted_peers
                .read()
                .unwrap()
                .iter()
                .any(|(_peer_id, public_keys)| public_keys.identity_public_key == their_public_key);
            if !found {
                // TODO: security logging (mimoo)
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!(
                        "noise: client connecting to us with an unknown public key: {}",
                        their_public_key
                    ),
                ));
            }
        }

        // check the timestamp is not a replay
        {
            let timestamps = anti_replay_timestamps.read().unwrap();
            if let Some(timestamp) = timestamps.get(&their_public_key) {
                // TODO: security logging the ip + blocking the ip? (mimoo)
                if timestamp == &client_timestamp_u64 {
                    return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!(
                        "noise: client initiated connection with a timestamp already seen before: {}",
                        client_timestamp_u64
                    ),
                ));
                }
            }
        }

        // construct and send the response
        let mut rng = rand::rngs::OsRng;
        let mut server_response = [0u8; noise::handshake_resp_msg_len(0)];
        let session = self
            .0
            .respond_to_client(&mut rng, handshake_state, None, &mut server_response)
            .map_err(|e| {
                io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!("noise: wrong message received {}", e),
                )
            })?;
        socket.write_all(&server_response).await?;

        // the connection succeeded, store the client timestamp for replay prevention
        {
            let mut timestamps = anti_replay_timestamps.write().unwrap();
            timestamps
                .entry(their_public_key)
                .and_modify(|e| *e = client_timestamp_u64)
                .or_insert(client_timestamp_u64);
        }

        // finalize the connection
        Ok(NoiseSession::new(socket, session))
    }
}

//
// Tests
// -----
//

#[cfg(test)]
mod test {
    use super::*;

    use futures::{executor::block_on, future::join};
    use libra_crypto::test_utils::TEST_SEED;
    use memsocket::MemorySocket;
    use std::{
        collections::HashMap,
        io,
        sync::{Arc, RwLock},
    };

    use libra_crypto::traits::Uniform as _;
    use rand::SeedableRng as _;

    /// helper to setup two testing peers
    fn build_peers() -> (
        (NoiseWrapper, x25519::PublicKey),
        (NoiseWrapper, x25519::PublicKey),
    ) {
        let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED);

        let client_private = x25519::PrivateKey::generate(&mut rng);
        let client_public = client_private.public_key();

        let server_private = x25519::PrivateKey::generate(&mut rng);
        let server_public = server_private.public_key();

        let client = NoiseWrapper::new(client_private);
        let server = NoiseWrapper::new(server_private);

        ((client, client_public), (server, server_public))
    }

    /// helper to perform a noise handshake with two peers
    fn perform_handshake(
        client: NoiseWrapper,
        server_public_key: x25519::PublicKey,
        server: NoiseWrapper,
        trusted_peers: Option<&Arc<RwLock<HashMap<PeerId, NetworkPeerInfo>>>>,
    ) -> io::Result<(NoiseSession<MemorySocket>, NoiseSession<MemorySocket>)> {
        // create an in-memory socket for testing
        let (dialer_socket, listener_socket) = MemorySocket::new_pair();
        let anti_replay_timestamps = Arc::new(RwLock::new(AntiReplayTimestamps::new()));

        // perform the handshake
        let (client_session, server_session) = block_on(join(
            client.dial(dialer_socket, server_public_key),
            server.accept(listener_socket, anti_replay_timestamps, trusted_peers),
        ));

        //
        Ok((client_session?, server_session?))
    }

    #[test]
    fn test_handshake() {
        // perform handshake with two testing peers
        let ((client, client_public), (server, server_public)) = build_peers();
        let (client, server) = perform_handshake(client, server_public, server, None).unwrap();

        assert_eq!(client.get_remote_static(), server_public,);
        assert_eq!(server.get_remote_static(), client_public,);
    }
}
<|endoftext|>


<commit_before>mod linux;
mod macos;

use crate::*;
use rustc::mir;
use rustc::ty::layout::{Align, LayoutOf, Size};

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn emulate_foreign_item_by_name(
        &mut self,
        link_name: &str,
        args: &[OpTy<'tcx, Tag>],
        dest: PlaceTy<'tcx, Tag>,
        ret: mir::BasicBlock,
    ) -> InterpResult<'tcx, bool> {
        let this = self.eval_context_mut();
        let tcx = &{ this.tcx.tcx };

        match link_name {
            // Environment related shims
            "getenv" => {
                let result = this.getenv(args[0])?;
                this.write_scalar(result, dest)?;
            }

            "unsetenv" => {
                let result = this.unsetenv(args[0])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "setenv" => {
                let result = this.setenv(args[0], args[1])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "getcwd" => {
                let result = this.getcwd(args[0], args[1])?;
                this.write_scalar(result, dest)?;
            }

            "chdir" => {
                let result = this.chdir(args[0])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            // File related shims
            "open" | "open64" => {
                let result = this.open(args[0], args[1])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "fcntl" => {
                let result = this.fcntl(args[0], args[1], args.get(2).cloned())?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "read" => {
                let result = this.read(args[0], args[1], args[2])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "write" => {
                let fd = this.read_scalar(args[0])?.to_i32()?;
                let buf = this.read_scalar(args[1])?.not_undef()?;
                let n = this.read_scalar(args[2])?.to_machine_usize(tcx)?;
                trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
                let result = if fd == 1 || fd == 2 {
                    // stdout/stderr
                    use std::io::{self, Write};

                    let buf_cont = this.memory.read_bytes(buf, Size::from_bytes(n))?;
                    // We need to flush to make sure this actually appears on the screen
                    let res = if fd == 1 {
                        // Stdout is buffered, flush to make sure it appears on the screen.
                        // This is the write() syscall of the interpreted program, we want it
                        // to correspond to a write() syscall on the host -- there is no good
                        // in adding extra buffering here.
                        let res = io::stdout().write(buf_cont);
                        io::stdout().flush().unwrap();
                        res
                    } else {
                        // No need to flush, stderr is not buffered.
                        io::stderr().write(buf_cont)
                    };
                    match res {
                        Ok(n) => n as i64,
                        Err(_) => -1,
                    }
                } else {
                    this.write(args[0], args[1], args[2])?
                };
                // Now, `result` is the value we return back to the program.
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "unlink" => {
                let result = this.unlink(args[0])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "symlink" => {
                let result = this.symlink(args[0], args[1])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "rename" => {
                let result = this.rename(args[0], args[1])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "mkdir" => {
                let result = this.mkdir(args[0], args[1])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "rmdir" => {
                let result = this.rmdir(args[0])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "closedir" => {
                let result = this.closedir(args[0])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "lseek" | "lseek64" => {
                let result = this.lseek64(args[0], args[1], args[2])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            // Other shims
            "posix_memalign" => {
                let ret = this.deref_operand(args[0])?;
                let align = this.read_scalar(args[1])?.to_machine_usize(this)?;
                let size = this.read_scalar(args[2])?.to_machine_usize(this)?;
                // Align must be power of 2, and also at least ptr-sized (POSIX rules).
                if !align.is_power_of_two() {
                    throw_unsup!(HeapAllocNonPowerOfTwoAlignment(align));
                }
                if align < this.pointer_size().bytes() {
                    throw_ub_format!(
                        "posix_memalign: alignment must be at least the size of a pointer, but is {}",
                        align,
                    );
                }

                if size == 0 {
                    this.write_null(ret.into())?;
                } else {
                    let ptr = this.memory.allocate(
                        Size::from_bytes(size),
                        Align::from_bytes(align).unwrap(),
                        MiriMemoryKind::C.into(),
                    );
                    this.write_scalar(ptr, ret.into())?;
                }
                this.write_null(dest)?;
            }

            "dlsym" => {
                let _handle = this.read_scalar(args[0])?;
                let symbol = this.read_scalar(args[1])?.not_undef()?;
                let symbol_name = this.memory.read_c_str(symbol)?;
                let err = format!("bad c unicode symbol: {:?}", symbol_name);
                let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err);
                if let Some(dlsym) = Dlsym::from_str(symbol_name)? {
                    let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym));
                    this.write_scalar(Scalar::from(ptr), dest)?;
                } else {
                    this.write_null(dest)?;
                }
            }

            // Hook pthread calls that go to the thread-local storage memory subsystem.
            "pthread_key_create" => {
                let key_place = this.deref_operand(args[0])?;

                // Extract the function type out of the signature (that seems easier than constructing it ourselves).
                let dtor = match this.test_null(this.read_scalar(args[1])?.not_undef()?)? {
                    Some(dtor_ptr) => Some(this.memory.get_fn(dtor_ptr)?.as_instance()?),
                    None => None,
                };

                // Figure out how large a pthread TLS key actually is.
                // This is `libc::pthread_key_t`.
                let key_type = args[0].layout.ty
                    .builtin_deref(true)
                    .ok_or_else(|| err_ub_format!(
                        "wrong signature used for `pthread_key_create`: first argument must be a raw pointer."
                    ))?
                    .ty;
                let key_layout = this.layout_of(key_type)?;

                // Create key and write it into the memory where `key_ptr` wants it.
                let key = this.machine.tls.create_tls_key(dtor) as u128;
                if key_layout.size.bits() < 128 && key >= (1u128 << key_layout.size.bits() as u128)
                {
                    throw_unsup!(OutOfTls);
                }

                this.write_scalar(Scalar::from_uint(key, key_layout.size), key_place.into())?;

                // Return success (`0`).
                this.write_null(dest)?;
            }
            "pthread_key_delete" => {
                let key = this.read_scalar(args[0])?.to_bits(args[0].layout.size)?;
                this.machine.tls.delete_tls_key(key)?;
                // Return success (0)
                this.write_null(dest)?;
            }
            "pthread_getspecific" => {
                let key = this.read_scalar(args[0])?.to_bits(args[0].layout.size)?;
                let ptr = this.machine.tls.load_tls(key, tcx)?;
                this.write_scalar(ptr, dest)?;
            }
            "pthread_setspecific" => {
                let key = this.read_scalar(args[0])?.to_bits(args[0].layout.size)?;
                let new_ptr = this.read_scalar(args[1])?.not_undef()?;
                this.machine.tls.store_tls(key, this.test_null(new_ptr)?)?;

                // Return success (`0`).
                this.write_null(dest)?;
            }

            // Stack size/address stuff.
            | "pthread_attr_init"
            | "pthread_attr_destroy"
            | "pthread_self"
            | "pthread_attr_setstacksize" => {
                this.write_null(dest)?;
            }
            "pthread_attr_getstack" => {
                let addr_place = this.deref_operand(args[1])?;
                let size_place = this.deref_operand(args[2])?;

                this.write_scalar(
                    Scalar::from_uint(STACK_ADDR, addr_place.layout.size),
                    addr_place.into(),
                )?;
                this.write_scalar(
                    Scalar::from_uint(STACK_SIZE, size_place.layout.size),
                    size_place.into(),
                )?;

                // Return success (`0`).
                this.write_null(dest)?;
            }

            // We don't support threading.
            "pthread_create" => {
                throw_unsup_format!("Miri does not support threading");
            }

            // Stub out calls for condvar, mutex and rwlock, to just return `0`.
            | "pthread_mutexattr_init"
            | "pthread_mutexattr_settype"
            | "pthread_mutex_init"
            | "pthread_mutexattr_destroy"
            | "pthread_mutex_lock"
            | "pthread_mutex_unlock"
            | "pthread_mutex_destroy"
            | "pthread_rwlock_rdlock"
            | "pthread_rwlock_unlock"
            | "pthread_rwlock_wrlock"
            | "pthread_rwlock_destroy"
            | "pthread_condattr_init"
            | "pthread_condattr_setclock"
            | "pthread_cond_init"
            | "pthread_condattr_destroy"
            | "pthread_cond_destroy"
            => {
                this.write_null(dest)?;
            }

            // We don't support fork so we don't have to do anything for atfork.
            "pthread_atfork" => {
                this.write_null(dest)?;
            }

            // Some things needed for `sys::thread` initialization to go through.
            | "signal"
            | "sigaction"
            | "sigaltstack"
            => {
                this.write_scalar(Scalar::from_int(0, dest.layout.size), dest)?;
            }

            "sysconf" => {
                let name = this.read_scalar(args[0])?.to_i32()?;

                trace!("sysconf() called with name {}", name);
                // TODO: Cache the sysconf integers via Miri's global cache.
                let paths = &[
                    (&["libc", "_SC_PAGESIZE"], Scalar::from_int(PAGE_SIZE, dest.layout.size)),
                    (&["libc", "_SC_GETPW_R_SIZE_MAX"], Scalar::from_int(-1, dest.layout.size)),
                    (
                        &["libc", "_SC_NPROCESSORS_ONLN"],
                        Scalar::from_int(NUM_CPUS, dest.layout.size),
                    ),
                ];
                let mut result = None;
                for &(path, path_value) in paths {
                    if let Some(val) = this.eval_path_scalar(path)? {
                        let val = val.to_i32()?;
                        if val == name {
                            result = Some(path_value);
                            break;
                        }
                    }
                }
                if let Some(result) = result {
                    this.write_scalar(result, dest)?;
                } else {
                    throw_unsup_format!("Unimplemented sysconf name: {}", name)
                }
            }

            "isatty" => {
                this.write_null(dest)?;
            }

            "posix_fadvise" => {
                // fadvise is only informational, we can ignore it.
                this.write_null(dest)?;
            }

            "mmap" => {
                // This is a horrible hack, but since the guard page mechanism calls mmap and expects a particular return value, we just give it that value.
                let addr = this.read_scalar(args[0])?.not_undef()?;
                this.write_scalar(addr, dest)?;
            }

            "mprotect" => {
                this.write_null(dest)?;
            }

            _ => {
                match this.tcx.sess.target.target.target_os.as_str() {
                    "linux" => return linux::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret),
                    "macos" => return macos::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret),
                    _ => unreachable!(),
                }
            }
        };

        Ok(true)
    }
}
<commit_msg>Auto merge of #1194 - RalfJung:no-to-bits, r=RalfJung<commit_after>mod linux;
mod macos;

use crate::*;
use rustc::mir;
use rustc::ty::layout::{Align, LayoutOf, Size};

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn emulate_foreign_item_by_name(
        &mut self,
        link_name: &str,
        args: &[OpTy<'tcx, Tag>],
        dest: PlaceTy<'tcx, Tag>,
        ret: mir::BasicBlock,
    ) -> InterpResult<'tcx, bool> {
        let this = self.eval_context_mut();
        let tcx = &{ this.tcx.tcx };

        match link_name {
            // Environment related shims
            "getenv" => {
                let result = this.getenv(args[0])?;
                this.write_scalar(result, dest)?;
            }

            "unsetenv" => {
                let result = this.unsetenv(args[0])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "setenv" => {
                let result = this.setenv(args[0], args[1])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "getcwd" => {
                let result = this.getcwd(args[0], args[1])?;
                this.write_scalar(result, dest)?;
            }

            "chdir" => {
                let result = this.chdir(args[0])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            // File related shims
            "open" | "open64" => {
                let result = this.open(args[0], args[1])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "fcntl" => {
                let result = this.fcntl(args[0], args[1], args.get(2).cloned())?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "read" => {
                let result = this.read(args[0], args[1], args[2])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "write" => {
                let fd = this.read_scalar(args[0])?.to_i32()?;
                let buf = this.read_scalar(args[1])?.not_undef()?;
                let n = this.read_scalar(args[2])?.to_machine_usize(tcx)?;
                trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
                let result = if fd == 1 || fd == 2 {
                    // stdout/stderr
                    use std::io::{self, Write};

                    let buf_cont = this.memory.read_bytes(buf, Size::from_bytes(n))?;
                    // We need to flush to make sure this actually appears on the screen
                    let res = if fd == 1 {
                        // Stdout is buffered, flush to make sure it appears on the screen.
                        // This is the write() syscall of the interpreted program, we want it
                        // to correspond to a write() syscall on the host -- there is no good
                        // in adding extra buffering here.
                        let res = io::stdout().write(buf_cont);
                        io::stdout().flush().unwrap();
                        res
                    } else {
                        // No need to flush, stderr is not buffered.
                        io::stderr().write(buf_cont)
                    };
                    match res {
                        Ok(n) => n as i64,
                        Err(_) => -1,
                    }
                } else {
                    this.write(args[0], args[1], args[2])?
                };
                // Now, `result` is the value we return back to the program.
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "unlink" => {
                let result = this.unlink(args[0])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "symlink" => {
                let result = this.symlink(args[0], args[1])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "rename" => {
                let result = this.rename(args[0], args[1])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "mkdir" => {
                let result = this.mkdir(args[0], args[1])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "rmdir" => {
                let result = this.rmdir(args[0])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "closedir" => {
                let result = this.closedir(args[0])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            "lseek" | "lseek64" => {
                let result = this.lseek64(args[0], args[1], args[2])?;
                this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
            }

            // Other shims
            "posix_memalign" => {
                let ret = this.deref_operand(args[0])?;
                let align = this.read_scalar(args[1])?.to_machine_usize(this)?;
                let size = this.read_scalar(args[2])?.to_machine_usize(this)?;
                // Align must be power of 2, and also at least ptr-sized (POSIX rules).
                if !align.is_power_of_two() {
                    throw_unsup!(HeapAllocNonPowerOfTwoAlignment(align));
                }
                if align < this.pointer_size().bytes() {
                    throw_ub_format!(
                        "posix_memalign: alignment must be at least the size of a pointer, but is {}",
                        align,
                    );
                }

                if size == 0 {
                    this.write_null(ret.into())?;
                } else {
                    let ptr = this.memory.allocate(
                        Size::from_bytes(size),
                        Align::from_bytes(align).unwrap(),
                        MiriMemoryKind::C.into(),
                    );
                    this.write_scalar(ptr, ret.into())?;
                }
                this.write_null(dest)?;
            }

            "dlsym" => {
                let _handle = this.read_scalar(args[0])?;
                let symbol = this.read_scalar(args[1])?.not_undef()?;
                let symbol_name = this.memory.read_c_str(symbol)?;
                let err = format!("bad c unicode symbol: {:?}", symbol_name);
                let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err);
                if let Some(dlsym) = Dlsym::from_str(symbol_name)? {
                    let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym));
                    this.write_scalar(Scalar::from(ptr), dest)?;
                } else {
                    this.write_null(dest)?;
                }
            }

            // Hook pthread calls that go to the thread-local storage memory subsystem.
            "pthread_key_create" => {
                let key_place = this.deref_operand(args[0])?;

                // Extract the function type out of the signature (that seems easier than constructing it ourselves).
                let dtor = match this.test_null(this.read_scalar(args[1])?.not_undef()?)? {
                    Some(dtor_ptr) => Some(this.memory.get_fn(dtor_ptr)?.as_instance()?),
                    None => None,
                };

                // Figure out how large a pthread TLS key actually is.
                // This is `libc::pthread_key_t`.
                let key_type = args[0].layout.ty
                    .builtin_deref(true)
                    .ok_or_else(|| err_ub_format!(
                        "wrong signature used for `pthread_key_create`: first argument must be a raw pointer."
                    ))?
                    .ty;
                let key_layout = this.layout_of(key_type)?;

                // Create key and write it into the memory where `key_ptr` wants it.
                let key = this.machine.tls.create_tls_key(dtor) as u128;
                if key_layout.size.bits() < 128 && key >= (1u128 << key_layout.size.bits() as u128)
                {
                    throw_unsup!(OutOfTls);
                }

                this.write_scalar(Scalar::from_uint(key, key_layout.size), key_place.into())?;

                // Return success (`0`).
                this.write_null(dest)?;
            }
            "pthread_key_delete" => {
                let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?;
                this.machine.tls.delete_tls_key(key)?;
                // Return success (0)
                this.write_null(dest)?;
            }
            "pthread_getspecific" => {
                let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?;
                let ptr = this.machine.tls.load_tls(key, tcx)?;
                this.write_scalar(ptr, dest)?;
            }
            "pthread_setspecific" => {
                let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?;
                let new_ptr = this.read_scalar(args[1])?.not_undef()?;
                this.machine.tls.store_tls(key, this.test_null(new_ptr)?)?;

                // Return success (`0`).
                this.write_null(dest)?;
            }

            // Stack size/address stuff.
            | "pthread_attr_init"
            | "pthread_attr_destroy"
            | "pthread_self"
            | "pthread_attr_setstacksize" => {
                this.write_null(dest)?;
            }
            "pthread_attr_getstack" => {
                let addr_place = this.deref_operand(args[1])?;
                let size_place = this.deref_operand(args[2])?;

                this.write_scalar(
                    Scalar::from_uint(STACK_ADDR, addr_place.layout.size),
                    addr_place.into(),
                )?;
                this.write_scalar(
                    Scalar::from_uint(STACK_SIZE, size_place.layout.size),
                    size_place.into(),
                )?;

                // Return success (`0`).
                this.write_null(dest)?;
            }

            // We don't support threading.
            "pthread_create" => {
                throw_unsup_format!("Miri does not support threading");
            }

            // Stub out calls for condvar, mutex and rwlock, to just return `0`.
            | "pthread_mutexattr_init"
            | "pthread_mutexattr_settype"
            | "pthread_mutex_init"
            | "pthread_mutexattr_destroy"
            | "pthread_mutex_lock"
            | "pthread_mutex_unlock"
            | "pthread_mutex_destroy"
            | "pthread_rwlock_rdlock"
            | "pthread_rwlock_unlock"
            | "pthread_rwlock_wrlock"
            | "pthread_rwlock_destroy"
            | "pthread_condattr_init"
            | "pthread_condattr_setclock"
            | "pthread_cond_init"
            | "pthread_condattr_destroy"
            | "pthread_cond_destroy"
            => {
                this.write_null(dest)?;
            }

            // We don't support fork so we don't have to do anything for atfork.
            "pthread_atfork" => {
                this.write_null(dest)?;
            }

            // Some things needed for `sys::thread` initialization to go through.
            | "signal"
            | "sigaction"
            | "sigaltstack"
            => {
                this.write_scalar(Scalar::from_int(0, dest.layout.size), dest)?;
            }

            "sysconf" => {
                let name = this.read_scalar(args[0])?.to_i32()?;

                trace!("sysconf() called with name {}", name);
                // TODO: Cache the sysconf integers via Miri's global cache.
                let paths = &[
                    (&["libc", "_SC_PAGESIZE"], Scalar::from_int(PAGE_SIZE, dest.layout.size)),
                    (&["libc", "_SC_GETPW_R_SIZE_MAX"], Scalar::from_int(-1, dest.layout.size)),
                    (
                        &["libc", "_SC_NPROCESSORS_ONLN"],
                        Scalar::from_int(NUM_CPUS, dest.layout.size),
                    ),
                ];
                let mut result = None;
                for &(path, path_value) in paths {
                    if let Some(val) = this.eval_path_scalar(path)? {
                        let val = val.to_i32()?;
                        if val == name {
                            result = Some(path_value);
                            break;
                        }
                    }
                }
                if let Some(result) = result {
                    this.write_scalar(result, dest)?;
                } else {
                    throw_unsup_format!("Unimplemented sysconf name: {}", name)
                }
            }

            "isatty" => {
                this.write_null(dest)?;
            }

            "posix_fadvise" => {
                // fadvise is only informational, we can ignore it.
                this.write_null(dest)?;
            }

            "mmap" => {
                // This is a horrible hack, but since the guard page mechanism calls mmap and expects a particular return value, we just give it that value.
                let addr = this.read_scalar(args[0])?.not_undef()?;
                this.write_scalar(addr, dest)?;
            }

            "mprotect" => {
                this.write_null(dest)?;
            }

            _ => {
                match this.tcx.sess.target.target.target_os.as_str() {
                    "linux" => return linux::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret),
                    "macos" => return macos::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret),
                    _ => unreachable!(),
                }
            }
        };

        Ok(true)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate pnet;

/// A simple echo server for packets using a test protocol

use std::iter::repeat;

use pnet::packet::{MutablePacket, Packet};
use pnet::packet::ip::IpNextHeaderProtocols;
use pnet::packet::udp::MutableUdpPacket;
use pnet::transport::{transport_channel, udp_packet_iter};
use pnet::transport::TransportProtocol::Ipv4;
use pnet::transport::TransportChannelType::Layer4;

fn main() {
    let protocol = Layer4(Ipv4(IpNextHeaderProtocols::Test1));

    // Create a new transport channel, dealing with layer 4 packets on a test protocol
    // It has a receive buffer of 4096 bytes.
    let (mut tx, mut rx) = match transport_channel(4096, protocol) {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => {
            panic!("An error occurred when creating the transport channel: {}",
                   e)
        }
    };

    // We treat received packets as if they were UDP packets
    let mut iter = udp_packet_iter(&mut rx);
    loop {
        match iter.next() {
            Ok((packet, addr)) => {
                // Allocate enough space for a new packet
                let mut vec: Vec<u8> = repeat(0u8).take(packet.packet().len()).collect();
                let mut new_packet = MutableUdpPacket::new(&mut vec[..]).unwrap();

                // Create a clone of the original packet
                new_packet.clone_from(&packet);

                // Switch the source and destination ports
                new_packet.set_source(packet.get_destination());
                new_packet.set_destination(packet.get_source());

                // Send the packet
                match tx.send_to(new_packet, addr) {
                    Ok(n) => assert_eq!(n, packet.packet().len()),
                    Err(e) => panic!("failed to send packet: {}", e),
                }
            }
            Err(e) => {
                // If an error occurs, we can handle it here
                panic!("An error occurred while reading: {}", e);
            }
        }
    }
}
<commit_msg>Update main.rs<commit_after>extern crate pnet;

use std::iter::repeat;

use pnet::packet::{MutablePacket, Packet};
use pnet::packet::ip::IpNextHeaderProtocols;
use pnet::packet::udp::MutableUdpPacket;
use pnet::transport::{transport_channel, udp_packet_iter};
use pnet::transport::TransportProtocol::Ipv4;
use pnet::transport::TransportChannelType::Layer4;

fn main() {
    let protocol = Layer4(Ipv4(IpNextHeaderProtocols::Test1));

    // Create a new transport channel, dealing with layer 4 packets on a test protocol
    // It has a receive buffer of 4096 bytes.
    let (mut tx, mut rx) = match transport_channel(4096, protocol) {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => {
            panic!("An error occurred when creating the transport channel: {}",
                   e)
        }
    };

    // We treat received packets as if they were UDP packets
    let mut iter = udp_packet_iter(&mut rx);
    loop {
        match iter.next() {
            Ok((packet, addr)) => {
                // Allocate enough space for a new packet
                let mut vec: Vec<u8> = repeat(0u8).take(packet.packet().len()).collect();
                let mut new_packet = MutableUdpPacket::new(&mut vec[..]).unwrap();

                // Create a clone of the original packet
                new_packet.clone_from(&packet);

                // Switch the source and destination ports
                new_packet.set_source(packet.get_destination());
                new_packet.set_destination(packet.get_source());

                // Send the packet
                match tx.send_to(new_packet, addr) {
                    Ok(n) => assert_eq!(n, packet.packet().len()),
                    Err(e) => panic!("failed to send packet: {}", e),
                }
            }
            Err(e) => {
                // If an error occurs, we can handle it here
                panic!("An error occurred while reading: {}", e);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::mem;

use version::Api;
use version::Version;
use context::CommandContext;
use backend::Facade;
use BufferViewExt;
use ContextExt;
use TransformFeedbackSessionExt;
use buffer::{BufferType, BufferView, BufferViewAnySlice};
use index::PrimitiveType;
use program::OutputPrimitives;
use program::Program;
use vertex::Vertex;

use gl;

/// To use transform feedback, you must create a transform feedback session.
///
/// A transform feedback session mutably borrows the buffer where the data will be written.
/// You can only get your data back when the session is destroyed.
#[derive(Debug)]
pub struct TransformFeedbackSession<'a> {
    buffer: BufferViewAnySlice<'a>,
    program: &'a Program,
}

/// Error that can happen when creating a `TransformFeedbackSession`.
#[derive(Debug, Clone)]
pub enum TransformFeedbackSessionCreationError {

}

/// Returns true if transform feedback is supported by the OpenGL implementation.
pub fn is_transform_feedback_supported<F>(facade: &F) -> bool where F: Facade {
    let context = facade.get_context();

    context.get_version() >= &Version(Api::Gl, 3, 0) ||
    context.get_version() >= &Version(Api::GlEs, 3, 0) ||
    context.get_extensions().gl_ext_transform_feedback
}

impl<'a> TransformFeedbackSession<'a> {
    /// Builds a new transform feedback session.
    ///
    /// TODO: this constructor should ultimately support passing multiple buffers of different
    ///       types
    pub fn new<F, V>(facade: &F, program: &'a Program, buffer: &'a mut BufferView<V>)
                     -> Result<TransformFeedbackSession<'a>, TransformFeedbackSessionCreationError>
                     where F: Facade, V: Vertex + Copy + Send + 'static
    {
        if !is_transform_feedback_supported(facade) {
            panic!();   // FIXME:
        }

        if !program.transform_feedback_matches(&<V as Vertex>::build_bindings(),
                                               mem::size_of::<V>())
        {
            panic!();   // FIXME:
        }

        Ok(TransformFeedbackSession {
            buffer: buffer.as_slice_any(),
            program: program,
        })
    }
}

impl<'a> TransformFeedbackSessionExt for TransformFeedbackSession<'a> {
    fn bind(&self, mut ctxt: &mut CommandContext, draw_primitives: PrimitiveType) {
        // TODO: check that the state matches what is required
        if ctxt.state.transform_feedback_enabled.is_some() {
            unimplemented!();
        }

        self.buffer.indexed_bind_to(ctxt, BufferType::TransformFeedbackBuffer, 0);

        unsafe {
            let primitives = match (self.program.get_output_primitives(), draw_primitives) {
                (Some(OutputPrimitives::Points), _) => gl::POINTS,
                (Some(OutputPrimitives::Lines), _) => gl::LINES,
                (Some(OutputPrimitives::Triangles), _) => gl::TRIANGLES,
                (None, PrimitiveType::Points) => gl::POINTS,
                (None, PrimitiveType::LinesList) => gl::LINES,
                (None, PrimitiveType::LinesListAdjacency) => gl::LINES,
                (None, PrimitiveType::LineStrip) => gl::LINES,
                (None, PrimitiveType::LineStripAdjacency) => gl::LINES,
                (None, PrimitiveType::TrianglesList) => gl::TRIANGLES,
                (None, PrimitiveType::TrianglesListAdjacency) => gl::TRIANGLES,
                (None, PrimitiveType::TriangleStrip) => gl::TRIANGLES,
                (None, PrimitiveType::TriangleStripAdjacency) => gl::TRIANGLES,
                (None, PrimitiveType::TriangleFan) => gl::TRIANGLES,
                (None, PrimitiveType::Patches { .. }) => unreachable!(),
            };

            ctxt.gl.BeginTransformFeedback(primitives);
            ctxt.state.transform_feedback_enabled = Some(primitives);
            ctxt.state.transform_feedback_paused = false;
        }
    }

    fn unbind(mut ctxt: &mut CommandContext) {
        if ctxt.state.transform_feedback_enabled.is_none() {
            return;
        }

        unsafe {
            ctxt.gl.EndTransformFeedback();
            ctxt.state.transform_feedback_enabled = None;
            ctxt.state.transform_feedback_paused = false;
        }
    }

    fn ensure_buffer_out_of_transform_feedback(mut ctxt: &mut CommandContext, buffer: gl::types::GLuint) {
        if ctxt.state.transform_feedback_enabled.is_none() {
            return;
        }

        let mut needs_unbind = false;
        for elem in ctxt.state.indexed_transform_feedback_buffer_bindings.iter_mut() {
            if elem.buffer == buffer {
                needs_unbind = true;
                break;
            }
        }

        if needs_unbind {
            TransformFeedbackSession::unbind(ctxt);
        }
    }
}

impl<'a> Drop for TransformFeedbackSession<'a> {
    fn drop(&mut self) {
        // FIXME: since the session can be mem::forget'ed, the code in buffer/alloc.rs should make
        //        sure that the buffer isn't in use for transform feedback
    }
}
<commit_msg>Add docs to TransformFeedbackSession<commit_after>use std::mem;

use version::Api;
use version::Version;
use context::CommandContext;
use backend::Facade;
use BufferViewExt;
use ContextExt;
use TransformFeedbackSessionExt;
use buffer::{BufferType, BufferView, BufferViewAnySlice};
use index::PrimitiveType;
use program::OutputPrimitives;
use program::Program;
use vertex::Vertex;

use gl;

/// Transform feedback allows you to obtain in a buffer the list of the vertices generated by
/// the vertex shader, geometry shader, or tessellation evaluation shader of your program. This
/// is usually used to cache the result in order to draw the vertices multiple times with multiple
/// different fragment shaders.
///
/// To use transform feedback, you must create a transform feedback session. A transform feedback
/// session mutably borrows the buffer where the data will be written. Each draw command submitted
/// with a session will continue to append data after the data written by the previous draw command.
/// You can only use the data when the session is destroyed.
///
/// # Notes
///
/// Here are a few things to note if you aren't familiar with transform feedback:
///
/// - The program you use must have transform feedback enabled, either with attributes in the
///   vertex shader's source code (for recent OpenGL versions only) or by indicating a list of
///   vertex attributes when building the program.
///
/// - A transform feedback session is bound to a specific program and buffer. You can't switch
///   them once the session has been created. An error is generated if you draw with a different
///   program than the one you created the session with.
///
/// - The transform feedback process doesn't necessarly fill the whole buffer. To retreive the
///   number of vertices that are written to the buffer, use a query object (see the
///   `draw_parameters` module). It is however usually easy to determine in advance the number of
///   vertices that will be written based on the input data.
///
/// - The buffer will obtain either a list of points, a list of lines (two vertices), or a list of
///   triangles (three vertices). If you draw a triangle strip or a triangle fan for example,
///   individual triangles will be written to the buffer (meaning that some vertices will be
///   duplicated).
///
/// - You can use the same session multiple times in a row, in which case the data will continue
///   to be pushed in the buffer after the existing data. However you must always use the same type
///   of primitives and the same program.
///
/// # Example
///
/// ```no_run
/// # #[macro_use]
/// # extern crate glium;
/// # use glium::Surface;
/// # fn main() {
/// # let display: glium::Display = unsafe { std::mem::uninitialized() };
/// # let program: glium::Program = unsafe { std::mem::uninitialized() };
/// # let vb: glium::vertex::VertexBufferAny = unsafe { std::mem::uninitialized() };
/// # let ib: glium::index::IndexBuffer<u16> = unsafe { std::mem::uninitialized() };
/// #[derive(Copy, Clone, Debug, PartialEq)]
/// struct Vertex {
///     output_val: (f32, f32),
/// }
///
/// implement_vertex!(Vertex, output_val);
///
/// let mut out_buffer: glium::VertexBuffer<Vertex> = glium::VertexBuffer::empty(&display, 6);
///
/// {
///     let session = glium::vertex::TransformFeedbackSession::new(&display, &program,
///                                                                &mut out_buffer).unwrap();
///
///     let params = glium::DrawParameters {
///         transform_feedback: Some(&session),
///         .. Default::default()
///     };
///
///     display.draw().draw(&vb, &ib, &program, &uniform!{}, &params).unwrap();
/// }
///
/// let result: Vec<Vertex> = out_buffer.read_if_supported().unwrap();
/// println!("List of generated vertices: {:?}", result);
/// # }
/// ```
#[derive(Debug)]
pub struct TransformFeedbackSession<'a> {
    buffer: BufferViewAnySlice<'a>,
    program: &'a Program,
}

/// Error that can happen when creating a `TransformFeedbackSession`.
#[derive(Debug, Clone)]
pub enum TransformFeedbackSessionCreationError {

}

/// Returns true if transform feedback is supported by the OpenGL implementation.
pub fn is_transform_feedback_supported<F>(facade: &F) -> bool where F: Facade {
    let context = facade.get_context();

    context.get_version() >= &Version(Api::Gl, 3, 0) ||
    context.get_version() >= &Version(Api::GlEs, 3, 0) ||
    context.get_extensions().gl_ext_transform_feedback
}

impl<'a> TransformFeedbackSession<'a> {
    /// Builds a new transform feedback session.
    ///
    /// TODO: this constructor should ultimately support passing multiple buffers of different
    ///       types
    pub fn new<F, V>(facade: &F, program: &'a Program, buffer: &'a mut BufferView<V>)
                     -> Result<TransformFeedbackSession<'a>, TransformFeedbackSessionCreationError>
                     where F: Facade, V: Vertex + Copy + Send + 'static
    {
        if !is_transform_feedback_supported(facade) {
            panic!();   // FIXME:
        }

        if !program.transform_feedback_matches(&<V as Vertex>::build_bindings(),
                                               mem::size_of::<V>())
        {
            panic!();   // FIXME:
        }

        Ok(TransformFeedbackSession {
            buffer: buffer.as_slice_any(),
            program: program,
        })
    }
}

impl<'a> TransformFeedbackSessionExt for TransformFeedbackSession<'a> {
    fn bind(&self, mut ctxt: &mut CommandContext, draw_primitives: PrimitiveType) {
        // TODO: check that the state matches what is required
        if ctxt.state.transform_feedback_enabled.is_some() {
            unimplemented!();
        }

        self.buffer.indexed_bind_to(ctxt, BufferType::TransformFeedbackBuffer, 0);

        unsafe {
            let primitives = match (self.program.get_output_primitives(), draw_primitives) {
                (Some(OutputPrimitives::Points), _) => gl::POINTS,
                (Some(OutputPrimitives::Lines), _) => gl::LINES,
                (Some(OutputPrimitives::Triangles), _) => gl::TRIANGLES,
                (None, PrimitiveType::Points) => gl::POINTS,
                (None, PrimitiveType::LinesList) => gl::LINES,
                (None, PrimitiveType::LinesListAdjacency) => gl::LINES,
                (None, PrimitiveType::LineStrip) => gl::LINES,
                (None, PrimitiveType::LineStripAdjacency) => gl::LINES,
                (None, PrimitiveType::TrianglesList) => gl::TRIANGLES,
                (None, PrimitiveType::TrianglesListAdjacency) => gl::TRIANGLES,
                (None, PrimitiveType::TriangleStrip) => gl::TRIANGLES,
                (None, PrimitiveType::TriangleStripAdjacency) => gl::TRIANGLES,
                (None, PrimitiveType::TriangleFan) => gl::TRIANGLES,
                (None, PrimitiveType::Patches { .. }) => unreachable!(),
            };

            ctxt.gl.BeginTransformFeedback(primitives);
            ctxt.state.transform_feedback_enabled = Some(primitives);
            ctxt.state.transform_feedback_paused = false;
        }
    }

    fn unbind(mut ctxt: &mut CommandContext) {
        if ctxt.state.transform_feedback_enabled.is_none() {
            return;
        }

        unsafe {
            ctxt.gl.EndTransformFeedback();
            ctxt.state.transform_feedback_enabled = None;
            ctxt.state.transform_feedback_paused = false;
        }
    }

    fn ensure_buffer_out_of_transform_feedback(mut ctxt: &mut CommandContext, buffer: gl::types::GLuint) {
        if ctxt.state.transform_feedback_enabled.is_none() {
            return;
        }

        let mut needs_unbind = false;
        for elem in ctxt.state.indexed_transform_feedback_buffer_bindings.iter_mut() {
            if elem.buffer == buffer {
                needs_unbind = true;
                break;
            }
        }

        if needs_unbind {
            TransformFeedbackSession::unbind(ctxt);
        }
    }
}

impl<'a> Drop for TransformFeedbackSession<'a> {
    fn drop(&mut self) {
        // FIXME: since the session can be mem::forget'ed, the code in buffer/alloc.rs should make
        //        sure that the buffer isn't in use for transform feedback
    }
}
<|endoftext|>


<commit_before>// If no features are used, there is an "unused mut" warning in `ALL_EXTENSIONS`
// BUG: ? For some reason this doesn't do anything if I try and function scope this
#![allow(unused_mut)]

use lazy_static::lazy_static;
use std::collections::HashMap;
use std::error::Error;

use crate::map::Map;
use crate::{file::FileStoredFormat, value::Value, Format};

#[cfg(feature = "toml")]
mod toml;

#[cfg(feature = "json")]
mod json;

#[cfg(feature = "yaml")]
mod yaml;

#[cfg(feature = "ini")]
mod ini;

#[cfg(feature = "ron")]
mod ron;

#[cfg(feature = "json5")]
mod json5;

/// File formats provided by the library.
///
/// Although it is possible to define custom formats using [`Format`] trait it is recommended to use FileFormat if possible.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FileFormat {
    /// TOML (parsed with toml)
    #[cfg(feature = "toml")]
    Toml,

    /// JSON (parsed with serde_json)
    #[cfg(feature = "json")]
    Json,

    /// YAML (parsed with yaml_rust)
    #[cfg(feature = "yaml")]
    Yaml,

    /// INI (parsed with rust_ini)
    #[cfg(feature = "ini")]
    Ini,

    /// RON (parsed with ron)
    #[cfg(feature = "ron")]
    Ron,

    /// JSON5 (parsed with json5)
    #[cfg(feature = "json5")]
    Json5,
}

lazy_static! {
    #[doc(hidden)]
    // #[allow(unused_mut)] ?
    pub static ref ALL_EXTENSIONS: HashMap<FileFormat, Vec<&'static str>> = {
        let mut formats: HashMap<FileFormat, Vec<_>> = HashMap::new();

        #[cfg(feature = "toml")]
        formats.insert(FileFormat::Toml, vec!["toml"]);

        #[cfg(feature = "json")]
        formats.insert(FileFormat::Json, vec!["json"]);

        #[cfg(feature = "yaml")]
        formats.insert(FileFormat::Yaml, vec!["yaml", "yml"]);

        #[cfg(feature = "ini")]
        formats.insert(FileFormat::Ini, vec!["ini"]);

        #[cfg(feature = "ron")]
        formats.insert(FileFormat::Ron, vec!["ron"]);

        #[cfg(feature = "json5")]
        formats.insert(FileFormat::Json5, vec!["json5"]);

        formats
    };
}

impl FileFormat {
    pub(crate) fn extensions(&self) -> &'static [&'static str] {
        // It should not be possible for this to fail
        // A FileFormat would need to be declared without being added to the
        // ALL_EXTENSIONS map.
        ALL_EXTENSIONS.get(self).unwrap()
    }

    pub(crate) fn parse(
        &self,
        uri: Option<&String>,
        text: &str,
    ) -> Result<Map<String, Value>, Box<dyn Error + Send + Sync>> {
        match self {
            #[cfg(feature = "toml")]
            FileFormat::Toml => toml::parse(uri, text),

            #[cfg(feature = "json")]
            FileFormat::Json => json::parse(uri, text),

            #[cfg(feature = "yaml")]
            FileFormat::Yaml => yaml::parse(uri, text),

            #[cfg(feature = "ini")]
            FileFormat::Ini => ini::parse(uri, text),

            #[cfg(feature = "ron")]
            FileFormat::Ron => ron::parse(uri, text),

            #[cfg(feature = "json5")]
            FileFormat::Json5 => json5::parse(uri, text),
        }
    }
}

impl Format for FileFormat {
    fn parse(
        &self,
        uri: Option<&String>,
        text: &str,
    ) -> Result<Map<String, Value>, Box<dyn Error + Send + Sync>> {
        self.parse(uri, text)
    }
}

impl FileStoredFormat for FileFormat {
    fn file_extensions(&self) -> &'static [&'static str] {
        self.extensions()
    }
}
<commit_msg>Fix: If no features are enabled, this should still build<commit_after>// If no features are used, there is an "unused mut" warning in `ALL_EXTENSIONS`
// BUG: ? For some reason this doesn't do anything if I try and function scope this
#![allow(unused_mut)]

use lazy_static::lazy_static;
use std::collections::HashMap;
use std::error::Error;

use crate::map::Map;
use crate::{file::FileStoredFormat, value::Value, Format};

#[cfg(feature = "toml")]
mod toml;

#[cfg(feature = "json")]
mod json;

#[cfg(feature = "yaml")]
mod yaml;

#[cfg(feature = "ini")]
mod ini;

#[cfg(feature = "ron")]
mod ron;

#[cfg(feature = "json5")]
mod json5;

/// File formats provided by the library.
///
/// Although it is possible to define custom formats using [`Format`] trait it is recommended to use FileFormat if possible.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FileFormat {
    /// TOML (parsed with toml)
    #[cfg(feature = "toml")]
    Toml,

    /// JSON (parsed with serde_json)
    #[cfg(feature = "json")]
    Json,

    /// YAML (parsed with yaml_rust)
    #[cfg(feature = "yaml")]
    Yaml,

    /// INI (parsed with rust_ini)
    #[cfg(feature = "ini")]
    Ini,

    /// RON (parsed with ron)
    #[cfg(feature = "ron")]
    Ron,

    /// JSON5 (parsed with json5)
    #[cfg(feature = "json5")]
    Json5,
}

lazy_static! {
    #[doc(hidden)]
    // #[allow(unused_mut)] ?
    pub static ref ALL_EXTENSIONS: HashMap<FileFormat, Vec<&'static str>> = {
        let mut formats: HashMap<FileFormat, Vec<_>> = HashMap::new();

        #[cfg(feature = "toml")]
        formats.insert(FileFormat::Toml, vec!["toml"]);

        #[cfg(feature = "json")]
        formats.insert(FileFormat::Json, vec!["json"]);

        #[cfg(feature = "yaml")]
        formats.insert(FileFormat::Yaml, vec!["yaml", "yml"]);

        #[cfg(feature = "ini")]
        formats.insert(FileFormat::Ini, vec!["ini"]);

        #[cfg(feature = "ron")]
        formats.insert(FileFormat::Ron, vec!["ron"]);

        #[cfg(feature = "json5")]
        formats.insert(FileFormat::Json5, vec!["json5"]);

        formats
    };
}

impl FileFormat {
    pub(crate) fn extensions(&self) -> &'static [&'static str] {
        // It should not be possible for this to fail
        // A FileFormat would need to be declared without being added to the
        // ALL_EXTENSIONS map.
        ALL_EXTENSIONS.get(self).unwrap()
    }

    pub(crate) fn parse(
        &self,
        uri: Option<&String>,
        text: &str,
    ) -> Result<Map<String, Value>, Box<dyn Error + Send + Sync>> {
        match self {
            #[cfg(feature = "toml")]
            FileFormat::Toml => toml::parse(uri, text),

            #[cfg(feature = "json")]
            FileFormat::Json => json::parse(uri, text),

            #[cfg(feature = "yaml")]
            FileFormat::Yaml => yaml::parse(uri, text),

            #[cfg(feature = "ini")]
            FileFormat::Ini => ini::parse(uri, text),

            #[cfg(feature = "ron")]
            FileFormat::Ron => ron::parse(uri, text),

            #[cfg(feature = "json5")]
            FileFormat::Json5 => json5::parse(uri, text),

            #[cfg(all(
                not(feature = "toml"),
                not(feature = "json"),
                not(feature = "yaml"),
                not(feature = "ini"),
                not(feature = "ron"),
                not(feature = "json5"),
            ))]
            _ => unreachable!("No features are enabled, this library won't work without features"),
        }
    }
}

impl Format for FileFormat {
    fn parse(
        &self,
        uri: Option<&String>,
        text: &str,
    ) -> Result<Map<String, Value>, Box<dyn Error + Send + Sync>> {
        self.parse(uri, text)
    }
}

impl FileStoredFormat for FileFormat {
    fn file_extensions(&self) -> &'static [&'static str] {
        self.extensions()
    }
}
<|endoftext|>


<commit_before>use std::io::{ self, BufRead, Write };
use std::borrow::ToOwned;
use std::iter::{ Iterator, AdditiveIterator };

use log::Event;
use format::{ Encode, Decode };

use l::LogLevel::Info;

//use regex::Regex;

use chrono::*;

pub struct Weechat3;

//static NORMAL_LINE: Regex = regex!(r"^(\d+-\d+-\d+ \d+:\d+:\d+)\t[@%+~&]?([^ <-]\S+)\t(.*)");
//static ACTION_LINE: Regex = regex!(r"^(\d+-\d+-\d+ \d+:\d+:\d+)\t \*\t(\S+) (.*)");
//static OTHER_LINES: Regex = regex!(r"^(\d+-\d+-\d+ \d+:\d+:\d+)\s(?:--|<--|-->)\s(\S+)\s(\S+)\s(\S+)\s(\S+)\s(\S+)(.*)\n$");
//static OTHER_LINES: Regex = regex!(r"(.+)");

static TIME_DATE_FORMAT: &'static str = "%Y-%m-%d %H:%M:%S";

pub struct Iter<R> where R: BufRead {
    input: R,
    buffer: String
}

impl<R> Iterator for Iter<R> where R: BufRead {
    type Item = ::Result<Event>;
    fn next(&mut self) -> Option<::Result<Event>> {
        fn timestamp(date: &str, time: &str) -> i64 {
            UTC.datetime_from_str(&format!("{} {}", date, time), TIME_DATE_FORMAT).unwrap().timestamp()
        }
        fn join(s: &[&str]) -> String {
            let len = s.iter().map(|s| s.len()).sum();
            let mut out = s.iter().fold(String::with_capacity(len),
               |mut s, b| { s.push_str(b); s.push(' '); s });
            out.pop(); out
        }
        fn mask(s: &str) -> String {
            s.trim_left_matches('(').trim_right_matches(')').to_owned()
        }

        loop {
            self.buffer.clear();
            match self.input.read_line(&mut self.buffer) {
                Ok(0) | Err(_) => return None,
                Ok(_) => ()
            }

            let tokens = self.buffer.split(|c: char| c.is_whitespace()).collect::<Vec<_>>();
            if log_enabled!(Info) {
                info!("Original:  `{}`", self.buffer);
                info!("Parsing:   {:?}", tokens);
            }
            match tokens[..tokens.len() - 1].as_ref() {
                [date, time, "-->", nick, host, "has", "joined", channel, _..] => return Some(Ok(Event::Join {
                    nick: nick.to_owned(), channel: channel.to_owned(), mask: mask(host),
                    time: timestamp(date, time)
                })),
                [date, time, "<--", nick, host, "has", "left", channel, reason..] => return Some(Ok(Event::Part {
                    nick: nick.to_owned(), channel: channel.to_owned(), mask: mask(host),
                    reason: mask(&join(reason)), time: timestamp(date, time)
                })),
                [date, time, "--", notice, content..]
                    if notice.starts_with("Notice(")
                    => return Some(Ok(Event::Notice {
                    nick: notice["Notice(".len()..notice.len() - 2].to_owned(),
                    content: join(content),
                    time: timestamp(date, time)
                })),
                [date, time, "--", "irc:", "disconnected", "from", "server", _..] => return Some(Ok(Event::Disconnect {
                    time: timestamp(date, time)
                })),
                [date, time, sp, "*", nick, msg..]
                    if sp.is_empty()
                    => return Some(Ok(Event::Action {
                    from: nick.to_owned(), content: join(msg),
                    time: timestamp(date, time)
                })),
                [date, time, nick, msg..] => return Some(Ok(Event::Msg {
                    from: nick.to_owned(),
                    content: join(msg),
                    time: timestamp(date, time)
                })),
                _ => ()
            }
            /*
            if let Some(cap) = NORMAL_LINE.captures(line) {
                return Some(Ok(Event::Msg {
                    from: cap.at(2).unwrap().to_owned(),
                    content: cap.at(3).unwrap().to_owned(),
                    time: time(cap.at(1).unwrap())
                }))
            } else if let Some(cap) = ACTION_LINE.captures(line) {
                return Some(Ok(Event::Action {
                    from: cap.at(1).unwrap().to_owned(),
                    content: cap.at(2).unwrap().to_owned(),
                    time: time(cap.at(1).unwrap())
                }))
            } else if let Some(cap) = OTHER_LINES.captures(line) {
                println!("{:?}", cap.iter().collect::<Vec<_>>());
                if cap.at(4) == Some("has") && cap.at(5) == Some("kicked") {
                    println!("    Matched Event::Kick");
                    return Some(Ok(Event::Kick {
                        kicked_nick: cap.at(6).unwrap().to_owned(),
                        kicking_nick: cap.at(3).unwrap().to_owned(),
                        kick_message: cap.at(4).unwrap().to_owned(),
                        time: time(cap.at(1).unwrap())
                    }))
                } else if cap.at(2) == Some("Topic") && cap.at(3) == Some("for") {
                    return Some(Ok(Event::Topic {
                        topic: { let mut s = cap.at(6).unwrap().to_string(); s.push_str(cap.at(7).unwrap()); s },
                        time: time(cap.at(1).unwrap())
                    }))
                } else if cap.at(3) == Some("has") && cap.at(5) == Some("changed") && cap.at(6) == Some("topic") {
                    return Some(Ok(Event::TopicChange {
                        new_topic: cap.at(5).unwrap().to_owned(),
                        time: time(cap.at(1).unwrap())
                    }))
                } else if cap.at(3) == Some("Mode") {
                    return Some(Ok(Event::Mode {
                        time: time(cap.at(1).unwrap())
                    }))
                } else if cap.at(4) == Some("has") && cap.at(5) == Some("joined") {
                    return Some(Ok(Event::Join {
                        nick: cap.at(2).unwrap().to_owned(),
                        channel: cap.at(6).unwrap().to_owned(),
                        mask: mask(cap.at(3).unwrap()),
                        time: time(cap.at(1).unwrap())
                    }))
                } else if cap.at(4) == Some("has") && cap.at(5) == Some("left") {
                    return Some(Ok(Event::Part {
                        nick: cap.at(2).unwrap().to_owned(),
                        channel: cap.at(6).unwrap().to_owned(),
                        reason: cap.at(7).unwrap().to_owned(),
                        mask: mask(cap.at(3).unwrap()),
                        time: time(cap.at(1).unwrap())
                    }))
                } else if cap.at(5) == Some("now") && cap.at(6) == Some("known") {
                    return Some(Ok(Event::Nick {
                        old: String::new(),
                        new: String::new(),
                        time: time(cap.at(1).unwrap())
                    }))
                }
            }*/
        }
    }
}

impl<R> Decode<R, Iter<R>> for Weechat3 where R: BufRead {
    fn decode(&mut self, input: R) -> Iter<R> {
        Iter {
            input: input,
            buffer: String::new()
        }
    }
}

impl<W> Encode<W> for Weechat3 where W: Write {
    fn encode(&self, mut output: W, event: &Event) -> io::Result<()> {
        fn date(t: i64) -> String {
            format!("{}", UTC.timestamp(t, 0).format(TIME_DATE_FORMAT))
        }
        match event {
            &Event::Msg { ref from, ref content, ref time } => {
                try!(writeln!(&mut output, "{}\t{}\t{}", date(*time), from, content))
            },
            &Event::Action { ref from, ref content, ref time } => {
                try!(writeln!(&mut output, "{}\t *\t{} {}", date(*time), from, content))
            },
            &Event::Join { ref nick, ref mask, ref channel, ref time } => {
                try!(writeln!(&mut output, "{}\t-->\t{} ({}) has joined {}",
                date(*time), nick, mask, channel))
            },
            &Event::Part { ref nick, ref mask, ref channel, ref time, ref reason } => {
                try!(write!(&mut output, "{}\t<--\t{} ({}) has left {}",
                date(*time), nick, mask, channel));
                if reason.len() > 0 {
                    try!(write!(&mut output, " ({})", reason));
                }
                try!(write!(&mut output, "\n"))
            },
            &Event::Quit { ref nick, ref mask, ref time, ref reason } => {
                try!(write!(&mut output, "{}\t<--\t{} ({}) has quit", date(*time), nick, mask));
                if reason.len() > 0 {
                    try!(write!(&mut output, " ({})", reason));
                }
                try!(write!(&mut output, "\n"))
            },
            &Event::Disconnect { ref time } => {
                try!(writeln!(&mut output, "{}\t--\tirc: disconnected from server", date(*time)))
            },
            &Event::Notice { ref nick, ref content, ref time } => {
                try!(writeln!(&mut output, "{}\t--\tNotice({}): {}", date(*time), nick, content))
            },
            _ => ()
        }
        Ok(())
    }
}
<commit_msg>Remove remains of regex parsing<commit_after>use std::io::{ self, BufRead, Write };
use std::borrow::ToOwned;
use std::iter::{ Iterator, AdditiveIterator };

use log::Event;
use format::{ Encode, Decode };

use l::LogLevel::Info;

use chrono::*;

pub struct Weechat3;

static TIME_DATE_FORMAT: &'static str = "%Y-%m-%d %H:%M:%S";

pub struct Iter<R> where R: BufRead {
    input: R,
    buffer: String
}

impl<R> Iterator for Iter<R> where R: BufRead {
    type Item = ::Result<Event>;
    fn next(&mut self) -> Option<::Result<Event>> {
        fn timestamp(date: &str, time: &str) -> i64 {
            UTC.datetime_from_str(&format!("{} {}", date, time), TIME_DATE_FORMAT).unwrap().timestamp()
        }
        fn join(s: &[&str]) -> String {
            let len = s.iter().map(|s| s.len()).sum();
            let mut out = s.iter().fold(String::with_capacity(len),
               |mut s, b| { s.push_str(b); s.push(' '); s });
            out.pop(); out
        }
        fn mask(s: &str) -> String {
            s.trim_left_matches('(').trim_right_matches(')').to_owned()
        }

        loop {
            self.buffer.clear();
            match self.input.read_line(&mut self.buffer) {
                Ok(0) | Err(_) => return None,
                Ok(_) => ()
            }

            let tokens = self.buffer.split(|c: char| c.is_whitespace()).collect::<Vec<_>>();
            if log_enabled!(Info) {
                info!("Original:  `{}`", self.buffer);
                info!("Parsing:   {:?}", tokens);
            }
            match tokens[..tokens.len() - 1].as_ref() {
                [date, time, "-->", nick, host, "has", "joined", channel, _..] => return Some(Ok(Event::Join {
                    nick: nick.to_owned(), channel: channel.to_owned(), mask: mask(host),
                    time: timestamp(date, time)
                })),
                [date, time, "<--", nick, host, "has", "left", channel, reason..] => return Some(Ok(Event::Part {
                    nick: nick.to_owned(), channel: channel.to_owned(), mask: mask(host),
                    reason: mask(&join(reason)), time: timestamp(date, time)
                })),
                [date, time, "--", notice, content..]
                    if notice.starts_with("Notice(")
                    => return Some(Ok(Event::Notice {
                    nick: notice["Notice(".len()..notice.len() - 2].to_owned(),
                    content: join(content),
                    time: timestamp(date, time)
                })),
                [date, time, "--", "irc:", "disconnected", "from", "server", _..] => return Some(Ok(Event::Disconnect {
                    time: timestamp(date, time)
                })),
                [date, time, sp, "*", nick, msg..]
                    if sp.is_empty()
                    => return Some(Ok(Event::Action {
                    from: nick.to_owned(), content: join(msg),
                    time: timestamp(date, time)
                })),
                [date, time, nick, msg..] => return Some(Ok(Event::Msg {
                    from: nick.to_owned(),
                    content: join(msg),
                    time: timestamp(date, time)
                })),
                _ => ()
            }
        }
    }
}

impl<R> Decode<R, Iter<R>> for Weechat3 where R: BufRead {
    fn decode(&mut self, input: R) -> Iter<R> {
        Iter {
            input: input,
            buffer: String::new()
        }
    }
}

impl<W> Encode<W> for Weechat3 where W: Write {
    fn encode(&self, mut output: W, event: &Event) -> io::Result<()> {
        fn date(t: i64) -> String {
            format!("{}", UTC.timestamp(t, 0).format(TIME_DATE_FORMAT))
        }
        match event {
            &Event::Msg { ref from, ref content, ref time } => {
                try!(writeln!(&mut output, "{}\t{}\t{}", date(*time), from, content))
            },
            &Event::Action { ref from, ref content, ref time } => {
                try!(writeln!(&mut output, "{}\t *\t{} {}", date(*time), from, content))
            },
            &Event::Join { ref nick, ref mask, ref channel, ref time } => {
                try!(writeln!(&mut output, "{}\t-->\t{} ({}) has joined {}",
                date(*time), nick, mask, channel))
            },
            &Event::Part { ref nick, ref mask, ref channel, ref time, ref reason } => {
                try!(write!(&mut output, "{}\t<--\t{} ({}) has left {}",
                date(*time), nick, mask, channel));
                if reason.len() > 0 {
                    try!(write!(&mut output, " ({})", reason));
                }
                try!(write!(&mut output, "\n"))
            },
            &Event::Quit { ref nick, ref mask, ref time, ref reason } => {
                try!(write!(&mut output, "{}\t<--\t{} ({}) has quit", date(*time), nick, mask));
                if reason.len() > 0 {
                    try!(write!(&mut output, " ({})", reason));
                }
                try!(write!(&mut output, "\n"))
            },
            &Event::Disconnect { ref time } => {
                try!(writeln!(&mut output, "{}\t--\tirc: disconnected from server", date(*time)))
            },
            &Event::Notice { ref nick, ref content, ref time } => {
                try!(writeln!(&mut output, "{}\t--\tNotice({}): {}", date(*time), nick, content))
            },
            _ => ()
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before><commit_msg>Use separate buffer lengths for hrtf example for lower latency<commit_after><|endoftext|>