mip / yip

mip and yip are two tools written in Rust to get the current IP address based on a API call. yip is a server, and mip is a library.

mip

Get IPv4 address in rust – with 0 dependencies!

Since Rust has such a powerful standard library I like to build tools that do not rely on any other libraries. mip is one such tool, as it only uses Rust standard functions to parse IPv4 addresses from a text-based response, such as the one given when opening https://httpbin.org/ip

The core of it all is a function called parse_ip which can be found on GitHub here.

Since the code works with raw HTTP requests it needs to find the body first. The body and headers are separated by two newlines. To get the body it does the following:

let header_body: Vec<&str> = input.split("\r\n\r\n").collect();
let ip_parts: Vec<&str> = header_body[header_body.len() - 1]
// more code below

The first line splits the raw HTML string (called input here) by the two new lines (\r\n\r\n), then it splits it again to grab the body part.

After that some clean-up is performed. The code tries to figure out the different IP address parts.

let ip_parts: Vec<&str> = header_body[header_body.len() - 1]
        .split(|c| {
            c == ' '
                || c == '.' // split . (divider between IP parts)
                || c == ',' // split , (divider between multi IPs)
                || c == ':' // split : (e.g. "your ip:")
                || c == '<' // split XML/HTML opening tags 
                || c == '>' // split XML/HTML closing tags
                || c == '{' // split JSON opening tags 
                || c == '}' // split JSON closing tags
                || c == '\n' // split new lines
                || c == '\r' // split new lines
        })
        .collect();

With all the parts extracted it’s time to loop

let mut ip_vec: Vec<i32> = vec![];

for p in ip_parts {
  // Skip empty parts
  if p == "" {
    continue;
  }
    
  // Some clean up. 
  // It's just enough for working with httpbin.org
  let mut p_clean = str::replace(p, "\"", "");
  p_clean = str::replace(p_clean.as_str(), ",", "");

  let mut r = i32::from_str(p_clean.as_str());

  match r {
    Ok(num) => r = Ok(num),
    Err(_e) => r = Ok(256),
  }

  let n = r.unwrap();

  if n < 256 {
    ip_vec.push(n);
  }
}

And after the clean-up loop it is time to return the IPv4 address.

return Ipv4Addr::from_str(format!("{}.{}.{}.{}", ip_vec[0], ip_vec[1], ip_vec[2], ip_vec[3]).as_str(),);

And that’s it. The last line returns the numbers as a Rust IPv4 type.

yip

Tiniest server which responds with requester IPv4 address

yip is a HTTP server that responds with the callers’ IPv4 address. That’s it.

Here’s all the code.

use std::net::{TcpListener, TcpStream, Shutdown};
use std::io::{Result, Write};
use std::env;

fn handle_client(mut stream: TcpStream) {
    let addr = stream.peer_addr().unwrap().to_string();
    let ip: Vec<&str> = addr.split(":").collect();
    let _r = stream.write_fmt(format_args!("{}", ip[0]));
    stream.shutdown(Shutdown::Both).expect("Failed to shutdown stream");
}

fn main() -> Result<()> {
    // Read port from env
    let port = env::var("PORT").unwrap_or_default().parse().unwrap_or(8111);
    // Create TCP listener
    let listener = TcpListener::bind(format!("0.0.0.0:{}", port))?;

    // accept connections and process them serially
    for stream in listener.incoming() {
        handle_client(stream?);
    }
    Ok(())
}

It’s creating a tcp listener and listens for incoming requests, then gets the IP of the caller and returns that as a HTTP response.

Links

mip

Sourcehttps://github.com/KevinGimbel/mip
Docs.rshttps://docs.rs/crate/mip/latest

yip

Sourcehttps://github.com/KevinGimbel/yip
Docs.rshttps://docs.rs/crate/yip/latest