// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "lib/network.h"

#include "babel_main.h"
#include "babeld.h"
#include "util.h"

int roughly(int value)
{
	if (value < 0)
		return -roughly(-value);
	else if (value <= 1)
		return value;
	else
		return value * 3 / 4 + frr_weak_random() % (value / 2);
}

/* d = s1 - s2 */
void timeval_minus(struct timeval *d, const struct timeval *s1, const struct timeval *s2)
{
	if (s1->tv_usec >= s2->tv_usec) {
		d->tv_usec = s1->tv_usec - s2->tv_usec;
		d->tv_sec = s1->tv_sec - s2->tv_sec;
	} else {
		d->tv_usec = s1->tv_usec + 1000000 - s2->tv_usec;
		d->tv_sec = s1->tv_sec - s2->tv_sec - 1;
	}
}

uint32_t timeval_minus_msec(const struct timeval *s1, const struct timeval *s2)
{
	if (s1->tv_sec < s2->tv_sec)
		return 0;

	/* Avoid overflow. */
	if (s1->tv_sec - s2->tv_sec > 2000000)
		return 2000000000;

	if (s1->tv_sec > s2->tv_sec)
		return (unsigned)((unsigned)(s1->tv_sec - s2->tv_sec) * 1000 +
				  ((int)s1->tv_usec - s2->tv_usec) / 1000);

	if (s1->tv_usec <= s2->tv_usec)
		return 0;

	return (unsigned)(s1->tv_usec - s2->tv_usec) / 1000u;
}

/* d = s + msecs */
void timeval_add_msec(struct timeval *d, const struct timeval *s, int msecs)
{
	int usecs;
	d->tv_sec = s->tv_sec + msecs / 1000;
	usecs = s->tv_usec + (msecs % 1000) * 1000;
	if (usecs < 1000000) {
		d->tv_usec = usecs;
	} else {
		d->tv_usec = usecs - 1000000;
		d->tv_sec++;
	}
}

void set_timeout(struct timeval *timeout, int msecs)
{
	timeval_add_msec(timeout, &babel_now, roughly(msecs));
}

/* returns <0 if "s1" < "s2", etc. */
int timeval_compare(const struct timeval *s1, const struct timeval *s2)
{
	if (s1->tv_sec < s2->tv_sec)
		return -1;
	else if (s1->tv_sec > s2->tv_sec)
		return 1;
	else if (s1->tv_usec < s2->tv_usec)
		return -1;
	else if (s1->tv_usec > s2->tv_usec)
		return 1;
	else
		return 0;
}

/* set d at min(d, s) */
/* {0, 0} represents infinity */
void timeval_min(struct timeval *d, const struct timeval *s)
{
	if (s->tv_sec == 0)
		return;

	if (d->tv_sec == 0 || timeval_compare(d, s) > 0) {
		*d = *s;
	}
}

/* set d to min(d, x) with x in [secs, secs+1] */
void timeval_min_sec(struct timeval *d, time_t secs)
{
	if (d->tv_sec == 0 || d->tv_sec > secs) {
		d->tv_sec = secs;
		d->tv_usec = frr_weak_random() % 1000000;
	}
}

/* parse a float value in second and return the corresponding mili-seconds.
 For example:
 parse_msec("12.342345") returns 12342 */
int parse_msec(const char *string)
{
	unsigned int in, fl;
	int i, j;

	in = fl = 0;
	i = 0;
	while (string[i] == ' ' || string[i] == '\t')
		i++;
	while (string[i] >= '0' && string[i] <= '9') {
		in = in * 10 + string[i] - '0';
		i++;
	}
	if (string[i] == '.') {
		i++;
		j = 0;
		while (string[i] >= '0' && string[i] <= '9') {
			fl = fl * 10 + string[i] - '0';
			i++;
			j++;
		}

		while (j > 3) {
			fl /= 10;
			j--;
		}
		while (j < 3) {
			fl *= 10;
			j++;
		}
	}

	while (string[i] == ' ' || string[i] == '\t')
		i++;

	if (string[i] == '\0')
		return in * 1000 + fl;

	return -1;
}

/* There's no good name for a positive int in C, call it nat. */
int parse_nat(const char *string)
{
	long l;
	char *end;

	l = strtol(string, &end, 0);

	while (*end == ' ' || *end == '\t')
		end++;
	if (*end != '\0')
		return -1;

	if (l < 0 || l > INT_MAX)
		return -1;

	return (int)l;
}

unsigned char *mask_prefix(unsigned char *restrict ret, const unsigned char *restrict prefix,
			   unsigned char plen)
{
	if (plen >= IPV6_MAX_BITLEN) {
		memcpy(ret, prefix, IPV6_MAX_BYTELEN);
		return ret;
	}

	memset(ret, 0, 16);
	memcpy(ret, prefix, plen / 8);
	if (plen % 8 != 0)
		ret[plen / 8] = CHECK_FLAG(prefix[plen / 8],
					   CHECK_FLAG((0xFF << (8 - (plen % 8))), 0xFF));
	return ret;
}

const unsigned char v4prefix[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };

static const unsigned char llprefix[16] = { 0xFE, 0x80 };

const char *format_address(const unsigned char *address)
{
	static char buf[4][INET6_ADDRSTRLEN];
	static int i = 0;
	i = (i + 1) % 4;
	if (v4mapped(address))
		inet_ntop(AF_INET, address + 12, buf[i], INET6_ADDRSTRLEN);
	else
		inet_ntop(AF_INET6, address, buf[i], INET6_ADDRSTRLEN);
	return buf[i];
}

