SS_Rebelious
SS_Rebelious

Reputation: 1354

Rust: how to convert SocketAddr to IpNetwork?

Using Diesel and Rocket I want to be able to log IPs that requests are coming from. Here is the similar discussion, but without conclusion. Diesel maps Postgres inet type to IpNetwork, but Rocket returns SocketAddr from the request data. How to convert SocketAddr to IpNetwork?

models.rs:

#![allow(proc_macro_derive_resolution_fallback)]
use crate::schema::bs_date_forecast;

use chrono::{NaiveDate, NaiveDateTime};
use ipnetwork::IpNetwork;
use diesel::prelude::*;

#[derive(Queryable, Insertable, Serialize, Deserialize)]
#[table_name="bs_date_forecast"]
pub struct BsDateForecast {
    pub id: Option<i32>,
    pub predicted_date: NaiveDate,
    pub seer_ip: IpNetwork,
    pub created_timestamp: NaiveDateTime,  // Defined automatically at the database level
}

impl BsDateForecast {
    pub fn create(forecast: BsDateForecast,
                  conn: &PgConnection) -> Vec<BsDateForecast> {
        diesel::insert_into(bs_date_forecast::table)
            .values(&forecast)
            .get_results(conn)
            .expect("Error creating record")
    }
}

routes.rs:

#[post("/predictions", format = "application/json", data = "<prediction>")]
pub fn create(prediction: Json<BsDateForecast>,
              conn: DbConn,
              user_ip: SocketAddr) -> Json<Vec<BsDateForecast>> {
    let insert = BsDateForecast{
        id: None,
        seer_ip: user_ip.to_string(),  // How to get IpNetwork here???
        ..prediction.into_inner()
    };
    Json(BsDateForecast::create(insert, &conn))
}

Upvotes: 0

Views: 641

Answers (1)

Sebastian Redl
Sebastian Redl

Reputation: 71899

The INET type can store a subnet (including subnet mask) or a single host. The IpNetwork type is meant to store a subnet, but a single host can be stored the Postgres way, by specifying a full-length prefix.

A SocketAddr is an IpAddr plus a port number.

So the conversion is: get the IP part of the SocketAddr, create an IpNetwork from that by specifying the correct prefix.

The complication here is that the prefix is different for IP4 and IP6.

fn socket_addr_to_ip_network(socket_addr: &SocketAddr) -> IpNetwork {
  let ip = socket_addr.ip();
  IpNetwork::new(ip, single_host_prefix(ip))
    .expect("single_host_prefix created invalid prefix")
}
fn single_host_prefix(ip_addr: &IpAddr) -> u8 {
  match ip_addr { &IpAddr::V4(_) => 32, &IpAddr::V6(_) => 128 }
}

Upvotes: 2

Related Questions