// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: arpscan.cpp
// C++ Compiler Used: MSVC, BCC32, GCC, HPUX aCC, SOLARIS CC
// Produced By: DataReel Software Development Team
// File Creation Date: 09/20/1999
// Date Last Modified: 06/17/2016
// Copyright (c) 2001-2024 DataReel Software Development
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
This library is free software; you can redistribute it and/or

modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
 
This library 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
Lesser General Public License for more details.
 
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  
USA

Test program used to get the Media Access Control (MAC) addresses
of all the hosts on a specified class C subnet.
*/
// ----------------------------------------------------------- // 
#include "gxdlcode.h"

#if defined (__USE_ANSI_CPP__) // Use the ANSI Standard C++ library
#include <iostream>
#include <fstream>
using namespace std; // Use unqualified names for Standard C++ library
#else // Use the old iostream library by default
#include <iostream.h>
#include <fstream.h>
#endif // __USE_ANSI_CPP__

#include <string.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include "gxsping.h"
#include "gxip32.h"
#include "gxmac48.h"

// Platform specific include files
#if defined (__UNIX__)
#include <net/if_arp.h>
#include <sys/ioctl.h>
#endif

#if defined (__WIN32__)
int WIN32GetMAC(const char *ip_address, char *mac_address);
#endif

int GetMacAddress(const char *ip_address, char *eth_dev, char *fname = 0,
		  fstream *outfile = 0)
{
  // Construct a raw socket;
  gxSocket raw_socket;
  if(raw_socket.InitSocketLibrary() != 0) {
    cout << raw_socket.SocketExceptionMessage() << "\n" << flush;
    if(outfile)
      *(outfile) << raw_socket.SocketExceptionMessage() << "\n" << flush;
    return 0;
  }
  cout << ip_address << '\t' << flush;
  if(outfile) *(outfile) << ip_address << '\t' << flush;
  gxsHostNameInfo *hostnm = raw_socket.GetHostInformation((char *)ip_address);
  if(hostnm) {
    cout << hostnm->h_name << flush;
    if(outfile) *(outfile) << hostnm->h_name << flush;
  }
  else {
    cout << "no_DNS_entry" << flush;
   if(outfile) *(outfile) << "no_DNS_entry" << flush;
  }
  
  if(raw_socket.InitSocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, 0, 
			   (char *)ip_address) < 0) {
    cout << raw_socket.SocketExceptionMessage() << "\n" << flush;
    if(outfile)
      *(outfile) << raw_socket.SocketExceptionMessage() << "\n" << flush;
    return 0;
  }
  
  // Ping with a specified timeout value
  gxsPing ping(&raw_socket);
  if(ping.Ping(4) != gxSOCKET_NO_ERROR) {
    if(raw_socket.GetSocketError() == gxSOCKET_REQUEST_TIMEOUT) {
    }
    else {
      cout << "\n" << flush;
      cout << raw_socket.SocketExceptionMessage() << "\n" << flush;
      if(outfile) {
	*(outfile) << "\n" << flush;
	*(outfile) << raw_socket.SocketExceptionMessage() << "\n" << flush;
      }
      delete hostnm;
      return 0;
    }
  }

#if defined (__WIN32__)
  char mac_address[255];
  if(!WIN32GetMAC(ip_address, mac_address)) {
    cout << "\n" << flush;
    if(outfile) *(outfile) << "\n" << flush;
    delete hostnm;
    return 0; // No entry in the arp cache
  } 
  gxMAC48 mac(mac_address, '-', 2);
  cout << '\t' << mac.c_str(mac_address) << flush;
  if(outfile) *(outfile) << '\t' << mac.c_str(mac_address) << flush;
#elif defined (__UNIX__)
  arpreq arp_data;
  memset(&arp_data, 0, sizeof(arp_data));
  memcpy(&arp_data.arp_pa, &raw_socket.sin, sizeof(arp_data.arp_pa));         

#if defined (__LINUX__) 
  strcpy(arp_data.arp_dev, eth_dev);
#endif
  
#ifndef SIOCGARP
#error SIOCGARP is not available on this platform
#endif

  if(ioctl(raw_socket.GetSocket(), SIOCGARP, &arp_data) == -1) {
    cout << "\n" << flush;
    if(outfile) *(outfile) << "\n" << flush; 
    delete hostnm;
    return 0; // No entry in the arp cache
  }
  
  char mac_address[255];
  unsigned char *ptr = (unsigned char *)&arp_data.arp_ha.sa_data[0];
  sprintf(mac_address, "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X",*ptr, 
	  *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5));
  
  gxMAC48 mac(mac_address, ':', 2);
  cout << '\t' << mac.c_str(mac_address) << flush;
  if(outfile) *(outfile) << '\t' << mac.c_str(mac_address) << flush;
#else
#error You must define a target platform: __WIN32__ or __UNIX__
#endif

  if(fname) { // Find the vendor information
    char name[255];
    char comment[255];
    if(FindVendor((const char *)fname, mac, name, comment)) {
      cout << '\t' << name << flush;
      if(outfile) *(outfile) << '\t' << name << flush;
    }
  }
  cout << "\n" << flush;
  if(outfile) *(outfile) << "\n" << flush;
  delete hostnm;
  return 1;
}

