How do I read a single packet from TcpStream using Tokio? - rust

I'm trying to receive a single data packet using tokio:
extern crate tokio;
extern crate tokio_io;
use tokio::net::{TcpListener};
use tokio::prelude::*;
use std::net::SocketAddr;
fn main() {
let addr = "0.0.0.0:8080".parse::<SocketAddr>().unwrap();
let socket = TcpListener::bind(&addr).unwrap();
println!("Listening on: {}", addr);
let done = socket
.incoming()
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
.for_each(move |mut socket| {
let mut bytes = vec![];
bytes.reserve(1024);
let processor = socket.read_buf(&mut bytes).into_future()
.and_then(move |_size| {
println!("bytes: {:?}", bytes);
Ok(())
})
.map_err(|_| ());;
tokio::spawn(processor)
});
tokio::run(done);
}
This code prints an empty packet. How do I change this code to print the received packet with data?

For myself, I almost found the answer. Very helpful Similar question.
struct AsWeGetIt<R>(R);
impl<R> Stream for AsWeGetIt<R>
where
R: AsyncRead,
{
type Item = BytesMut;
type Error = std::io::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let mut buf = BytesMut::with_capacity(1000);
self.0
.read_buf(&mut buf)
.map(|async| async.map(|_| Some(buf)))
}
}
....
let processor = AsWeGetIt(socket).into_future()
.and_then(|(bytes,_)| {
println!("bytes: {:?}", bytes);
Ok(())
}).map_err(|_| ());
But for a better understanding how to do without a separate structure ...
And why and what is the map using?

If your goal is really to receive one packet, I think you've succeeded!
I've tested the program a few times, and I get a response. I'm testing with:
nc 127.0.0.1 8080 <<< hello
After running that a few times, I get the following output:
Listening on: 0.0.0.0:8080
bytes: [104, 101, 108, 108, 111, 10]
bytes: []
bytes: [104, 101, 108, 108, 111, 10]
bytes: []
bytes: [104, 101, 108, 108, 111, 10]
bytes: []
bytes: [104, 101, 108, 108, 111, 10]
bytes: [104, 101, 108, 108, 111, 10]
As you can see, sometimes we have data ready, and sometimes we don't. I think in your testing you've just been unlucky and only gotten TCP responses before any data has been sent?
I'm about 90% sure that TCP streams can include empty packets, and this is what we're seeing. (if someone has more knowledge here, feel free to edit the answer or comment).
To fix your program you might want to reconsider your goal.
Reading one TCP packet seems rarely helpful. Instead, generally, you want to read some amount of bytes and process the data as it comes. My understanding of TCP is as a stream of bytes, not really a stream of packets. The packets are just a way to get bytes from one place to another, and they could be any length without breaking compatibility. "One packet" is a fairly nebulous concept.
Here's an example reading the first 16 bytes of the stream using the tokio::io::read_exact function:
extern crate tokio;
use tokio::net::TcpListener;
use tokio::prelude::*;
use std::net::SocketAddr;
fn main() {
let addr = "0.0.0.0:8080".parse::<SocketAddr>().unwrap();
let socket = TcpListener::bind(&addr).unwrap();
println!("Listening on: {}", addr);
let done = socket
.incoming()
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
.for_each(move |mut socket| {
// this function deals with bytes a bit differently and will just fill the
// buffer exactly rather than adding onto the end.
let mut bytes = vec![0; 16];
let processor = tokio::io::read_exact(socket, bytes)
.and_then(move |(socket, bytes)| {
println!("bytes: {:?}", bytes);
Ok(())
})
.map_err(|_| ());
tokio::spawn(processor)
});
tokio::run(done);
}

Related

Can't receive DNS queries over TCP