const char *format_prefix(const unsigned char *prefix, unsigned char plen)
{
	static char buf[4][INET6_ADDRSTRLEN + 4];
	static int i = 0;
	int n;
	i = (i + 1) % 4;
	if (plen >= 96 && v4mapped(prefix)) {
		inet_ntop(AF_INET, prefix + 12, buf[i], INET6_ADDRSTRLEN);
		n = strlen(buf[i]);
		snprintf(buf[i] + n, INET6_ADDRSTRLEN + 4 - n, "/%d", plen - 96);
	} else {
		inet_ntop(AF_INET6, prefix, buf[i], INET6_ADDRSTRLEN);
		n = strlen(buf[i]);
		snprintf(buf[i] + n, INET6_ADDRSTRLEN + 4 - n, "/%d", plen);
	}
	return buf[i];
}

const char *format_eui64(const unsigned char *eui)
{
	static char buf[4][28];
	static int i = 0;
	i = (i + 1) % 4;
	snprintf(buf[i], 28, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", eui[0], eui[1], eui[2],
		 eui[3], eui[4], eui[5], eui[6], eui[7]);
	return buf[i];
}

const char *format_thousands(unsigned int value)
{
	static char buf[4][15];
	static int i = 0;
	i = (i + 1) % 4;
	snprintf(buf[i], 15, "%u.%.3u", value / 1000, value % 1000);
	return buf[i];
}

int parse_address(const char *address, unsigned char *addr_r, int *af_r)
{
	struct in_addr ina;
	struct in6_addr ina6;
	int rc;

	rc = inet_pton(AF_INET, address, &ina);
	if (rc > 0) {
		v4tov6(addr_r, (const unsigned char *)&ina);
		if (af_r)
			*af_r = AF_INET;
		return 0;
	}

	rc = inet_pton(AF_INET6, address, &ina6);
	if (rc > 0) {
		memcpy(addr_r, &ina6, IPV6_MAX_BYTELEN);
		if (af_r)
			*af_r = AF_INET6;
		return 0;
	}

	return -1;
}

int parse_eui64(const char *eui, unsigned char *eui_r)
{
	int n;
	n = sscanf(eui, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &eui_r[0],
		   &eui_r[1], &eui_r[2], &eui_r[3], &eui_r[4], &eui_r[5], &eui_r[6], &eui_r[7]);
	if (n == 8)
		return 0;

	n = sscanf(eui, "%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx", &eui_r[0],
		   &eui_r[1], &eui_r[2], &eui_r[3], &eui_r[4], &eui_r[5], &eui_r[6], &eui_r[7]);
	if (n == 8)
		return 0;

	n = sscanf(eui, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &eui_r[0], &eui_r[1],
		   &eui_r[2], &eui_r[5], &eui_r[6], &eui_r[7]);
	if (n == 6) {
		eui_r[3] = 0xFF;
		eui_r[4] = 0xFE;
		return 0;
	}
	return -1;
}

int wait_for_fd(int direction, int fd, int msecs)
{
	fd_set fds;
	int rc;
	struct timeval tv;

	tv.tv_sec = msecs / 1000;
	tv.tv_usec = (msecs % 1000) * 1000;

	FD_ZERO(&fds);
	FD_SET(fd, &fds);
	if (direction)
		rc = select(fd + 1, NULL, &fds, NULL, &tv);
	else
		rc = select(fd + 1, &fds, NULL, NULL, &tv);

	return rc;
}

int martian_prefix(const unsigned char *prefix, int plen)
{
	return (plen >= 8 && prefix[0] == 0xFF) ||
	       (plen >= 10 && prefix[0] == 0xFE && (CHECK_FLAG(prefix[1], 0xC0) == 0x80)) ||
	       (plen >= 128 && memcmp(prefix, zeroes, 15) == 0 &&
		(prefix[15] == 0 || prefix[15] == 1)) ||
	       (plen >= 96 && v4mapped(prefix) &&
		((plen >= 104 && (prefix[12] == 127 || prefix[12] == 0)) ||
		 (plen >= 100 && CHECK_FLAG(prefix[12], 0xE0) == 0xE0)));
}

int linklocal(const unsigned char *address)
{
	return memcmp(address, llprefix, 8) == 0;
}

int v4mapped(const unsigned char *address)
{
	return memcmp(address, v4prefix, 12) == 0;
}

void v4tov6(unsigned char *dst, const unsigned char *src)
{
	memcpy(dst, v4prefix, 12);
	memcpy(dst + 12, src, 4);
}

void inaddr_to_uchar(unsigned char *dest, const struct in_addr *src)
{
	v4tov6(dest, (const unsigned char *)src);
	assert(v4mapped(dest));
}

void uchar_to_inaddr(struct in_addr *dest, const unsigned char *src)
{
	assert(v4mapped(src));
	memcpy(dest, src + 12, 4);
}

void in6addr_to_uchar(unsigned char *dest, const struct in6_addr *src)
{
	memcpy(dest, src, IPV6_MAX_BYTELEN);
}

void uchar_to_in6addr(struct in6_addr *dest, const unsigned char *src)
{
	memcpy(dest, src, IPV6_MAX_BYTELEN);
}

int daemonise(void)
{
	int rc;

	fflush(stdout);
	fflush(stderr);

	rc = fork();
	if (rc < 0)
		return -1;

	if (rc > 0)
		exit(0);

	rc = setsid();
	if (rc < 0)
		return -1;

	return 1;
}