int main(int argc, char **argv)
{
#ifdef __UNIX__
  // Raw sockets require root access on all UNIX platforms. Programs
  // that use raw sockets can be executed by non-root users if the
  // effective user ID of the executing process is set to the same as
  // the file's owner. In the case root must own the executable and
  // the effective User-ID or Group-ID on execution bit must be set
  // by root using the chmod command: "chmod u+s" or "chmod 4755"
  // Now the effective user ID of the executing process will be set to 
  // the same user ID of the file's owner. 
  if(geteuid() != 0) {
    cout << "You must be root to run this program" << "\n";
    return 0;
  }
#endif

  // Check the command line arguments
#if defined (__WIN32__)
  if(argc < 2) {
    cout << "\n";
    cout << "Usage: " << argv[0] << " subnet " << "\n";
    cout << "Example 1: " << argv[0] <<" 192.168.0 " << "\n";
    cout << "Example 2: " << argv[0] <<" 192.168.0 maccodes.txt" << "\n";
    cout << "Example 3: " << argv[0] <<" 192.168.0.1 " << "\n";
    cout << "Example 4: " << argv[0] <<" 192.168.0.1 maccodes.txt" << "\n";
    cout << "Example 5: " << argv[0] <<" 192.168.0.255 maccodes.txt \
outfile.txt" << "\n";
    return 1;
  }
  char *subnet = argv[1];
  char *eth_dev = 0;
  char *fname = 0;
  char *ofname = 0;
  if(argc >= 3) fname = argv[2];
  if(argc == 4) ofname = argv[3];
#elif defined (__UNIX__)
  if(argc < 3) {
    cout << "\n";
    cout << "Usage: " << argv[0] << " subnet ethernet_device" << "\n";
    cout << "Example 1: " << argv[0] <<" 192.168.0 eth0" << "\n";
    cout << "Example 2: " << argv[0] <<" 192.168.0 eth0 maccodes.txt" 
	 << "\n";
    cout << "Example 3: " << argv[0] <<" 192.168.0.1 eth0" << "\n";
    cout << "Example 4: " << argv[0] <<" 192.168.0.1 eth0 maccodes.txt" 
	 << "\n";
    cout << "Example 5: " << argv[0] <<" 192.168.0.255 eth0 maccodes.txt \
outfile.txt" << "\n";
    return 1;
  }
  char *subnet = argv[1];
  char *eth_dev = argv[2];
  char *fname = 0;
  char *ofname = 0;
  if(argc >= 4) fname = argv[3];
  if(argc == 5) fname = argv[4];
#else
#error You must define a target platform: __WIN32__ or __UNIX__
#endif

  fstream outfile;
  int out = 0;
  if(ofname) {
    // Open the output file and truncate if it already exists
    out = 1;
    outfile.open(ofname, ios::out|ios::trunc|ios::binary);
    if(!outfile) {
      cout << "Could not open the " << ofname << " output file" << "\n";
      return 1;
    }
  }

  gxIP32 ip32(subnet);
  char sbuf[255];
  if((ip32.GetByte4() == 0) || (ip32.GetByte4() == 255)) {
    // Find the mac address of all the nodes on a specified subnet
    for(int i = 1; i < 255; i++) {
      ip32.SetByte4(i);
      if(out)
	GetMacAddress(ip32.c_str(sbuf), eth_dev, fname, &outfile); 
      else
	GetMacAddress(ip32.c_str(sbuf), eth_dev, fname);
    }
  }
  else {
    // Get the mac address of a single node
    if(out)
      GetMacAddress(ip32.c_str(sbuf), eth_dev, fname, &outfile);
    else 
      GetMacAddress(ip32.c_str(sbuf), eth_dev, fname);
  }

  if(out) outfile.close();
  return 0;
}

#if defined (__WIN32__)
int WIN32GetMAC(const char *ip_address, char *mac_address)
// Function used to get a mac address from the WIN32 arp cache.
// Returns true if an arp cache entry exists for the specified
// IP address string.
{
  system("arp -a > arpcache.txt");
  ifstream infile("arpcache.txt", ios::in);
  if(!infile) return 0; // Cannot open the file
  
  char sbuf[255];
  char ip_in[255];

  while(!infile.eof()) {
    infile.getline(sbuf, 255);
    int len, offset, i = 0;
    int byte_count = strlen(sbuf);
    ip_in[0] = 0;
    mac_address[0] = 0;
    
    // Trim leading tabs and spaces
    while((sbuf[i] == ' ') || (sbuf[i] == '\t')) i++;
    byte_count -= i;
    if(byte_count <= 0) continue;
    memmove(sbuf, sbuf+i, byte_count);
    sbuf[byte_count] = 0;

    // Parse the IP Address
    for(i = 0; i < byte_count; i ++) {
      if((sbuf[i] == ' ') || (sbuf[i] == '\t')) break;
      ip_in[i] = sbuf[i];
    }
    ip_in[i] = 0; // Null terminate the string

    if(strcmp(ip_in, ip_address) == 0) { // Parse the MAC Address
      while((sbuf[i] == ' ') || (sbuf[i] == '\t')) i++;
      byte_count -= i;
      if(byte_count <= 0) continue;
      memmove(sbuf, sbuf+i, byte_count);
      for(i = 0; i < byte_count; i ++) {
	if(sbuf[i] == ' ') break;
	mac_address[i] = sbuf[i];
      }
      mac_address[i] = 0; // Null terminate the string
      
      if(mac_address[0] != 0)  { // Remove any trailing spaces
	offset = len = strlen(mac_address);
	while(offset--) if(mac_address[offset] != ' ') break;
	if(offset < len) mac_address[offset+1] = 0;
	return 1; // Found the arp cache entry
      }
      else {
	return 0;
      }
    }
  }
  infile.close();
  return 0;
}
#endif
// ----------------------------------------------------------- // 
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //