dom_ip_list - Tool zur Zuordnung IP zu Domain bei Apache und nginx

Hat man auf seinen Servern reichlich Domains auf verschiedene IPs verteilt, fehlt mitunter der schnelle Überblick. Da ich selbst gerade für den Umzug von Domains wissen wollte, was wo liegt, erstellte ich dafür das folgende kleine Tool in C++ für Linux. Mit (Free)Pascal, C# (mono) oder php+bash wäre man schneller fertig und der Code wäre besser lesbar, aber ich wollte mal wieder etwas in C++ schreiben. Keine Frage, der Code ist mit der Zwischenspeicherung in einer Objekt-Liste mit dem typischen Template-Ansatz übertrieben kompliziert. Es kann so aber auch zur Veranschaulichung für derartige Aufgaben dienen.

/* 
 * File:   main.cpp
 * Author: nschroeder, proteino.de
 *
 * Created on 25. Januar 2014, 20:10
 */
 
#include <cstdlib>
#include <iostream>
#include <cstdio>
#include <sys/types.h>
#include <dirent.h>
#include <fstream>
#include <stdint.h>
#include <list>
 
using namespace std;
 
// The List STL template requires overloading operators =, == and <.
// look at http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html#LIST
 
class domMember{
	friend ostream &operator<<(ostream &, const domMember &);
 
	public:
		std::string		filename;
		std::string		ip;
		std::string		dom;
 
		domMember();
		domMember(const domMember &);
		~domMember(){};
		domMember &operator=(const domMember &rhs);
		int operator==(const domMember &rhs) const;
		int operator<(const domMember &rhs) const;
};
 
domMember::domMember(){																// Constructor
	filename		= "";
	ip				= "";
	dom				= "";
}
 
domMember::domMember(const domMember &copyin){										// Copy constructor to handle pass by value.
	filename		= copyin.filename;
	ip				= copyin.ip;
	dom				= copyin.dom;
}
 
ostream &operator<<(ostream &output, const domMember &aaa){
	output << aaa.filename << ' ' << aaa.ip << ' ' << aaa.dom << endl;
	return output;
}
 
domMember& domMember::operator=(const domMember &rhs){
	this->filename	= rhs.filename;
	this->dom		= rhs.dom;
	this->ip		= rhs.ip;
	return *this;
}
 
int domMember::operator==(const domMember &rhs) const{
	if( this->filename != rhs.filename) return 0;
	if( this->dom != rhs.dom) return 0;
	if( this->ip != rhs.ip) return 0;
	return 1;
}
 
int domMember::operator<(const domMember &rhs) const{
	if( this->filename == rhs.filename && this->ip == rhs.ip && this->dom < rhs.dom) return 1;
	if( this->filename == rhs.filename && this->ip < rhs.ip) return 1;
	if( this->filename < rhs.filename ) return 1;
	return 0;
}
 
std::list<domMember>	domList;
 
std::string trim(const std::string& str, const std::string& whitespace = " \t"){	// http://stackoverflow.com/questions/1798112/removing-leading-and-trailing-spaces-from-a-string
    const int32_t strBegin = str.find_first_not_of(whitespace);
    if(strBegin == std::string::npos)return "";										// no content
    const int32_t strEnd = str.find_last_not_of(whitespace);
    const int32_t strRange = strEnd - strBegin + 1;
    return str.substr(strBegin, strRange);
}
 
uint32_t paramCheck(int argc, char** argv) {
	uint32_t	confType=0;
    if(argc==2){
		std::string s2=argv[1];
		if(s2.compare("nginx")==0)confType=1;
		else if(s2.compare("apache")==0){
			confType=2;
			argv[1]="apache2";
		}else if(s2.compare("apache2")==0)confType=2;
    }
 
	if(confType==0){
        cout<<"Call with param 'apache' or 'nginx' to define for what Webserver the Conf-Files will be checked.\n";
		exit;
	}
	return confType;
}
 
void readNginxConffile(const std::string& sFile){									/// Reads Conffiles from nginx, single conf-File for every Domain, IP must be configered
	std::string				sLine;
	uint32_t				iPos;
	uint32_t				iHelp;
	domMember				domTemp;
 
	ifstream inFile(sFile.c_str());
	iHelp=0;
	while(!inFile.eof()){
		getline(inFile,sLine);
		if(sLine.find("server_name")!=std::string::npos){							// search for ServerName (normally a List, the simple DomainName is mostly the FileName)
			iPos=sLine.find(";");
			domTemp.dom=trim(sLine.substr(12,iPos-12));
			iHelp|=1;
		}
 
		if(sLine.find("listen")!=std::string::npos){								// search for IP-Address
			iPos=sLine.find(":");
			domTemp.ip=trim(sLine.substr(7,iPos-7));								// listen und den Port wegschnippeln
			iHelp|=2;
		}
 
		if(iHelp==3){
			domTemp.filename=sFile;
			domList.push_back(domTemp);
			iHelp=0;
		}
	}
}
 
