/* Copyright (C) 2021 Open Information Security Foundation
 *
 * You can copy, redistribute or modify this Program under the terms of
 * the GNU General Public License version 2 as published by the Free
 * Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * version 2 along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

use super::bittorrent_dht::BitTorrentDHTTransaction;
use crate::jsonbuilder::{JsonBuilder, JsonError};

/// Format bytes as an IP address string.
fn print_ip_addr(addr: &[u8]) -> std::string::String {
    if addr.len() == 4 {
        return format!("{}.{}.{}.{}", addr[0], addr[1], addr[2], addr[3]);
    } else if addr.len() == 16 {
        return format!("{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}",
                       addr[0],
                       addr[1],
                       addr[2],
                       addr[3],
                       addr[4],
                       addr[5],
                       addr[6],
                       addr[7],
                       addr[8],
                       addr[9],
                       addr[10],
                       addr[11],
                       addr[12],
                       addr[13],
                       addr[14],
                       addr[15]);
    } else {
        return "".to_string();
    }
}

fn log_bittorrent_dht(
    tx: &BitTorrentDHTTransaction, js: &mut JsonBuilder,
) -> Result<(), JsonError> {
    js.set_hex("transaction_id", &tx.transaction_id)?;
    if let Some(client_version) = &tx.client_version {
        js.set_hex("client_version", client_version)?;
    }
    if let Some(request_type) = &tx.request_type {
        js.set_string("request_type", request_type)?;
    }
    if let Some(error) = &tx.error {
        js.open_object("error")?;
        js.set_uint("num", u64::from(error.num))?;
        js.set_string("msg", &error.msg)?;
        js.close()?;
    };
    if let Some(request) = &tx.request {
        js.open_object("request")?;
        js.set_hex("id", &request.id)?;
        if let Some(target) = &request.target {
            js.set_hex("target", target)?;
        }
        if let Some(info_hash) = &request.info_hash {
            js.set_hex("info_hash", info_hash)?;
        }
        if let Some(token) = &request.token {
            js.set_hex("token", token)?;
        }
        if let Some(implied_port) = request.implied_port {
            js.set_uint("implied_port", u64::from(implied_port))?;
        }
        if let Some(port) = request.port {
            js.set_uint("port", u64::from(port))?;
        }
        js.close()?;
    };
    if let Some(response) = &tx.response {
        js.open_object("response")?;
        js.set_hex("id", &response.id)?;
        if let Some(nodes) = &response.nodes {
            if !nodes.is_empty() {
                js.open_array("nodes")?;
                for node in nodes {
                    js.start_object()?;
                    js.set_hex("id", &node.id)?;
                    js.set_string("ip", &print_ip_addr(&node.ip))?;
                    js.set_uint("port", node.port.into())?;
                    js.close()?;
                }
                js.close()?;
            }
        }
        if let Some(nodes) = &response.nodes6 {
            if !nodes.is_empty() {
                js.open_array("nodes6")?;
                for node in nodes {
                    js.start_object()?;
                    js.set_hex("id", &node.id)?;
                    js.set_string("ip", &print_ip_addr(&node.ip))?;
                    js.set_uint("port", node.port.into())?;
                    js.close()?;
                }
                js.close()?;
            }
        }
        if let Some(values) = &response.values {
            js.open_array("values")?;
            for value in values {
                js.start_object()?;
                js.set_string("ip", &print_ip_addr(&value.ip))?;
                js.set_uint("port", value.port.into())?;
                js.close()?;
            }
            js.close()?;
        }
        if let Some(token) = &response.token {
            js.set_hex("token", token)?;
        }
        js.close()?;
    };
    Ok(())
}

#[no_mangle]
pub unsafe extern "C" fn rs_bittorrent_dht_logger_log(
    tx: *mut std::os::raw::c_void, js: &mut JsonBuilder,
) -> bool {
    let tx = cast_pointer!(tx, BitTorrentDHTTransaction);
    log_bittorrent_dht(tx, js).is_ok()
}
