/* Copyright 2022-2026 Kurt Nienhaus
 *
 * This file is part of VgaGames.
 * VgaGames is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 * VgaGames 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
 * along with VgaGames.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include "nw.h"

VG_BOOL nw_addr_equal(int, struct sockaddr *, struct sockaddr *, socklen_t);
void nw_conncl_set(unsigned char *, int, int);
VG_BOOL nw_conncl_get(unsigned char *, int);
void nw_free_nwdata_zdata(struct nwdata_zdata *);
void nw_free_nwdata_xdata(struct nwdata_xdata *);
int nw_zheader_set(const struct nwdata_zdata *, char *);
int nw_zheader_get(struct nwdata_zdata *, const char *);
int nw_xheader_set(const struct nwdata_xdata *, char *);
int nw_xheader_get(struct nwdata_xdata *, const char *);


/* return whether both addresses are equal */
VG_BOOL
nw_addr_equal(int ipv, struct sockaddr *sad1, struct sockaddr *sad2, socklen_t salen)
{
  if (sad1 == NULL || sad2 == NULL) { return VG_FALSE; }

#ifdef AF_INET6
  if (ipv == 6) {
    struct sockaddr_in6 * si1 = (struct sockaddr_in6 *)sad1;
    struct sockaddr_in6 * si2 = (struct sockaddr_in6 *)sad2;
    if (salen >= (socklen_t)sizeof(si1->sin6_addr)
        && memcmp(&si1->sin6_addr, &si2->sin6_addr, sizeof(si1->sin6_addr)) == 0
        && memcmp(&si1->sin6_port, &si2->sin6_port, sizeof(si1->sin6_port)) == 0
       ) {
      return VG_TRUE;
    }
  }
#endif

  if (ipv == 4) {
    struct sockaddr_in * si1 = (struct sockaddr_in *)sad1;
    struct sockaddr_in * si2 = (struct sockaddr_in *)sad2;
    if (salen >= (socklen_t)sizeof(si1->sin_addr)
        && memcmp(&si1->sin_addr, &si2->sin_addr, sizeof(si1->sin_addr)) == 0
        && memcmp(&si1->sin_port, &si2->sin_port, sizeof(si1->sin_port)) == 0
       ) {
      return VG_TRUE;
    }
  }

  return VG_FALSE;
} /* Ende nw_addr_equal */


/* set for a client-number whether connected */
void
nw_conncl_set(unsigned char *memptr, int clnr, int val)
{
  int iconn;

  if (memptr == NULL) { return; }
  if (clnr < 1 || clnr > MAX_CLIENTS) { return; }

  iconn = NW_CLNR2ICONN(clnr);
  if (val) {
    *memptr |= (1 << iconn);
  } else {
    *memptr &= ~(1 << iconn);
  }
} /* Ende nw_conncl_set */


/* return for a client-number whether connected */
VG_BOOL
nw_conncl_get(unsigned char *memptr, int clnr)
{
  int iconn;

  if (memptr == NULL) { return VG_FALSE; }
  if (clnr < 1 || clnr > MAX_CLIENTS) { return VG_FALSE; }

  iconn = NW_CLNR2ICONN(clnr);
  return (VG_BOOL)!!(*memptr & (1 << iconn));
} /* Ende nw_conncl_get */


/* free additional-data list */
void
nw_free_nwdata_zdata(struct nwdata_zdata *zdata)
{
  struct nwdata_zdata *znext;

  for (; zdata != NULL; zdata = znext) {
    znext = zdata->next;
    if (zdata->data != NULL && zdata->size > 0) { free(zdata->data); }
    free(zdata);
  }
} /* Ende nw_free_nwdata_zdata */


/* free exchange-data list */
void
nw_free_nwdata_xdata(struct nwdata_xdata *xdata)
{
  struct nwdata_xdata *xnext;

  for (; xdata != NULL; xdata = xnext) {
    xnext = xdata->next;
    if (xdata->data != NULL && xdata->size > 0) { free(xdata->data); }
    free(xdata);
  }
} /* Ende nw_free_nwdata_xdata */


/* set header for additional-data, return number of bytes */
int
nw_zheader_set(const struct nwdata_zdata *zdata, char *data)
{
  if (zdata == NULL || data == NULL) { return 0; }

  /* size */
  USHORT_GET(zdata->size, data[0], data[1]);

  /* client-number */
  data[2] = zdata->clnr;

  return 3;
} /* Ende nw_zheader_set */


/* read header for additional-data, return number of bytes */
int
nw_zheader_get(struct nwdata_zdata *zdata, const char *data)
{
  if (zdata == NULL || data == NULL) { return 0; }

  /* size */
  USHORT_SET(zdata->size, data[0], data[1]);
  if (zdata->size > 0) {
    zdata->data = SML3_malloc(zdata->size);
  }

  /* client-number */
  zdata->clnr = data[2];

  return 3;
} /* Ende nw_zheader_get */


/* set header for exchange-data, return number of bytes */
int
nw_xheader_set(const struct nwdata_xdata *xdata, char *data)
{
  if (xdata == NULL || data == NULL) { return 0; }

  /* size */
  U3BYTE_GET(xdata->size, data[0], data[1], data[2]);

  /* client-number */
  data[3] = xdata->clnr;

  /* tag */
  data[4] = xdata->tag;

  /* conncl */
  data[5] = xdata->conncl;

  return 6;
} /* Ende nw_xheader_set */


/* read header for exchange-data, return number of bytes */
int
nw_xheader_get(struct nwdata_xdata *xdata, const char *data)
{
  if (xdata == NULL || data == NULL) { return 0; }

  /* size */
  U3BYTE_SET(xdata->size, data[0], data[1], data[2]);
  if (xdata->size > 0) {
    xdata->data = SML3_malloc(xdata->size);
  }

  /* client-number */
  xdata->clnr = data[3];

  /* tag */
  xdata->tag = data[4];

  /* conncl */
  xdata->conncl = data[5];

  return 6;
} /* Ende nw_xheader_get */
