From 6dda64dcbe5dd0a722e5a82cc47a6cc7c4016fd1 Mon Sep 17 00:00:00 2001 From: Dominic Reich Date: Thu, 7 Jan 2016 17:40:04 +0100 Subject: [PATCH] initial commit * I moved the project from private to public so anyone that wants to read a bit in my code can do that. Although my code might not be the best to read, as there might be lots of bugs and not really look that good. I'm not a professional codewriter but I had a few lessons back in school. :) * Do not blame me for anything. --- .gitignore | 6 + LICENSE | 22 ++ README.md | 4 + blacklist.cpp | 632 ++++++++++++++++++++++++++++++++++++++++++++++++++ build.sh | 3 + version.h | 34 +++ 6 files changed, 701 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 blacklist.cpp create mode 100755 build.sh create mode 100644 version.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6b98bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin/ +obj/ +*\~ +*.bak +*.old +*.swp diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0856dee --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Dominic Reich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..7a555eb --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# blacklist +An iptables-wrapper for blacklisting IPs + +Uncontinued due to the switch to FreeBSD and the use of pf instead of iptables (which usage syntax is pretty similar to blacklist) diff --git a/blacklist.cpp b/blacklist.cpp new file mode 100644 index 0000000..a8d9264 --- /dev/null +++ b/blacklist.cpp @@ -0,0 +1,632 @@ +/* + * main.cpp + * + * The MIT License (MIT) {{{ + * + * Copyright (c) 2015 Dominic Reich + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. }}} + * + */ + +/* + * SOME SPECIAL NOTES FOR THIS PROGRAM + * 1. When compiling, use -lboost_system in 'link-libraries' + * 2. When compiling for other hosts, use -static in 'linker-options' + * + * + */ + +#include +#include +#include // get the current user-id +#include // ip-address check +#include // ip-address check +#include +//#include // sorting the vector +#include // sort in vector +#include // get duplicates in vector +#include // remove duplicates in vectora +#include // find ip in file (string compare) +#include "version.h" // Code::Blocks AutoVersioning + + +/** \brief Some definitions + * \param DEFAULT_FILENAME Sets the default filename for the blacklist file + * \param IPTABLES iptables' default location + * + * \info We might change this later so we can include those defines + * \info at compile time like: + * \info ./configure --iptables=[new-location] --default-file=[new-location] + * + */ + +#define DEFAULT_FILENAME "/etc/blacklist-ip" +#define TMP_FILENAME "/tmp/blacklist" +#define IPTABLES "/sbin/iptables" + +using namespace std; + +inline bool checkRoot(); +inline bool fileExists(const string &name); +inline bool checkIptables(); +void printHelp(bool printAll); +////inline bool checkBlacklistFile(const char *filename); +//const char *setFilename(char *tmpName); +inline bool listFile(const char *filename); +inline bool checkFile(const char *filename); +//inline bool compare(string a, string b); +int loadIptables(const char *filename); +int flushIptables(); +int listIptables(); +int addIpToIptables(string ip); +int checkIp(string ip); +inline bool addIpToFile(const char *filename, string ip); +inline bool removeIpFromFile(const char *filename, string ip); +inline bool countIpAdresses(const char *filename); +inline bool findIpInFile(const char *filename, string ip); + +/** \brief main function (startup function) + * + * \param argc int + * \param argv char** + * \return int + * + */ +int main(int argc, char **argv) +{ + if(checkIptables() == false) + return 1; + + if(argc < 2 || argc > 3) + { + printHelp(false); + return 0; + } + + string Choice = argv[1]; + int cmd = 0; + + const char *Filename = DEFAULT_FILENAME; + + /// add ip-address to file (check for root) + if(Choice == "-a") + { + if(checkRoot() == false) + return 1; + + if(argc != 3) + return 1; + + cmd = addIpToFile(Filename, argv[2]); + if(cmd == false) + return 1; + + /// remove ip-address from file (check for root) + } else if(Choice == "-d") + { + if(checkRoot() == false) + return 1; + + if(argc != 3) + return 1; + + cmd = removeIpFromFile(Filename, argv[2]); + if(cmd == false) + return 1; + + /// list ip-addresses from file on screen (no root required) + } else if(Choice == "-l") + { + if(!listFile(Filename)) + return 1; + + /// find an ip-address in file + } else if(Choice == "-f") + { + if(argc != 3) + return 1; + + cmd = checkIp(argv[2]); + if(cmd != 0) + return 1; + + if(!findIpInFile(Filename, argv[2])) + return 1; + + /// show how many ips are stored in file + } else if(Choice == "-C") + { + if(!countIpAdresses(Filename)) + return 1; + + /// sort and check file for duplicates (check for root) + } else if(Choice == "-c") + { + if(checkRoot() == false) + return 1; + + if(!checkFile(Filename)) + return 1; + + /// reload iptables chain BLACKLIST (check for root) + /// (check file, flush iptables chain, re-load ips from file to chain) + } else if(Choice == "-r") + { + if(checkRoot() == false) + return 1; + + // flush chain + cmd = flushIptables(); + if(cmd != 0) + return 1; + + // check file + if(!checkFile(Filename)) + return 1; + + // re-load ips + cmd = loadIptables(Filename); + if(cmd != 0) + return 1; + + /// print iptables content to screen (check for root, iptables needs root) + } else if(Choice == "-L") + { + if(checkRoot() == false) + return 1; + + cmd = listIptables(); + if(cmd != 0) + return 1; + + /// flush iptables (check for root, iptables needs root) + } else if(Choice == "-F") + { + if(checkRoot() == false) + return 1; + + cmd = flushIptables(); + if(cmd != 0) + return 1; + + /// print help screen (no root required) + } else if(Choice == "-h") + { + printHelp(true); + } else + { + printHelp(false); + } + + /// exit program with exit code 0 + return 0; +} + +/** \brief checks for superuser + * + * \return bool + * + */ +inline bool checkRoot() +{ + if(getuid() != 0) + { + cout << "you must be root for this operation" << endl; + return false; + } + return true; +} + +/** \brief checks for iptables installation + * + * \return bool + * + */ +inline bool checkIptables() +{ + if(!fileExists(IPTABLES)) + { + cout << "could not find " << IPTABLES << endl; + return false; + } + return true; +} + +/** \brief checks if a file exists + * Source: http://stackoverflow.com/a/12774387 + * + * \param name const string& + * \return bool + * + */ +inline bool fileExists(const string &name) { + struct stat buffer; + return (stat (name.c_str(), &buffer) == 0); +} + +/** \brief prints a help text on screen (short or long) + * + * \param printAll bool + * \return void + * + */ +void printHelp(bool printAll) +{ + cout << "blacklist " << AutoVersion::FULLVERSION_STRING << ", "; + cout << AutoVersion::STATUS << endl; + cout << "Copyright (c) 2015 by Dominic Reich\n" << endl; + cout << "Usage: blacklist [options] " << endl; + if(printAll == true) + { + cout << "The blacklist file is located at: " << DEFAULT_FILENAME << endl; + cout << "Options: -a add ip-address (to file)" << endl; + cout << " -d delete ip-address (from file)" << endl; + cout << " -l list ip-addresses (from file)" << endl; + cout << " -f find ip-address in file" << endl; + cout << " -c check file (sort and remove dulicates)" << endl; + cout << " -r reload (check file, flush iptables, load ips from file)" << endl; + cout << " -C count ip-addresses (from file)" << endl; + cout << " -L list ip-addresses (from iptables)" << endl; + cout << " -F flush (iptables)" << endl; + } + +} + +/** \brief checks file for duplicates and sorts it + * + * \param filename const char* + * \return bool + * + */ +inline bool checkFile(const char *filename) +{ + fstream File; + + File.open(filename, ios::in); + if(!File.is_open()) + { + return false; + } + + string line = ""; + vector ips; + + while(getline(File, line)) + { + ips.push_back(line.c_str()); + } + + boost::erase(ips, boost::unique(boost::sort(ips))); + + File.close(); + + File.open(filename, ios::out | ios::trunc); + if(!File.is_open()) + { + return false; + } + + for(size_t i = 0; i < ips.size(); i++) + { + File << ips[i] << endl; + } + + File.close(); + + return true; +} + +/** \brief prints content of file to screen + * + * \param filename const char* + * \return bool + * + */ +inline bool listFile(const char *filename) +{ + ifstream inFile(filename); + if(!inFile.is_open()) + { + //cout << "could not open file " << filename << endl; + return false; + } + + string line = ""; + while(getline(inFile, line)) + { + cout << line << endl; + } + inFile.close(); + return true; +} + +/** \brief sets iptables chain BLACKLIST up with ips from file + * + * \param filename const char* + * \return int + * + */ +int loadIptables(const char *filename) +{ + ifstream File(filename); + if(!File.is_open()) + { + //cout << "could not open file " << filename << endl; + return 1; + } + + int cmd = 0; + string line = ""; + while(getline(File, line)) + { + cmd = addIpToIptables(line); + if(cmd != 0) + { + return cmd; + } + } + File.close(); + + return 0; +} + +/** \brief flushes iptables chain BLACKLIST + * + * \return int + * + */ +int flushIptables() +{ + string cmd_iptables = IPTABLES; + int cmd = 0; + cmd = system((cmd_iptables+" -F BLACKLIST").c_str()); + + return cmd; +} + + +/** \brief prints iptables content to screen + * + * \return int + * + */ +int listIptables() +{ + string cmd_iptables = IPTABLES; + int cmd = 0; + cmd = system((cmd_iptables+" -L BLACKLIST -vn").c_str()); + + return cmd; +} + +/** \brief adds ip to iptables chain BLACKLIST + * + * \param ip string + * \return int + * + */ +int addIpToIptables(string ip) +{ + string cmd_iptables = IPTABLES; + int cmd = 0; + cmd = checkIp(ip); + if(cmd != 0) + { + return cmd; + } + + // for now we don't add a destination rule as we normally won't send + // something if the host is unable to talk to us + //cmd = system((cmd_iptables+" -A BLACKLIST -d "+ip+"/32 -j DROP").c_str()); + //if(cmd != 0) + //{ + // return cmd; + //} + + // original line + //cmd = system((cmd_iptables+" -A BLACKLIST -s "+ip+"/32 -j DROP").c_str()); + cmd = system((cmd_iptables+" -A BLACKLIST -s "+ip+" -j DROP").c_str()); + if(cmd != 0) + { + return cmd; + } + + return 0; +} + +/** \brief checks for a valid ip-address + * + * \param ip string + * \return int + * + */ +int checkIp(string ip) +{ + boost::system::error_code ec; + boost::asio::ip::address::from_string(ip, ec); + if(ec) + { + cout << ec.message() << endl; + return ec.value(); + } + + return 0; +} + +/** \brief adds ip-address to file + * + * \param filename const char* + * \param ip string + * \return bool + * + */ +inline bool addIpToFile(const char *filename, string ip) +{ + int cmd = 0; + cmd = checkIp(ip); + if(cmd != 0) + { + return false; + } + + ofstream File(filename, ios::app); + if(!File.is_open()) + { + return false; + } + File << ip << endl; + File.close(); + + // disable auto-check for now + //checkFile(Filename); + + return true; +} + +/** \brief removes ip-address from file + * + * \param filename const char* + * \param ip string + * \return bool + * + */ +inline bool removeIpFromFile(const char *filename, string ip) +{ + int cmd = 0; + cmd = checkIp(ip); + if(cmd != 0) + { + return false; + } + + if(!findIpInFile(filename, ip)) + { + return false; + } + + const char *tmpFile = TMP_FILENAME; + + ifstream inFile(filename); + if(!inFile.is_open()) + { + return false; + } + + ofstream outFile(tmpFile, ios::out); + if(!outFile.is_open()) + { + return false; + } + + string line = ""; + while(getline(inFile, line)) + { + if(boost::iequals(ip, line) == false) + { + outFile << line << endl; + } + } + inFile.close(); + outFile.close(); + + ifstream a(tmpFile); + if(!a.is_open()) + { + return false; + } + + ofstream b(filename, ios::out); + if(!b.is_open()) + { + return false; + } + + b << a.rdbuf(); + + a.close(); + b.close(); + + remove(tmpFile); + + // TODO: Read file, load every line into an array + // search ip in that array and remove that array element + // write array elements into file (without appending, just create new one) + return true; +} + +/** \brief Counts ip-addresses in file + * + * \param filename const char* + * \return bool + * + */ +inline bool countIpAdresses(const char *filename) +{ + int Count = 0; + + ifstream File(filename); + if(!File.is_open()) + { + //cout << "could not open file " << filename << endl; + return false; + } + + string line = ""; + while(getline(File, line)) + { + Count++; + } + File.close(); + + //cout << "There are currently " << Count << "IP Adresses blacklisted." << endl; + // as when we want to use that number to calculate something, only print the count + cout << Count << endl; + return true; +} + +/** \brief Finds an ip-address in file + * + * \param filename const char* + * \param ip string + * \return bool + * + */ +inline bool findIpInFile(const char *filename, string ip) +{ + ifstream File(filename); + if(!File.is_open()) + { + return false; + } + + string line = ""; + while(getline(File, line)) + { + if(boost::iequals(ip, line) == true) + { + //cout << ip << " found in " << filename << "!" << endl; + return true; + } + } + File.close(); + return false; +} + +/* vim: set ts=2 sw=2 tw=0 et :*/ diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..71b6655 --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +#!/bin/sh +g++ -O2 -pipe -fomit-frame-pointer -Wall -s -pie -fpie -lboost_system -o blacklist blacklist.cpp +sudo cp blacklist /usr/local/bin/ diff --git a/version.h b/version.h new file mode 100644 index 0000000..bd172de --- /dev/null +++ b/version.h @@ -0,0 +1,34 @@ +#ifndef VERSION_H +#define VERSION_H + +namespace AutoVersion{ + + //Date Version Types + static const char DATE[] = "06"; + static const char MONTH[] = "06"; + static const char YEAR[] = "2015"; + static const char UBUNTU_VERSION_STYLE[] = "15.06"; + + //Software Status + static const char STATUS[] = "Release Candidate [raspberry port]"; + static const char STATUS_SHORT[] = "rc"; + + //Standard Version Type + static const long MAJOR = 0; + static const long MINOR = 3; + static const long BUILD = 56; + static const long REVISION = 276; + + //Miscellaneous Version Types + static const long BUILDS_COUNT = 56; + #define RC_FILEVERSION 0,3,56,276 + #define RC_FILEVERSION_STRING "0, 3, 56, 276\0" + static const char FULLVERSION_STRING [] = "0.3.56.276"; + + //These values are to keep track of your versioning state, don't modify them. + static const long BUILD_HISTORY = 56; + +} +#endif //VERSION_H + +/* vim: set ts=2 sw=2 tw=0 et :*/