void readApacheConffile(const std::string& sFile){									/// Reads Conffiles from Apache, tested with ISPCP-Config (all Domains in one single File)
	std::string				sLine;
	uint32_t				iPos1;
	uint32_t				iPos2;
	uint32_t				iHelp;
	domMember				domTemp;
 
	ifstream inFile(sFile.c_str());
	iHelp=0;
	while(!inFile.eof()){
		getline(inFile,sLine);
		iPos1=sLine.find("ServerName");												// for later Versions also take care about ServerAlias
		if(iPos1<sLine.length()){													// search for ServerName
			domTemp.dom=trim(sLine.substr(iPos1+10));
			iHelp|=1;
		}
 
		iPos1=sLine.find("VirtualHost");
		if(iPos1<sLine.length()){													// search for IP-Address
			iPos2=sLine.find(":");
			domTemp.ip=trim(sLine.substr(iPos1+11,iPos2-(iPos1+11)));				// listen und den Port wegschnippeln
			iHelp|=2;
		}
 
		if(iHelp==3){
			domTemp.filename=sFile;
			domList.push_back(domTemp);
			iHelp=0;
		}
	}
}
 
int main(int argc, char** argv) {
	uint32_t confType=paramCheck(argc, argv);
    if(confType==0)return 1;
 
    DIR						*dirHandle;
    struct dirent			*dirEntry;
	std::string				sPath;
	std::string				s2;
 
	sPath="/etc/";
	sPath+=argv[1];
    sPath+="/sites-available/";
 
    dirHandle=opendir(sPath.c_str());
    if(dirHandle!=NULL){
        while(0!=(dirEntry=readdir(dirHandle))){
            s2=dirEntry->d_name;
            if(s2.compare(".")==0)continue;
            if(s2.compare("..")==0)continue;
            if(s2.compare("default")==0)continue;
            if(s2.find("default.")==0)continue;
            if(s2.find(".swp")==s2.length()-4)continue;								// if a file is opened with vim there exists a .swp
 
            s2=sPath;
            s2+=dirEntry->d_name;
 
			switch(confType){
				case 1:		readNginxConffile(s2.c_str());
							break;
				case 2:		readApacheConffile(s2.c_str());
							break;
			}
        }
        closedir(dirHandle);
 
		domList.sort();
		s2="";
		std::list<domMember>::iterator i;
		for(i=domList.begin();i!=domList.end();++i){
			if(s2.compare((*i).filename)){
				cout<<endl<<(*i).filename<<endl;
				s2=(*i).filename;
			}
			cout<<"\t"<<(*i).ip<<"\t"<<(*i).dom<<endl;
		}
    }
 
    return 0;
}

Compilieren
Das Tool dom_ip_list habe ich mit NetBeans erstellt und compiliert. Als Compiler habe ich G++ verwendet. Da keine außergewöhnlichen Abhängigkeiten und Einstellungen existieren, kann natürlich auch ohne make-File direkt auf der Console mittels compiliert werden. Das macht man mittels g++ dom_ip_list.cpp und die resultierende a.out benennt man in den gewünschten Namen um.

AufrufVorschau: dom_ip_list - Ausgabe der Zuordnung IP-Domain für nginx und Apache
Aufgerufen wird es mit einem Parameter für den gewünschten Server, also apache oder nginx. Da seit Ewigkeiten mit Apache der Apache2 gemeint ist, sind Angaben mit und ohne 2 erlaubt.

Server-Konfiguration
Die Domainkonfigurationen müssen wie üblich unterhalb von /etc/[servername]/sites-available/ liegen.
Getestet habe ich mit einer typischen nginx-Konfiguration, bei der die IP-Adressen mit in den Domaindefinitionen stehen. Dies ist für nginx nicht unbedingt erforderlich, würde ich aber wegen besserer Übersichtlichkeit dringend empfehlen. Ohne diese IP-Angabe funktioniert das Tool natürlich auch nicht, da es keine DNS-Abfragen ausführt.
Die von mir verwendete Apache-Konfiguration stammt aus den automatisch von ispCP generierten Dateien, sollte aber auch mit anderen manuell erstellten Files oder sonstigen Template-Engines funktionieren.

Ausgabe
Die Ausgabe erfolgt sortiert nach Dateiname, IP, Domain. Im Screenshot sieht man eine Ausgabe (testweise die Confs in eine VM kopiert) für nginx und Apache direkt hintereinander.

Einen Kommentar schreiben