When I send DNS query to root server for com name servers over UDP, it's working just fine, but the message is truncated. When I retry over TCP, the Root server does not times out.
use std::net::TcpStream;
use std::io::Write;
fn main() {
let mut stream = TcpStream::connect("199.9.14.201:53")
.unwrap();
stream.write(&[251, 188, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 99, 111, 109, 0, 0, 2, 0, 1])
.expect("ugh");
let mut buf: [u8; 1000] = [1; 1000];
match stream.peek(&mut buf) {
Ok(..) => {
println!("{:?}", buf);
}
Err(e) => {
println!("{:?}", e);
}
}
println!("Hello, world!");
}
From RFC 1035 section 4.2.2:
Messages sent over TCP connections use server port 53 (decimal). The
message is prefixed with a two byte length field which gives the message length, excluding the two byte length field. This length
field allows the low-level processing to assemble a complete message
before beginning to parse it.
I looks like your code does not include the required message length. Thus the first two bytes of your message are treated as length which describe a length much larger than what you has send. Therefore the server is waiting for more data.

How to remove nul characters from string in rust?

use std::net::TcpStream;
use std::io::*;
use std::io::{self, Write};
use std::str::from_utf8;
use std::process::Command;
const MESSAGE_SIZE: usize = 10;
fn main()
{
let mut stream = TcpStream::connect("192.168.1.3:4444").unwrap();
println!("CONNECTED !!");
loop
{
stream.write(b"[*] >> ").expect("A");
let mut rx_bytes = [0u8; MESSAGE_SIZE];
stream.read(&mut rx_bytes).expect("k");
let received = from_utf8(&rx_bytes).expect("valid utf8").to_string();
print!("{}",received);
let output = Command::new("powershell").arg(received).output().expect("failed to execute process"); // Error at .arg(received).
println!("status: {}", output.status);
io::stdout().write_all(&output.stdout).unwrap();
io::stderr().write_all(&output.stderr).unwrap();
let res = from_utf8(&output.stdout).expect("valid utf8").to_string();
stream.write(res.as_bytes());
}
}
ERROR:-
thread 'main' panicked at 'failed to execute process: Error { kind: InvalidInput, message: "nul byte found in provided data" }', .\main.rs:20:72
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace
PS:- I am using netcat as the server.
The first thing you should probably do (if you haven't already) is to look at your incoming data:
println!("{:x?}", "azAZ\0X09".as_bytes()); // [61, 7a, 41, 5a, 0, 58, 30, 39]
Then you can determine why there is a null byte in there and what needs to be done about it.

Why does my socket read lock the write of the data?

I use tokio::net::TcpStream to connect a small TCP server, I write a few bytes and expect to read the response from the server.
When I do that with the nc command, it works perfectly
[denis#docker-1 ~]$ echo "get" | nc 10.0.0.11 9090
[37e64dd7-91db-4c13-9f89-f1c87467ffb3][processed]
and the server logs show
Incoming peer instructions.
Waiting for peer instructions...
Reading bytes...
Got a few bytes [4]
Got a few bytes [[103, 101, 116, 10, 0, ...]]
Reading bytes...
Got a few bytes [0]
Got a few bytes [[0, 0, 0, 0, 0, 0,...]]
Writing some data back from peer : [37e64dd7-91db-4c13-9f89-f1c87467ffb3]
But from my Rust client, I can write the bytes but as soon as I want to read the data from the server, everything is locked (even the write action)
use std::collections::HashMap;
use std::ops::DerefMut;
use tokio::io;
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use uuid::Uuid;
use std::sync::RwLock;
use lazy_static::*;
#[tokio::main]
async fn main() {
let data = "set".to_string();
let mut stream = TcpStream::connect("10.0.0.11:9090").await.unwrap();
let ( mut read, mut write) = tokio::io::split(stream);
let u2 = data.as_bytes();
write.write_all(u2).await.unwrap();
let mut msg : [u8;1024] = [0;1024];
let _response_size = read.read(&mut msg).await.unwrap();
println!("GOT = {:?}", msg);
}
When looking at the server logs (see below), it reads the 3 bytes sent by the client, but then it is not able to read further, waiting to detect there is 0 byte left to read.
Incoming peer instructions.
Waiting for peer instructions...
Reading bytes...
Got a few bytes [3]
Got a few bytes [[115, 101, 116, 0, 0, ...]]
Reading bytes...
Here is the server code
use std::collections::HashMap;
use std::ops::DerefMut;
use tokio::io;
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use uuid::Uuid;
use std::sync::RwLock;
use lazy_static::*;
struct DataPool {
data : [u8;1024],
size : usize,
}
async fn whirl_socket( socket : &mut TcpStream ) -> Vec<DataPool> {
let mut pool: Vec<DataPool> = vec![];
let mut buf = [0; 1024];
// In a loop, read data from the socket until finished
loop {
println!("Reading bytes...");
buf = [0; 1024];
let n = match socket.read(&mut buf).await {
Ok(n) => n,
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
break;
}
};
println!("Got a few bytes [{}]", n);
println!("Got a few bytes [{:?}]", &buf);
pool.push(DataPool {
data: buf,
size: n,
});
if n == 0 {
break;
}
}
pool
}
async fn launch_server_listener() -> io::Result<()> {
println!("Listen to 9090...");
let listener = TcpListener::bind("10.0.0.11:9090").await?;
loop {
println!("Waiting for peer instructions...");
let (mut socket, _) = listener.accept().await?;
println!("Incoming peer instructions.");
tokio::spawn(async move {
let mut pool= whirl_socket(&mut socket).await;
let my_uuid = Uuid::new_v4();
// Write the data back
println!("Writing some data back from peer : [{}]", my_uuid);
let s = format!( "[{}][processed]\n", my_uuid.to_string());
let u = s.as_bytes();
if let Err(e) = socket.write_all(u).await {
eprintln!("failed to write to socket; err = {:?}", e);
return;
}
});
}
}
async fn start_servers() -> Result<(), Box<dyn std::error::Error>> {
let _r = tokio::join!(launch_server_listener());
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
start_servers().await?;
Ok(())
}
A read of 0 bytes means the read stream has closed. So in your client code you need to close the write stream. You can do this with .shutdown() from the AsyncWriteExt trait:
write.write_all(u2).await.unwrap();
write.shutdown().await.unwrap();

How to define &mut [u8] in Rust

I want to call openssl::sign::Signer::sign, which has the signature:
pub fn sign(&self, buf: &mut [u8]) -> Result<usize, ErrorStack>
I'm trying to hand over buf. How to I make it &mut [u8]?
cargo.toml
[dependencies]
openssl = { version = "0.10", features = ["vendored"] }
src/main.rs
use openssl::{base64, sign::Signer};
fn main() {
let mut buffer = [];
let str = base64::encode_block(signer.sign(&mut buffer));
}
But get an error:
openssl::base64::encode_block(signer.sign(&mut buffer));
^^^^^^^^^^^^^^^^^^^ expected `&[u8]`, found enum `std::result::Result`
I'm not familiar with this crate, but base64::encode_block expects &[u8], while the return value of Signer::sign is a Result that returns the number of bytes written as usize.
I'm not sure if the number of bytes written is what you want to encode?
If you want to encode buffer to base64, you could do:
use openssl;
fn main() {
let mut buf = [104, 101, 108, 108, 111];
let encoded = openssl::base64::encode_block(&buf);
println!("{}", encoded);
}
That should give you:
aGVsbG8=
Which is hello as base64.
Or, as an example, if you want to sign the given buf and encode that as base64:
use openssl::{base64, hash::MessageDigest, pkey::PKey, rsa::Rsa, sign::Signer};
fn main() {
let keypair = Rsa::generate(2048).unwrap();
let keypair = PKey::from_rsa(keypair).unwrap();
let signer = Signer::new(MessageDigest::sha256(), &keypair).unwrap();
let mut buf = [104, 101, 108, 108, 111].repeat(52);
let written = signer.sign(&mut buf).unwrap();
println!("bytes written: {}", written);
let encoded = base64::encode_block(&buf);
println!("base64: {}", encoded);
}
Which returns:
bytes written: 256
base64: wB4lBbyzpfRPInxhwm0XVKI3dQYqcUZWNdKyb4RTGDUmwq1DDDbQjMRmFBobRse3pNOxoMy+QQNSXsgI46b75hPfkar9TUowrIGk4Y+ZWWX/cwNJCJazC1dfanP4uft0fzpYJKMSfJTAxswccu1g4yT+u0V5yq+eHbeGDJ+bF2MMhCPds7wGjJguxO0e4wx+HQdVGbU9jrHQ38oIYTChG92iKLRpciiyB8vrbNEBcNNi4hlw6U0sUdz6scpXceREdPFVA6wr0otY3wSZLfcIeKELoBQkR2KPNTTCROreVJ49tXwiQdGe7Ky0NDeNba2H5tKu3uLAtAiG/hVoKEAJG2VsbG8=

UdpSocket.recv_from fails with "end of file" but I can see the incoming package in Wireshark

Editor's note: This code example is from a version of Rust prior to 1.0 and is not valid Rust 1.0 code. The concepts discussed in the question are still valid.
I'm experimenting with torrent scraping using Rust. I can see the incoming package in Wireshark, but my recv_from calls always return Error("End of file"). Here's my program:
use std::io::net::ip::{Ipv4Addr, SocketAddr};
use std::io::net::udp::UdpSocket;
use std::rand;
use std::io::MemWriter;
fn main() {
let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 35000 };
let mut socket = match UdpSocket::bind(addr) {
Ok(s) => s,
Err(e) => panic!("couldn't bind socket: {}", e),
};
let mut buf: Vec<u8> = Vec::with_capacity(1000);
let transaction_id: u32 = rand::random();
let mut req_data = MemWriter::with_capacity(16);
req_data.write_be_u64(0x41727101980).unwrap(); // connection_id, identifies the protocol.
req_data.write_be_u32(0).unwrap(); // action: connect
req_data.write_be_u32(transaction_id).unwrap();
println!("{}", socket.send_to(req_data.get_ref(), ("31.172.63.252", 80)));
match socket.recv_from(buf.as_mut_slice()) {
Ok((amt, src)) => {
println!("Got {} bytes from {}.", amt, src);
},
Err(err) => println!("Can't recv_from: {}", err)
}
}
The output is always:
➜ udp-bug git:(master) ✗ cargo run
Compiling udp-bug v0.0.1 (file:///home/omer/rust/udp-bug)
Running `target/udp-bug`
Ok(())
Can't recv_from: end of file
However, I can see the expected response coming in Wireshark:
20235 3512.148636000 31.172.63.252 192.168.1.4 QUIC 60 CID: 0, Seq: 0
This package has a 16-byte payload, exactly what I expect. What's going wrong?
Editor's note: This code example is from a version of Rust prior to 1.0 and is not valid Rust 1.0 code. The concepts discussed in the answer are still valid.
I think your problem is that you're using Vec::with_capacity() as a mutable slice. Vec::with_capacity() only creates a vector with the specified capacity (naturally), but its length is zero. Consequently, the length of the slice taken from the vector will also be zero:
let v = Vec::with_capacity(128);
println!("{}", v.as_mut_slice().len()); // prints 0
Slices can't grow, so recv_from() has no space to write to and it fails with the error.
You have essentially two options here. First one is to use unsafe set_len() method:
let mut buf: Vec<u8> = Vec::with_capacity(1000);
unsafe { buf.set_len(1000); }
This way the buffer will have the correct length but its contents will likely be just garbage. This is not very important for this use case, however, as long as you only access the correct amount of bytes (using the information returned by recv_from()).
There is a better way, however. You can use stack-allocated fixed-size array:
let mut buf = [0u8, ..1000];
// ...
match socket.recv_from(buf.as_mut_slice()) {
// ...
}
Same thing goes for your req_data: you can use a statically sized array and a BufWriter:
let transaction_id: u32 = rand::random();
let mut req_data_buf = [0u8, ..16];
let mut req_data = BufWriter::new(req_data_buf);
req_data.write_be_u64(0x41727101980).unwrap(); // connection_id, identifies the protocol.
req_data.write_be_u32(0).unwrap(); // action: connect
req_data.write_be_u32(transaction_id).unwrap();
println!("{}", socket.send_to(req_data_buf, ("31.172.63.252", 80)));
This will only work with fixed-size buffers though. If you don't know the size of the buffer, you will still need a Vec.

Resources