/*
 * netconfig_net.c:
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: netconfig_net.c,v 1.1.1.1 2005/06/27 13:37:56 cm Exp $
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if_arp.h>
#include <net/if.h>
#include "netconfig_net.h"

// --- cNetconfigNetDevice -----------------------------------------------------

cNetconfigNetDevice::cNetconfigNetDevice() {
}

cNetconfigNetDevice::~cNetconfigNetDevice() {
}

bool cNetconfigNetDevice::TestFlag(const char *device, short flag) {
  bool result = false;
  int sockfd;
  struct ifreq ifr;

  sockfd = socket(PF_INET, SOCK_DGRAM, 0);

  if (-1 != sockfd) {
    memset(&ifr, 0x00, sizeof(struct ifreq));
    strncpy(ifr.ifr_name, device, IFNAMSIZ);

    if (-1 != ioctl(sockfd, SIOCGIFFLAGS, &ifr))
      result = ifr.ifr_flags & flag;

    close(sockfd);
  }

  return result;
}

bool cNetconfigNetDevice::isUp(const char *device) {
  return TestFlag(device, IFF_UP);
}

bool cNetconfigNetDevice::isLoopback(const char *device) {
  return TestFlag(device, IFF_LOOPBACK);
}

bool cNetconfigNetDevice::isMulticast(const char *device) {
  return TestFlag(device, IFF_MULTICAST);
}

bool cNetconfigNetDevice::isPointToPoint(const char *device) {
  return TestFlag(device, IFF_POINTOPOINT);
}

bool cNetconfigNetDevice::isRunning(const char *device) {
  return TestFlag(device, IFF_RUNNING);
}

bool cNetconfigNetDevice::isPromiscuous(const char *device) {
  return TestFlag(device, IFF_PROMISC);
}

bool cNetconfigNetDevice::isBroadcast(const char *device) {
  return TestFlag(device, IFF_BROADCAST);
}

bool cNetconfigNetDevice::SetState(const char *device, bool up) {
  bool result = false;
  int sockfd = -1;
  struct ifreq ifr;

  sockfd = socket(PF_INET, SOCK_DGRAM, 0);

  if (-1 != sockfd) {
    memset(&ifr, 0x00, sizeof(struct ifreq));
    strncpy(ifr.ifr_name, device, IFNAMSIZ);

    if (-1 != ioctl(sockfd, SIOCGIFFLAGS, &ifr)) {
      if (up)
        ifr.ifr_flags |= IFF_UP;
      else
        ifr.ifr_flags &= ~IFF_UP;

      if (-1 != ioctl(sockfd, SIOCSIFFLAGS, &ifr))
        result = true;
    }
    close(sockfd);
  }

  return result;
}

const struct sockaddr *cNetconfigNetDevice::GetSockAddr(const char *device, eSockAddr type) {
  int sockfd = -1;
  static struct sockaddr saddr;
  struct ifreq ifr;

  memset(&saddr, 0x00, sizeof(struct sockaddr));
  sockfd = socket(PF_INET, SOCK_DGRAM, 0);

  if (-1 != sockfd) {
    memset(&ifr, 0x00, sizeof(struct ifreq));
    strncpy(ifr.ifr_name, device, IFNAMSIZ);

    switch (type) {
      case saAddress:
        if (-1 != ioctl(sockfd, SIOCGIFADDR, &ifr))
          if (AF_INET == ifr.ifr_addr.sa_family)
            memcpy(&saddr, &ifr.ifr_addr, sizeof(struct sockaddr));
        break;
      case saNetmask:
        if (-1 != ioctl(sockfd, SIOCGIFNETMASK, &ifr))
          if (AF_INET == ifr.ifr_addr.sa_family)
            memcpy(&saddr, &ifr.ifr_netmask, sizeof(struct sockaddr));
        break;
      case saBroadcast:
        if (-1 != ioctl(sockfd, SIOCGIFBRDADDR, &ifr))
          if (AF_INET == ifr.ifr_broadaddr.sa_family)
            memcpy(&saddr, &ifr.ifr_broadaddr, sizeof(struct sockaddr));
        break;
      case saHwAddr:
        if (-1 != ioctl(sockfd, SIOCGIFHWADDR, &ifr))
          if (AF_LOCAL == ifr.ifr_hwaddr.sa_family)
            memcpy(&saddr, &ifr.ifr_hwaddr, sizeof(struct sockaddr));
        break;
      default:
        break;
    }
    close(sockfd);
  }
  return &saddr;
}

const struct sockaddr *cNetconfigNetDevice::SetSockAddr(const char *device, eSockAddr type, struct sockaddr *addr) {
  int sockfd = -1;
  struct ifreq ifr;

  sockfd = socket(PF_INET, SOCK_DGRAM, 0);

  if (-1 != sockfd) {
    memset(&ifr, 0x00, sizeof(struct ifreq));
    strncpy(ifr.ifr_name, device, IFNAMSIZ);

    switch (type) {
      case saAddress:
        memcpy(&ifr.ifr_addr, addr, sizeof(struct sockaddr));
        if (-1 != ioctl(sockfd, SIOCSIFADDR, &ifr))
          if (AF_INET == ifr.ifr_addr.sa_family)
            memcpy(addr, &ifr.ifr_addr, sizeof(struct sockaddr));
        else
          memset(addr, 0x00, sizeof(struct sockaddr));
        break;
      case saNetmask:
        memcpy(&ifr.ifr_netmask, addr, sizeof(struct sockaddr));
        if (-1 != ioctl(sockfd, SIOCSIFNETMASK, &ifr))
          if (AF_INET == ifr.ifr_netmask.sa_family)
            memcpy(addr, &ifr.ifr_netmask, sizeof(struct sockaddr));
        else
          memset(addr, 0x00, sizeof(struct sockaddr));
        break;
      case saBroadcast:
        memcpy(&ifr.ifr_broadaddr, addr, sizeof(struct sockaddr));
        if (-1 != ioctl(sockfd, SIOCSIFBRDADDR, &ifr))
          if (AF_INET == ifr.ifr_broadaddr.sa_family)
            memcpy(addr, &ifr.ifr_broadaddr, sizeof(struct sockaddr));
        else
          memset(addr, 0x00, sizeof(struct sockaddr));
        break;
      default:
        break;
    }
    close(sockfd);
  }
  return addr;
}

unsigned long cNetconfigNetDevice::GetAddr(const char *device) {
  struct sockaddr_in *saddr = (struct sockaddr_in *)GetSockAddr(device, saAddress);
  return saddr->sin_addr.s_addr;
}

unsigned long cNetconfigNetDevice::SetAddr(const char *device, unsigned long addr) {
  struct sockaddr_in saddr;

  memset(&saddr, 0x00, sizeof(struct sockaddr_in));
  saddr.sin_family = AF_INET;
  saddr.sin_addr.s_addr = addr;

  SetSockAddr(device, saAddress, (struct sockaddr *)&saddr);

  return saddr.sin_addr.s_addr;
}

unsigned long cNetconfigNetDevice::GetMask(const char *device) {
  struct sockaddr_in *saddr = (struct sockaddr_in *)GetSockAddr(device, saNetmask);
  return saddr->sin_addr.s_addr;
}

unsigned long cNetconfigNetDevice::SetMask(const char *device, unsigned long mask) {
  struct sockaddr_in saddr;

  memset(&saddr, 0x00, sizeof(struct sockaddr_in));
  saddr.sin_family = AF_INET;
  saddr.sin_addr.s_addr = mask;

  SetSockAddr(device, saNetmask, (struct sockaddr *)&saddr);

  return saddr.sin_addr.s_addr;
}

unsigned long cNetconfigNetDevice::GetBcast(const char *device) {
  struct sockaddr_in *saddr = (struct sockaddr_in *)GetSockAddr(device, saBroadcast);
  return saddr->sin_addr.s_addr;
}

unsigned long cNetconfigNetDevice::SetBcast(const char *device, unsigned long bcast) {
  struct sockaddr_in saddr;

  memset(&saddr, 0x00, sizeof(struct sockaddr_in));
  saddr.sin_family = AF_INET;
  saddr.sin_addr.s_addr = bcast;

  SetSockAddr(device, saBroadcast, (struct sockaddr *)&saddr);

  return saddr.sin_addr.s_addr;
}

struct sockaddr *cNetconfigNetDevice::GetHwAddr(const char *device) {
  const struct sockaddr *p_saddr = GetSockAddr(device, saHwAddr);
  struct sockaddr *p_raddr = NULL;

  p_raddr = (struct sockaddr *)malloc(sizeof(struct sockaddr));

  if (NULL != p_raddr)
    memcpy(p_raddr, p_saddr, sizeof(struct sockaddr));

  return p_raddr;
}

int cNetconfigNetDevice::GetIValue(const char *device, eIntValue type) {
  int sockfd = -1, ivalue = 0;
  struct ifreq ifr;

  sockfd = socket(PF_INET, SOCK_DGRAM, 0);

  if (-1 != sockfd) {
    memset(&ifr, 0x00, sizeof(struct ifreq));
    strncpy(ifr.ifr_name, device, IFNAMSIZ);

    switch (type) {
      case ivMetric:
        if (-1 != ioctl(sockfd, SIOCGIFMETRIC, &ifr))
          ivalue = ifr.ifr_metric;
        break;
      case ivMtu:
        if (-1 != ioctl(sockfd, SIOCGIFMTU, &ifr))
          ivalue = ifr.ifr_mtu;
        break;
      default:
        break;
    }
    close(sockfd);
  }
  return ivalue;
}

int cNetconfigNetDevice::GetMtu(const char *device) {
  return GetIValue(device, ivMtu);
}

int cNetconfigNetDevice::GetMetric(const char *device) {
  return GetIValue(device, ivMetric);
}
