Building a gRPC Application in Rust and Analyze • n3tw0rth skip to content
n3tw0rth a.k.a 0xbyt3z

Building a gRPC Application in Rust and Analyze

/ 4 min read

gRPC (gRPC Remote Procedure Calls) is a modern, high-performance RPC framework that can run in any environment. It uses HTTP/2 for transport, Protocol Buffers as the interface description language, and provides features such as authentication, load balancing, and more. In this blog post, we’ll walk through building a simple gRPC application in Rust and then capture and analyze the network traffic using tcpdump.

Prerequisites

Before we begin, make sure you have the following installed:

  • Rust: Install Rust from rustup.rs.
  • Protocol Buffers Compiler (protoc): Install protoc from here.
  • tcpdump: Install tcpdump using your package manager (e.g., apt-get install tcpdump on Ubuntu).

Step 1: Setting Up the Rust Project

First, let’s create a new Rust project:

cargo new grpc-example
cd grpc-example

Next, add the necessary dependencies to your Cargo.toml file:

[package]
name = "grpc-example"
version = "0.1.0"
edition = "2021"
[dependencies]
tonic = "0.7"
prost = "0.11"
tokio = { version = "1", features = ["full"] }
[build-dependencies]
tonic-build = "0.7"

Here, we’re using tonic, a gRPC implementation for Rust, and prost for Protocol Buffers.

Step 2: Defining the gRPC Service

Create a proto directory in your project root and add a hello.proto file:

syntax = "proto3";
package hello;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}

This defines a simple gRPC service with a single SayHello method.

Step 3: Generating Rust Code from Protobuf

Next, we’ll generate Rust code from the .proto file. Create a build.rs file in the root of your project:

fn main() {
tonic_build::compile_protos("proto/hello.proto").unwrap();
}

This will generate the necessary Rust code when you build your project.

Step 4: Implementing the gRPC Server

Now, let’s implement the gRPC server. Modify src/main.rs as follows:

use tonic::{transport::Server, Request, Response, Status};
use hello::greeter_server::{Greeter, GreeterServer};
use hello::{HelloRequest, HelloReply};
pub mod hello {
tonic::include_proto!("hello");
}
#[derive(Debug, Default)]
pub struct MyGreeter {}
#[tonic::async_trait]
impl Greeter for MyGreeter {
async fn say_hello(
&self,
request: Request<HelloRequest>,
) -> Result<Response<HelloReply>, Status> {
let name = request.into_inner().name;
let reply = HelloReply {
message: format!("Hello, {}!", name),
};
Ok(Response::new(reply))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "[::1]:50051".parse()?;
let greeter = MyGreeter::default();
println!("GreeterServer listening on {}", addr);
Server::builder()
.add_service(GreeterServer::new(greeter))
.serve(addr)
.await?;
Ok(())
}

This code sets up a gRPC server that listens on localhost:50051 and responds to SayHello requests.

Step 5: Running the Server

Run the server using:

Terminal window
cargo run

The server should now be running and listening for incoming gRPC requests.

Step 6: Capturing Packets with tcpdump

To capture the network traffic, open a new terminal and run tcpdump:

Terminal window
tcpdump -i lo -w grpc_traffic.pcap port 50051

This command captures all traffic on the loopback interface (lo) on port 50051 and writes it to a file called grpc_traffic.pcap.

Step 7: Making a gRPC Request

In another terminal, let’s make a gRPC request to our server. You can use a tool like grpcurl:

Terminal window
grpcurl -proto ./hello.proto -plaintext -d '{"name": "World"}' localhost:50051 hello.Greeter/SayHello

Note: if you add reflections, you do not have to provide the proto file, checkout the grpcurl for more details You should see a response like.

{
"message": "Hello, World!"
}

Step 8: Analyzing the Captured Packets

Stop the tcpdump process by pressing Ctrl+C. Now, let’s analyze the captured packets using Wireshark or tcpdump itself.

To analyze with tcpdump:

Terminal window
tcpdump -r grpc_traffic.pcap

You should see the HTTP/2 frames, including the gRPC request and response. Look for frames that contain the SayHello method and the corresponding response.

Alternatively, open grpc_traffic.pcap in Wireshark for a more detailed analysis. You can filter for http2 to see only HTTP/2 traffic.

https://protobuf.dev/programming-guides/encoding/

tcpdump -qns 0 -X -r grpc_traffic.pcap

Conclusion

In this blog post, we built a simple gRPC server in Rust, captured the network traffic using tcpdump, and analyzed the packets. gRPC is a powerful tool for building efficient, type-safe APIs, and Rust’s ecosystem makes it easy to get started. By understanding the underlying network traffic, you can gain deeper insights into how gRPC works and how to troubleshoot potential issues.

Feel free to expand on this example by adding more services, implementing client-side code, or exploring advanced features like streaming and authentication. Happy coding!

https://protobuf.dev/programming-guides/proto3/

21:52:33.263066 IP6 ::1.55746 > ::1.50051: tcp 130
6002 967e 00a2 0640 0000 0000 0000 0000 `..~...@........
0000 0000 0000 0001 0000 0000 0000 0000 ................
0000 0000 0000 0001 d9c2 c383 25c2 a109 ............%...
6cf1 19cf 8018 0200 00aa 0000 0101 080a l...............
37a4 b2bc 37a4 b2bc 0000 6401 0400 0000 7...7.....d.....
0183 8645 9162 72d1 41d7 c561 4a92 d8c6 ...E.br.A..aJ...
e1fa c65a 283f 418b a0e4 1d13 9d09 b8d8 ...Z(?A.........
00d8 7f5f 8b1d 75d0 620d 263d 4c4d 6564 ..._..u.b.&=LMed
7a94 9aca c96d 9430 15df 5c4a 4d65 645a z....m.0..\JMedZ
63b0 15dc 0ae0 4002 7465 864d 8335 05b1 c.....@.te.M.5..
1f40 8e9a cac8 b0c8 42d6 958b 510f 21aa .@......B...Q.!.
9b83 9bd9 ab00 000c 0001 0000 0001 0000 ................
0000 070a 0557 6f72 6c64 .....World