/* This file is part of Lcrzo.
*/

#if defined LCRZODEF_SYSTEM_Unix
#elif defined LCRZODEF_SYSTEM_Windows

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_init_arp_snmpdll(void);
int lcrzo_priv_conf_init_arp_snmpdll(void)
{ lcrzo_priv_snmpdll snmpdll;
  lcrzo_priv_snmpdllreq snmpdllreq;
  AsnObjectIdentifier oid0, oid1, oid2, oid3;
  lcrzo_uint32 ifindex, arptype;
  lcrzo_ipl ipaddress;
  lcrzo_etha etha;
  lcrzo_bool ispermanent;

  /* load the DLL, and init functions */
  lcrzo_er(lcrzo_priv_snmpdll_init(&snmpdll));

  /* get characteristics of the arp table */
  lcrzo_er(lcrzo_priv_AsnOid_init(&oid0, 1, 3, 6, 1, 2, 1, 4, 22, 1, 1, -1));
  lcrzo_er(lcrzo_priv_AsnOid_init(&oid1, 1, 3, 6, 1, 2, 1, 4, 22, 1, 2, -1));
  lcrzo_er(lcrzo_priv_AsnOid_init(&oid2, 1, 3, 6, 1, 2, 1, 4, 22, 1, 3, -1));
  lcrzo_er(lcrzo_priv_AsnOid_init(&oid3, 1, 3, 6, 1, 2, 1, 4, 22, 1, 4, -1));
  lcrzo_er(lcrzo_priv_snmpdllreq_init(&snmpdllreq, &oid0, &oid1, &oid2, &oid3, NULL));
  lcrzo_er(lcrzo_priv_AsnOid_close(&oid0));
  lcrzo_er(lcrzo_priv_AsnOid_close(&oid1));
  lcrzo_er(lcrzo_priv_AsnOid_close(&oid2));
  lcrzo_er(lcrzo_priv_AsnOid_close(&oid3));
  lcrzo_er(lcrzo_priv_snmpdllreq_getnext(&snmpdll, &snmpdllreq));
  while ( snmpdllreq.varBindList.list[0].name.ids[7] == 22 &&
          snmpdllreq.varBindList.list[0].name.ids[9] == 1)
  { //lcrzo_er(lcrzo_priv_snmpdllreq_print(&snmpdllreq));
    lcrzo_er(lcrzo_priv_snmpdllreq__uint32(&snmpdllreq, 0, &ifindex));
    lcrzo_er(lcrzo_priv_snmpdllreq__etha(&snmpdllreq, 1, etha));
    lcrzo_er(lcrzo_priv_snmpdllreq__uint32(&snmpdllreq, 2, &ipaddress));
    lcrzo_er(lcrzo_priv_snmpdllreq__uint32(&snmpdllreq, 3, &arptype));
    ispermanent=LCRZO_FALSE;
    if (arptype==4) ispermanent=LCRZO_TRUE;

    lcrzo_priv_conf_devicenumbername_arp_add_system(ifindex, etha, ipaddress, ispermanent);

    lcrzo_er(lcrzo_priv_snmpdllreq_getnext(&snmpdll, &snmpdllreq));
  }
  lcrzo_er(lcrzo_priv_snmpdllreq_close(&snmpdllreq));

  /* end */
  lcrzo_er(lcrzo_priv_snmpdll_close(&snmpdll));
  
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_init_arp_iphlpapidll(void);
int lcrzo_priv_conf_init_arp_iphlpapidll(void)
{ lcrzo_priv_iphlpapidll iphlpapidll;
  lcrzo_uint32 bufsize, i, devicenumber;
  PMIB_IPNETTABLE pipnettable;
  lcrzo_bool ispermanent;
  lcrzo_etha etha;
  int ret;
    
  /* load the library */
  lcrzo_er(lcrzo_priv_iphlpapidll_init(&iphlpapidll));

  /* get the ARP table size */
  bufsize=0;
  ret=iphlpapidll.GetIpNetTable(NULL, &bufsize, FALSE);
  if ( ret!=ERROR_INSUFFICIENT_BUFFER )
  { lcrzo_er(lcrzo_priv_iphlpapidll_close(&iphlpapidll));
    if (ret==ERROR_NO_DATA) return(LCRZO_ERR_OK);
    return(LCRZO_ERR_FUGETIPNETTABLE);
  }

  /* allocate data */
  pipnettable=(PMIB_IPNETTABLE)GlobalAlloc(GPTR, bufsize);
  if (pipnettable==NULL)
  { lcrzo_er(lcrzo_priv_iphlpapidll_close(&iphlpapidll));
    return(LCRZO_ERR_FUGLOBALALLOC);
  }
  
  /* get the ARP table */
  ret=iphlpapidll.GetIpNetTable(pipnettable, &bufsize, FALSE);
  if ( ret!=NO_ERROR )
  { GlobalFree(pipnettable);
    lcrzo_er(lcrzo_priv_iphlpapidll_close(&iphlpapidll));
    return(LCRZO_ERR_FUGETIPADDRTABLE);
  }
  
  /* loop through each entry */
  for ( i=0 ; i<pipnettable->dwNumEntries ; i++ )
  { if (pipnettable->table[i].dwPhysAddrLen==LCRZO_ETHA_MAXBYTES)
    { lcrzo_etha_init(pipnettable->table[i].bPhysAddr[0],
                      pipnettable->table[i].bPhysAddr[1],
                      pipnettable->table[i].bPhysAddr[2],
                      pipnettable->table[i].bPhysAddr[3],
                      pipnettable->table[i].bPhysAddr[4],
                      pipnettable->table[i].bPhysAddr[5], etha);
    }
    else
    { lcrzo_etha_initdefault(etha);
    }
    if (pipnettable->table[i].dwType==MIB_IPNET_TYPE_STATIC)
      ispermanent=LCRZO_TRUE;
    else
      ispermanent=LCRZO_FALSE;

    devicenumber=pipnettable->table[i].dwIndex;
    lcrzo_priv_conf_devicenumbername_arp_add_system(devicenumber, etha, lcrzo_htonl(pipnettable->table[i].dwAddr), ispermanent);
  }

  /* unload the library */
  GlobalFree(pipnettable);
  lcrzo_er(lcrzo_priv_iphlpapidll_close(&iphlpapidll));
  return(LCRZO_ERR_OK);
}

#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif


/*-------------------------------------------------------------*/
int lcrzo_priv_conf_init_arpcache(void)
#if defined LCRZODEF_SYSTEM_Linux
{ int ret, c;
  unsigned int hwtype, flags;
  FILE *pf;
  char mask[21], device[21];
  lcrzo_ips ips;
  lcrzo_ipl ipl;
  lcrzo_eths eths;
  lcrzo_etha etha;
  lcrzo_device purgeddevice;
  lcrzo_bool ispermanent;

  /*ouverture du procfs*/
  pf=fopen("/proc/net/arp", "r");
  if (pf==NULL) return (LCRZO_ERR_FUFOPEN);

  /*on passe l'entete*/
  c=fgetc(pf);
  while ( c!=0x0A && c!=EOF ) c=fgetc(pf);

  /*on lit les entrees*/
  while(!feof(pf))
  { ret=fscanf(pf, "%16s 0x%4x 0x%4x %20s %20s %20s\n", ips,
	       &hwtype, &flags, eths, mask, device);
    if (ret<6 )
    { /*on n'en a pas lu assez, on ignore et passe a la ligne suivante*/
      c=fgetc(pf);
      while ( c!=0x0A && c!=EOF ) c=fgetc(pf);
    }
    else
    { if ( hwtype!=ARPHRD_ETHER ) continue;
      ispermanent=flags&ATF_PERM;      
      ret=lcrzo_device_init(device, purgeddevice);
      if ( ret!=LCRZO_ERR_OK ) continue;
      ret=lcrzo_etha_init_eths(eths, etha);
      if ( ret!=LCRZO_ERR_OK ) continue;
      ret=lcrzo_ipl_init_ips(ips, &ipl);
      if ( ret!=LCRZO_ERR_OK ) continue;
      lcrzo_efr(lcrzo_priv_conf_arp_add_system(purgeddevice, etha, ipl, 
					       ispermanent), 
		fclose(pf));
    }
  }
  ret=fclose(pf);
  if ( ret == -1 ) return(LCRZO_ERR_FUFCLOSE);
  return(LCRZO_ERR_OK);
}
#elif defined LCRZODEF_SYSTEM_FreeBSD || defined LCRZODEF_SYSTEM_OpenBSD
{ int mib[6];
  LAURENT_size_t len;
  char *buf, *next, *end;
  struct rt_msghdr *rtm;
  struct sockaddr_inarp *sin2;
  struct sockaddr_dl *sdl;
  lcrzo_device device;
  lcrzo_etha etha;
  lcrzo_ipl ipl;
 
  /*detaille dans "man sysctl" et "socket.h"*/
  mib[0] = CTL_NET;        /*reseau*/
  mib[1] = PF_ROUTE;       /*famille de protocole*/
  mib[2] = 0;              /*numero de protocole*/
  mib[3] = AF_INET;        /*famille d'adresse*/
  mib[4] = NET_RT_FLAGS;   /*type d'information*/
  mib[5] = RTF_LLINFO;     /*flags*/
 
  /*on fait d'abord un premier appel pour connaitre la taille a allouer*/ 
  if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
  { return(LCRZO_ERR_FUSYSCTL);
  }

  /*on alloue la taille necessaire*/
  lcrzo_er(lcrzo_string_alloc(len, &buf));

  /*on fait le vrai appel*/
  if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) 
  { lcrzo_string_free(buf);
    return(LCRZO_ERR_FUSYSCTL);
  }
  end = buf + len;
  
  /*on parcours les elements de la liste trouvee*/
  for (next = buf ; next < end ; next += rtm->rtm_msglen) 
  { rtm = (struct rt_msghdr *)next;
    sin2 = (struct sockaddr_inarp *)(rtm + 1);
    sdl = (struct sockaddr_dl *)(sin2 + 1);
    /* get the device */
    if ( sdl->sdl_nlen>LCRZO_DEVICE_MAXBYTES )
      return(LCRZO_ERR_PAOVERFLOW);
    lcrzo_string_init_text(sdl->sdl_data, LCRZO_DEVICE_MAXBYTES, device);
    device[sdl->sdl_nlen]='\0';
    /* get the Ethernet address */
    memcpy(etha, LLADDR(sdl), ETHER_ADDR_LEN);
    /* get the IP address */
    ipl=lcrzo_ntohl(sin2->sin_addr.s_addr);
    lcrzo_priv_conf_arp_add_system(device, etha, ipl, LCRZO_FALSE); 
  }

  lcrzo_string_free(buf);
  return(LCRZO_ERR_OK);
}
#elif defined LCRZODEF_SYSTEM_Solaris
{ int ret, c, nbchamps;
  FILE *pf;
  char mask[21], device[21], ligne[120];
  lcrzo_ips ipsa;
  lcrzo_ipl ipl;
  lcrzo_eths ethsa[2];
  lcrzo_etha etha;

  /*ouverture du procfs*/
  pf=popen("/bin/netstat -pn", "r");
  if (pf==NULL) return(LCRZO_ERR_FUPOPEN);

  /*on passe l'entete : 3 lignes*/
  c=fgetc(pf); while ( c!=0x0A && c!=EOF ) c=fgetc(pf);
  c=fgetc(pf); while ( c!=0x0A && c!=EOF ) c=fgetc(pf);
  c=fgetc(pf); while ( c!=0x0A && c!=EOF ) c=fgetc(pf);

  /*on lit les entrees*/
  while(!feof(pf))
  { fgets(ligne, 119, pf);
    nbchamps=sscanf(ligne, "%20s %16s %16s %20s %20s",
		    device, ipsa, mask, ethsa[0], ethsa[1]);
    if (nbchamps<4 )
    { /*on n'en a pas lu assez, on ignore et passe a la ligne suivante*/
      c=fgetc(pf);
      while ( c!=0x0A && c!=EOF ) c=fgetc(pf);
    }
    else
    { ret=lcrzo_ipl_init_ips(ipsa, &ipl);
      if ( ret!=LCRZO_ERR_OK ) continue;
      ret=lcrzo_etha_init_eths(ethsa[nbchamps-4], etha);
      if ( ret!=LCRZO_ERR_OK ) continue;
      lcrzo_efr(lcrzo_priv_conf_arp_add_system(device, etha, ipl,LCRZO_FALSE), 
		pclose(pf));
    }
  }

  ret=pclose(pf);
  if ( ret == -1 ) return(LCRZO_ERR_FUPCLOSE);
  lcrzo_err_purge(); /*because Solaris set errno*/
  return (LCRZO_ERR_OK);
} 
#elif defined LCRZODEF_SYSTEM_Windows
{ int ret;
  lcrzo_priv_globalvars *ptrglo;

  lcrzo_er(lcrzo_priv_globalvars_ptr_get(&ptrglo));
  switch(ptrglo->windows_systemversion)
  { case LCRZO_WINDOWS_SYSTEMVERSION_98:
    case LCRZO_WINDOWS_SYSTEMVERSION_me:
    case LCRZO_WINDOWS_SYSTEMVERSION_nt4:
    case LCRZO_WINDOWS_SYSTEMVERSION_2k:
    case LCRZO_WINDOWS_SYSTEMVERSION_xp:
      ret=lcrzo_priv_conf_init_arp_iphlpapidll();
      if (ret!=LCRZO_ERR_OK)
      { ret=lcrzo_priv_conf_init_arp_snmpdll();
      }
      break;

    case LCRZO_WINDOWS_SYSTEMVERSION_95:
      ret=lcrzo_priv_conf_init_arp_snmpdll();
      break;

    default:
      ret=lcrzo_priv_conf_init_arp_iphlpapidll();
      if (ret!=LCRZO_ERR_OK)
      { ret=lcrzo_priv_conf_init_arp_snmpdll();
      }
      break;
  }

  /* return OK even if nothing was initialized */
  return(LCRZO_ERR_OK);
}
#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_arpcache_debug_fprint(LCRZOFILE *pf)
#ifdef LCRZODEF_SYSTEM_Unix
{ return(LCRZO_ERR_OK);
  pf=pf;
}
#elif defined LCRZODEF_SYSTEM_Windows
{ 
  fprintf(pf, "\n\n++++++++++++++++++++++++++++++++\n");
  fprintf(pf, "Configuration obtained with lcrzo_priv_conf_init_arp_iphlpapidll\n");
  lcrzo_conf_flush_system();
  lcrzo_err_fprint(pf, lcrzo_priv_conf_init_arp_iphlpapidll());
  lcrzo_conf_fprint(pf);
  lcrzo_priv_conf_devicenumbername_debug_fprint(pf);

  fprintf(pf, "\n\n++++++++++++++++++++++++++++++++\n");
  fprintf(pf, "Configuration obtained with lcrzo_priv_conf_init_arp_snmpdll\n");
  lcrzo_conf_flush_system();
  lcrzo_err_fprint(pf, lcrzo_priv_conf_init_arp_snmpdll());
  lcrzo_conf_fprint(pf);
  lcrzo_priv_conf_devicenumbername_debug_fprint(pf);

  return(LCRZO_ERR_OK);
}
#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif
