├── INSTALL ├── Makefile ├── README.md ├── TESTING ├── argtable ├── arg_end.c ├── arg_file.c ├── arg_int.c ├── arg_lit.c ├── arg_rem.c ├── arg_str.c ├── argtable2.c └── argtable2.h ├── base64.cpp ├── base64.h ├── common ├── defines.h ├── includes.h └── syscall_wrappers.h ├── conntrack.c ├── conntrack.h ├── gui ├── frontend.ui ├── gui.py ├── popup.ui ├── resource.qrc └── resource_rc.py ├── lpfw.cpp ├── lpfw.h ├── sha256 ├── sha256.c ├── sha256.h └── u64.h ├── testmain.cpp └── testprocess.cpp /INSTALL: -------------------------------------------------------------------------------- 1 | INSTALLATION INSTRUCTIONS 2 | 3 | See README.md 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | g++FLAGS = -g -fpermissive -std=c++11 -Wfatal-errors 2 | 3 | all: lpfw testprocess 4 | 5 | lpfw: sha256.o base64.o conntrack.o testmain.o \ 6 | argtable2.o arg_end.o arg_file.o arg_int.o arg_lit.o arg_rem.o arg_str.o \ 7 | lpfw.cpp lpfw.h common/defines.h common/includes.h 8 | g++ $(g++FLAGS) sha256.o base64.o conntrack.o testmain.o \ 9 | argtable2.o arg_end.o arg_file.o arg_int.o arg_lit.o arg_rem.o arg_str.o \ 10 | lpfw.cpp -lnetfilter_queue -lnetfilter_conntrack -lpthread -lcap -o lpfw 11 | 12 | sha256.o : sha256/sha256.c sha256/sha256.h sha256/u64.h 13 | g++ $(g++FLAGS) -c sha256/sha256.c 14 | base64.o : base64.cpp base64.h 15 | g++ $(g++FLAGS) -c base64.cpp 16 | conntrack.o : conntrack.c conntrack.h 17 | g++ $(g++FLAGS) -c conntrack.c 18 | argtable2.o : argtable/argtable2.c 19 | g++ $(g++FLAGS) -c argtable/argtable2.c 20 | arg_end.o : argtable/arg_end.c 21 | g++ $(g++FLAGS) -c argtable/arg_end.c 22 | arg_file.o : argtable/arg_file.c 23 | g++ $(g++FLAGS) -c argtable/arg_file.c 24 | arg_int.o : argtable/arg_int.c 25 | g++ $(g++FLAGS) -c argtable/arg_int.c 26 | arg_lit.o : argtable/arg_lit.c 27 | g++ $(g++FLAGS) -c argtable/arg_lit.c 28 | arg_rem.o : argtable/arg_rem.c 29 | g++ $(g++FLAGS) -c argtable/arg_rem.c 30 | arg_str.o : argtable/arg_str.c 31 | g++ $(g++FLAGS) -c argtable/arg_str.c 32 | testmain.o : testmain.cpp 33 | g++ $(g++FLAGS) -c testmain.cpp 34 | 35 | 36 | testprocess: testprocess.cpp 37 | g++ -g -std=c++11 testprocess.cpp -lpthread -o testprocess 38 | 39 | 40 | clean : 41 | rm -f testprocess *.o lpfw 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | UPDATE October 2020. I no longer plan to contribute to this project because a better alternative with an active maintainer and community has appeared over the last years. I recommend using OpenSnitch ( https://github.com/evilsocket/opensnitch ) which uses similar techniques to LPFW. I plan to contribute to that project. You can still use LPFW, it works but it may be rough around the edges. 2 | 3 | 4 | # Leopard Flower personal firewall for Linux (LPFW) 5 | 6 | LPFW gives the user control over which applications are allowed to use the 7 | network. 8 | It comes with a GUI. 9 | 10 | These instructions apply specifically to Ubuntu 14.04 64-bit but are very 11 | likely to work on other Linux distributions. 12 | Please note that on 32-bit Linuxes lpfw may function incorrectly. 13 | 14 | Install all dependencies: 15 | 16 | ```Shell 17 | sudo apt-get install make g++ libnetfilter-queue-dev libnetfilter-conntrack-dev libcap-dev python-qt4 18 | ``` 19 | 20 | Compile: 21 | ```Shell 22 | make 23 | ``` 24 | 25 | Quick start: 26 | Run `lpfw` as root. Run python `gui/gui.py` as regular user. 27 | 28 | ## Command line arguments 29 | 30 | These can be also seen with `lpfw --help`. 31 | 32 | --rules-file= 33 | File to which rules are commited (default: /etc/lpfw.rules) 34 | 35 | --logging_facility= 36 | Where to write logs. Possible values stdout(default), file, syslog 37 | 38 | --log-file= 39 | If --logging_facility=file, then this is the file to which to write logging information. Default /tmp/lpfw.log 40 | 41 | --pid-file= 42 | Pidfile which prevents two instances of lpfw being launched at the same time. Default /var/log/lpfw.pid 43 | 44 | --log-info= 45 | --log-traffic= 46 | --log-debug= 47 | Enables different levels of logging. Possible values 1 or 0 for yes/no. Default: all three 1. 48 | 49 | ## Known issues 50 | 51 | * Only IPv4. 52 | * Only TCP and UDP. LPFW will drop any other protocol packets. To prevent dropping, add a rule which must preceed the LPFW's NFQUEUE rules, e.g. 53 | * iptables -I OUTPUT 1 -p udplite -j ACCEPT 54 | * If LPFW crashes, run "sudo iptables -F" to disable all rules. 55 | 56 | 57 | 58 | ## The rest of this file's contents is technical information for system administrators and advanced users 59 | 60 | ### Traffic logging format 61 | 62 | An example of traffic log's line: 63 | 64 | " incoming 68 | 2. Protocol type UDP / TCP 69 | 3. 70 | 4. IP address of remote machine 71 | 5. port of remote machine 72 | 6. 73 | 7. local port 74 | 8. Path to the executable which initiated the packet or for which the packet was destined 75 | 9. Process ID of the executable 76 | 10. Action taken by LPFW with regard to this packet 77 | 78 | 79 | ### Architecture 80 | 81 | LeopardFlower (LPFW) utilizes a facility provided by netfilter whereby all outgoing and incoming packets which initiate a new connection are delivered to LPFW for decision on whether to drop them or accept them. LPFW sets up a rule with iptables similar to 82 | `iptables -A OUTPUT -j NFQUEUE --queue-num 11220` 83 | and installs a callback (using libnetfilter_queue) which is notified whenever a packet hits the NFQUEUE (NFQ). The fact that LPFW doesn't need to process every single packet but only those which initiate new connections, significantly decreases LPFW's CPU consumption. 84 | 85 | Upon start up, LPFW read a rules file which contains Internet access permissions per application. Based upon these rules, whenever a new packet hits NFQ, LPFW decides whether to allow or deny Internet access or whether to ask the user what to do if no rule for the application in question has yet been defined. 86 | 87 | In order to establish a correlation between a packet which hit nfq and the application which sent it, LPFW does the following: 88 | 89 | 1. For an outgoing packet - extract source port (for an incoming packet - extract destination port) and look up in /proc/net/tcp to see which socket corresponds to the port. 90 | 2. Having found the socket, scan /proc//fd to see which process owns the socket 91 | 3 Finally extract the application name from /proc//exe 92 | 93 | LPFW sets a unique netfilter mark on all connections of a specific app. This enables LPFW to instantly halt all app's Internet activity if user chooses so. In order to set such a netfilter mark, LPFW uses libnetfilter_conntrack library. 94 | 95 | 96 | ## Security 97 | 98 | LPFW strips itself of all capabilities except the following: 99 | 100 | CAP_SYS_PTRACE (to readlink() root's links in /proc) 101 | CAP_NET_ADMIN (to use netfilter_queue and netfilter_conntrack) 102 | CAP_DAC_READ_SEARCH (to scan all users' /proc/ entries) 103 | 104 | See `man 7 capabilities` for more information on capabilities. 105 | -------------------------------------------------------------------------------- /TESTING: -------------------------------------------------------------------------------- 1 | THE INFO BELOW IS FOR DEVELOPERS ONLY. 2 | 3 | There is no need to fear that your machine's internet will be locked up when testing. 4 | We use iptables' -m owner GID match specifically for testing. 5 | 6 | In one console start: 7 | sudo gdbserver host:2345 lpfw --test 8 | 9 | (If you like GUI you can then attach to the remote debugger from qtcreator's debug menu) 10 | Use sudo ./lpfw --test to start the test. 11 | Sometimes zombie testprocesses may block the nfqueue and give errors, use sudo pkill -f testprocess 12 | 13 | --------------------------------------------------- 14 | You can monitor in real-time how busy is nfqueue: 15 | watch -n 0.1 'sudo cat /proc/net/netfilter/nfnetlink_queue' 16 | 17 | For example: 18 | 40 23948 0 2 65531 0 0 106 1 19 | 20 | queue number 21 | peer portid: good chance it is process ID of software listening to the queue 22 | queue total: number of queues if queue load balancing is used 23 | copy mode: 0 and 1 only message only provide meta data. If 2 message provide a part of packet of size copy range. 24 | copy range: length of packet data to put in message 25 | queue dropped: number of packets dropped because queue was full 26 | user dropped: number of packets dropped because netlink message could not be sent to userspace. If this counter is not zero, try to increase netlink buffer size. On the application side, you will see gap in packet id if netlink message are lost. 27 | id sequence: packet id of last packet 28 | 1 29 | 30 | Pay special attention to queue total - it should not exceed 200, otherwise packets will be dropped and tests fail. 31 | --------------------------------------------------- 32 | -------------------------------------------------------------------------------- /argtable/arg_end.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | This file is part of the argtable2 library. 3 | Copyright (C) 1998-2001,2003-2011 Stewart Heitmann 4 | sheitmann@users.sourceforge.net 5 | 6 | The argtable2 library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Library General Public License as 8 | published by the Free Software Foundation; either version 2 of the 9 | License, or (at your option) any later version. 10 | 11 | This software is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Library General Public License for more details. 15 | 16 | You should have received a copy of the GNU Library General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 19 | USA. 20 | **********************************************************************/ 21 | 22 | /* config.h must be included before anything else */ 23 | #ifdef HAVE_CONFIG_H 24 | #include "config.h" 25 | #endif 26 | 27 | /* #ifdef HAVE_STDLIB_H */ 28 | #include 29 | /* #endif */ 30 | 31 | #include "argtable2.h" 32 | 33 | static void resetfn(struct arg_end *parent) 34 | { 35 | /*printf("%s:resetfn(%p)\n",__FILE__,parent);*/ 36 | parent->count = 0; 37 | } 38 | 39 | static void errorfn(void *parent, FILE *fp, int error, const char *argval, const char *progname) 40 | { 41 | progname = progname ? progname : ""; 42 | argval = argval ? argval : ""; 43 | 44 | fprintf(fp,"%s: ",progname); 45 | switch(error) 46 | { 47 | case ARG_ELIMIT: 48 | fputs("too many errors to display",fp); 49 | break; 50 | case ARG_EMALLOC: 51 | fputs("insufficent memory",fp); 52 | break; 53 | case ARG_ENOMATCH: 54 | fprintf(fp,"unexpected argument \"%s\"",argval); 55 | break; 56 | case ARG_EMISSARG: 57 | fprintf(fp,"option \"%s\" requires an argument",argval); 58 | break; 59 | case ARG_ELONGOPT: 60 | fprintf(fp,"invalid option \"%s\"",argval); 61 | break; 62 | default: 63 | fprintf(fp,"invalid option \"-%c\"",error); 64 | break; 65 | } 66 | fputc('\n',fp); 67 | } 68 | 69 | 70 | struct arg_end* arg_end(int maxcount) 71 | { 72 | size_t nbytes; 73 | struct arg_end *result; 74 | 75 | nbytes = sizeof(struct arg_end) 76 | + maxcount * sizeof(int) /* storage for int error[maxcount] array*/ 77 | + maxcount * sizeof(void*) /* storage for void* parent[maxcount] array */ 78 | + maxcount * sizeof(char*); /* storage for char* argval[maxcount] array */ 79 | 80 | result = (struct arg_end*)malloc(nbytes); 81 | if (result) 82 | { 83 | /* init the arg_hdr struct */ 84 | result->hdr.flag = ARG_TERMINATOR; 85 | result->hdr.shortopts = NULL; 86 | result->hdr.longopts = NULL; 87 | result->hdr.datatype = NULL; 88 | result->hdr.glossary = NULL; 89 | result->hdr.mincount = 1; 90 | result->hdr.maxcount = maxcount; 91 | result->hdr.parent = result; 92 | result->hdr.resetfn = (arg_resetfn*)resetfn; 93 | result->hdr.scanfn = NULL; 94 | result->hdr.checkfn = NULL; 95 | result->hdr.errorfn = errorfn; 96 | 97 | /* store error[maxcount] array immediately after struct arg_end */ 98 | result->error = (int*)(result+1); 99 | 100 | /* store parent[maxcount] array immediately after error[] array */ 101 | result->parent = (void**)(result->error + maxcount ); 102 | 103 | /* store argval[maxcount] array immediately after parent[] array */ 104 | result->argval = (const char**)(result->parent + maxcount ); 105 | } 106 | 107 | /*printf("arg_end(%d) returns %p\n",maxcount,result);*/ 108 | return result; 109 | } 110 | 111 | 112 | void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname) 113 | { 114 | int i; 115 | /*printf("arg_errors()\n");*/ 116 | for (i=0; icount; i++) 117 | { 118 | struct arg_hdr *errorparent = (struct arg_hdr *)(end->parent[i]); 119 | if (errorparent->errorfn) 120 | errorparent->errorfn(end->parent[i],fp,end->error[i],end->argval[i],progname); 121 | } 122 | } 123 | 124 | 125 | -------------------------------------------------------------------------------- /argtable/arg_file.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | This file is part of the argtable2 library. 3 | Copyright (C) 1998-2001,2003-2011 Stewart Heitmann 4 | sheitmann@users.sourceforge.net 5 | 6 | The argtable2 library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Library General Public License as 8 | published by the Free Software Foundation; either version 2 of the 9 | License, or (at your option) any later version. 10 | 11 | This software is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Library General Public License for more details. 15 | 16 | You should have received a copy of the GNU Library General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 19 | USA. 20 | **********************************************************************/ 21 | 22 | /* config.h must be included before anything else */ 23 | #ifdef HAVE_CONFIG_H 24 | #include "config.h" 25 | #endif 26 | 27 | #ifdef HAVE_STRING_H 28 | #include 29 | #endif 30 | 31 | #ifdef HAVE_STDLIB_H 32 | #include 33 | #endif 34 | 35 | #include "argtable2.h" 36 | #include 37 | #include 38 | 39 | #ifdef WIN32 40 | # define FILESEPARATOR1 '\\' 41 | # define FILESEPARATOR2 '/' 42 | #else 43 | # define FILESEPARATOR1 '/' 44 | # define FILESEPARATOR2 '/' 45 | #endif 46 | 47 | /* local error codes */ 48 | enum {EMINCOUNT=1,EMAXCOUNT}; 49 | 50 | 51 | static void resetfn(struct arg_file *parent) 52 | { 53 | /*printf("%s:resetfn(%p)\n",__FILE__,parent);*/ 54 | parent->count=0; 55 | } 56 | 57 | 58 | /* Returns ptr to the base filename within *filename */ 59 | static const char* arg_basename(const char *filename) 60 | { 61 | const char *result=NULL, *result1, *result2; 62 | 63 | /* Find the last occurrence of eother file separator character. */ 64 | /* Two alternative file separator chars are supported as legal */ 65 | /* file separators but not both together in the same filename. */ 66 | result1 = (filename ? strrchr(filename,FILESEPARATOR1) : NULL); 67 | result2 = (filename ? strrchr(filename,FILESEPARATOR2) : NULL); 68 | 69 | if (result2) 70 | result=result2+1; /* using FILESEPARATOR2 (the alternative file separator) */ 71 | 72 | if (result1) 73 | result=result1+1; /* using FILESEPARATOR1 (the preferred file separator) */ 74 | 75 | if (!result) 76 | result = filename; /* neither file separator was found so basename is the whole filename */ 77 | 78 | /* special cases of "." and ".." are not considered basenames */ 79 | if (result && ( strcmp(".",result)==0 || strcmp("..",result)==0 )) 80 | result = filename + strlen(filename); 81 | 82 | return result; 83 | } 84 | 85 | 86 | /* Returns ptr to the file extension within *basename */ 87 | static const char* arg_extension(const char *basename) 88 | { 89 | /* find the last occurrence of '.' in basename */ 90 | const char *result = (basename ? strrchr(basename,'.') : NULL); 91 | 92 | /* if no '.' was found then return pointer to end of basename */ 93 | if (basename && !result) 94 | result = basename+strlen(basename); 95 | 96 | /* special case: basenames with a single leading dot (eg ".foo") are not considered as true extensions */ 97 | if (basename && result==basename) 98 | result = basename+strlen(basename); 99 | 100 | /* special case: empty extensions (eg "foo.","foo..") are not considered as true extensions */ 101 | if (basename && result && result[1]=='\0') 102 | result = basename+strlen(basename); 103 | 104 | return result; 105 | } 106 | 107 | 108 | static int scanfn(struct arg_file *parent, const char *argval) 109 | { 110 | int errorcode = 0; 111 | 112 | if (parent->count == parent->hdr.maxcount) 113 | { 114 | /* maximum number of arguments exceeded */ 115 | errorcode = EMAXCOUNT; 116 | } 117 | else if (!argval) 118 | { 119 | /* a valid argument with no argument value was given. */ 120 | /* This happens when an optional argument value was invoked. */ 121 | /* leave parent arguiment value unaltered but still count the argument. */ 122 | parent->count++; 123 | } 124 | else 125 | { 126 | parent->filename[parent->count] = argval; 127 | parent->basename[parent->count] = arg_basename(argval); 128 | parent->extension[parent->count] = arg_extension(parent->basename[parent->count]); /* only seek extensions within the basename (not the file path)*/ 129 | parent->count++; 130 | } 131 | 132 | /*printf("%s:scanfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ 133 | return errorcode; 134 | } 135 | 136 | 137 | static int checkfn(struct arg_file *parent) 138 | { 139 | int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; 140 | /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ 141 | return errorcode; 142 | } 143 | 144 | 145 | static void errorfn(struct arg_file *parent, FILE *fp, int errorcode, const char *argval, const char *progname) 146 | { 147 | const char *shortopts = parent->hdr.shortopts; 148 | const char *longopts = parent->hdr.longopts; 149 | const char *datatype = parent->hdr.datatype; 150 | 151 | /* make argval NULL safe */ 152 | argval = argval ? argval : ""; 153 | 154 | fprintf(fp,"%s: ",progname); 155 | switch(errorcode) 156 | { 157 | case EMINCOUNT: 158 | fputs("missing option ",fp); 159 | arg_print_option(fp,shortopts,longopts,datatype,"\n"); 160 | break; 161 | 162 | case EMAXCOUNT: 163 | fputs("excess option ",fp); 164 | arg_print_option(fp,shortopts,longopts,argval,"\n"); 165 | break; 166 | 167 | default: 168 | fprintf(fp,"unknown error at \"%s\"\n",argval); 169 | } 170 | } 171 | 172 | 173 | struct arg_file* arg_file0(const char* shortopts, 174 | const char* longopts, 175 | const char *datatype, 176 | const char *glossary) 177 | { 178 | return arg_filen(shortopts,longopts,datatype,0,1,glossary); 179 | } 180 | 181 | 182 | struct arg_file* arg_file1(const char* shortopts, 183 | const char* longopts, 184 | const char *datatype, 185 | const char *glossary) 186 | { 187 | return arg_filen(shortopts,longopts,datatype,1,1,glossary); 188 | } 189 | 190 | 191 | struct arg_file* arg_filen(const char* shortopts, 192 | const char* longopts, 193 | const char *datatype, 194 | int mincount, 195 | int maxcount, 196 | const char *glossary) 197 | { 198 | size_t nbytes; 199 | struct arg_file *result; 200 | 201 | /* foolproof things by ensuring maxcount is not less than mincount */ 202 | maxcount = (maxcounthdr.flag = ARG_HASVALUE; 216 | result->hdr.shortopts = shortopts; 217 | result->hdr.longopts = longopts; 218 | result->hdr.glossary = glossary; 219 | result->hdr.datatype = datatype ? datatype : ""; 220 | result->hdr.mincount = mincount; 221 | result->hdr.maxcount = maxcount; 222 | result->hdr.parent = result; 223 | result->hdr.resetfn = (arg_resetfn*)resetfn; 224 | result->hdr.scanfn = (arg_scanfn*)scanfn; 225 | result->hdr.checkfn = (arg_checkfn*)checkfn; 226 | result->hdr.errorfn = (arg_errorfn*)errorfn; 227 | 228 | /* store the filename,basename,extension arrays immediately after the arg_file struct */ 229 | result->filename = (const char**)(result+1); 230 | result->basename = result->filename + maxcount; 231 | result->extension = result->basename + maxcount; 232 | result->count = 0; 233 | 234 | /* foolproof the string pointers by initialising them with empty strings */ 235 | for (i=0; ifilename[i] = ""; 238 | result->basename[i] = ""; 239 | result->extension[i] = ""; 240 | } 241 | } 242 | /*printf("arg_filen() returns %p\n",result);*/ 243 | return result; 244 | } 245 | -------------------------------------------------------------------------------- /argtable/arg_int.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | This file is part of the argtable2 library. 3 | Copyright (C) 1998-2001,2003-2011 Stewart Heitmann 4 | sheitmann@users.sourceforge.net 5 | 6 | The argtable2 library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Library General Public License as 8 | published by the Free Software Foundation; either version 2 of the 9 | License, or (at your option) any later version. 10 | 11 | This software is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Library General Public License for more details. 15 | 16 | You should have received a copy of the GNU Library General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 19 | USA. 20 | **********************************************************************/ 21 | 22 | /* config.h must be included before anything else */ 23 | #ifdef HAVE_CONFIG_H 24 | #include "config.h" 25 | #endif 26 | 27 | /* #ifdef HAVE_STDLIB_H */ 28 | #include 29 | /* #endif */ 30 | 31 | #include "argtable2.h" 32 | #include 33 | #include 34 | #include 35 | 36 | /* local error codes */ 37 | enum {EMINCOUNT=1,EMAXCOUNT,EBADINT,EOVERFLOW}; 38 | 39 | static void resetfn(struct arg_int *parent) 40 | { 41 | /*printf("%s:resetfn(%p)\n",__FILE__,parent);*/ 42 | parent->count=0; 43 | } 44 | 45 | /* strtol0x() is like strtol() except that the numeric string is */ 46 | /* expected to be prefixed by "0X" where X is a user supplied char. */ 47 | /* The string may optionally be prefixed by white space and + or - */ 48 | /* as in +0X123 or -0X123. */ 49 | /* Once the prefix has been scanned, the remainder of the numeric */ 50 | /* string is converted using strtol() with the given base. */ 51 | /* eg: to parse hex str="-0X12324", specify X='X' and base=16. */ 52 | /* eg: to parse oct str="+0o12324", specify X='O' and base=8. */ 53 | /* eg: to parse bin str="-0B01010", specify X='B' and base=2. */ 54 | /* Failure of conversion is indicated by result where *endptr==str. */ 55 | static long int strtol0X(const char* str, const char **endptr, char X, int base) 56 | { 57 | long int val; /* stores result */ 58 | int s=1; /* sign is +1 or -1 */ 59 | const char *ptr=str; /* ptr to current position in str */ 60 | 61 | /* skip leading whitespace */ 62 | while (isspace(*ptr)) 63 | ptr++; 64 | /* printf("1) %s\n",ptr); */ 65 | 66 | /* scan optional sign character */ 67 | switch (*ptr) 68 | { 69 | case '+': 70 | ptr++; 71 | s=1; 72 | break; 73 | case '-': 74 | ptr++; 75 | s=-1; 76 | break; 77 | default: 78 | s=1; 79 | break; 80 | } 81 | /* printf("2) %s\n",ptr); */ 82 | 83 | /* '0X' prefix */ 84 | if ((*ptr++)!='0') 85 | { 86 | /* printf("failed to detect '0'\n"); */ 87 | *endptr=str; 88 | return 0; 89 | } 90 | /* printf("3) %s\n",ptr); */ 91 | if (toupper(*ptr++)!=toupper(X)) 92 | { 93 | /* printf("failed to detect '%c'\n",X); */ 94 | *endptr=str; 95 | return 0; 96 | } 97 | /* printf("4) %s\n",ptr); */ 98 | 99 | /* attempt conversion on remainder of string using strtol() */ 100 | val = strtol(ptr,(char**)endptr,base); 101 | if (*endptr==ptr) 102 | { 103 | /* conversion failed */ 104 | *endptr=str; 105 | return 0; 106 | } 107 | 108 | /* success */ 109 | return s*val; 110 | } 111 | 112 | 113 | /* Returns 1 if str matches suffix (case insensitive). */ 114 | /* Str may contain trailing whitespace, but nothing else. */ 115 | static int detectsuffix(const char *str, const char *suffix) 116 | { 117 | /* scan pairwise through strings until mismatch detected */ 118 | while( toupper(*str) == toupper(*suffix) ) 119 | { 120 | /* printf("'%c' '%c'\n", *str, *suffix); */ 121 | 122 | /* return 1 (success) if match persists until the string terminator */ 123 | if (*str=='\0') 124 | return 1; 125 | 126 | /* next chars */ 127 | str++; 128 | suffix++; 129 | } 130 | /* printf("'%c' '%c' mismatch\n", *str, *suffix); */ 131 | 132 | /* return 0 (fail) if the matching did not consume the entire suffix */ 133 | if (*suffix!=0) 134 | return 0; /* failed to consume entire suffix */ 135 | 136 | /* skip any remaining whitespace in str */ 137 | while (isspace(*str)) 138 | str++; 139 | 140 | /* return 1 (success) if we have reached end of str else return 0 (fail) */ 141 | return (*str=='\0') ? 1 : 0; 142 | } 143 | 144 | 145 | static int scanfn(struct arg_int *parent, const char *argval) 146 | { 147 | int errorcode = 0; 148 | 149 | if (parent->count == parent->hdr.maxcount) 150 | { 151 | /* maximum number of arguments exceeded */ 152 | errorcode = EMAXCOUNT; 153 | } 154 | else if (!argval) 155 | { 156 | /* a valid argument with no argument value was given. */ 157 | /* This happens when an optional argument value was invoked. */ 158 | /* leave parent arguiment value unaltered but still count the argument. */ 159 | parent->count++; 160 | } 161 | else 162 | { 163 | long int val; 164 | const char *end; 165 | 166 | /* attempt to extract hex integer (eg: +0x123) from argval into val conversion */ 167 | val = strtol0X(argval, &end, 'X', 16); 168 | if (end==argval) 169 | { 170 | /* hex failed, attempt octal conversion (eg +0o123) */ 171 | val = strtol0X(argval, &end, 'O', 8); 172 | if (end==argval) 173 | { 174 | /* octal failed, attempt binary conversion (eg +0B101) */ 175 | val = strtol0X(argval, &end, 'B', 2); 176 | if (end==argval) 177 | { 178 | /* binary failed, attempt decimal conversion with no prefix (eg 1234) */ 179 | val = strtol(argval, (char**)&end, 10); 180 | if (end==argval) 181 | { 182 | /* all supported number formats failed */ 183 | return EBADINT; 184 | } 185 | } 186 | } 187 | } 188 | 189 | /* Safety check for integer overflow. WARNING: this check */ 190 | /* achieves nothing on machines where size(int)==size(long). */ 191 | if ( val>INT_MAX || val(INT_MAX/1024) || val<(INT_MIN/1024) ) 199 | errorcode = EOVERFLOW; /* Overflow would occur if we proceed */ 200 | else 201 | val*=1024; /* 1KB = 1024 */ 202 | } 203 | else if (detectsuffix(end,"MB")) /* megabytes */ 204 | { 205 | if ( val>(INT_MAX/1048576) || val<(INT_MIN/1048576) ) 206 | errorcode = EOVERFLOW; /* Overflow would occur if we proceed */ 207 | else 208 | val*=1048576; /* 1MB = 1024*1024 */ 209 | } 210 | else if (detectsuffix(end,"GB")) /* gigabytes */ 211 | { 212 | if ( val>(INT_MAX/1073741824) || val<(INT_MIN/1073741824) ) 213 | errorcode = EOVERFLOW; /* Overflow would occur if we proceed */ 214 | else 215 | val*=1073741824; /* 1GB = 1024*1024*1024 */ 216 | } 217 | else if (!detectsuffix(end,"")) 218 | errorcode = EBADINT; /* invalid suffix detected */ 219 | 220 | /* if success then store result in parent->ival[] array */ 221 | if (errorcode==0) 222 | parent->ival[parent->count++] = val; 223 | } 224 | 225 | /* printf("%s:scanfn(%p,%p) returns %d\n",__FILE__,parent,argval,errorcode); */ 226 | return errorcode; 227 | } 228 | 229 | static int checkfn(struct arg_int *parent) 230 | { 231 | int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; 232 | /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ 233 | return errorcode; 234 | } 235 | 236 | static void errorfn(struct arg_int *parent, FILE *fp, int errorcode, const char *argval, const char *progname) 237 | { 238 | const char *shortopts = parent->hdr.shortopts; 239 | const char *longopts = parent->hdr.longopts; 240 | const char *datatype = parent->hdr.datatype; 241 | 242 | /* make argval NULL safe */ 243 | argval = argval ? argval : ""; 244 | 245 | fprintf(fp,"%s: ",progname); 246 | switch(errorcode) 247 | { 248 | case EMINCOUNT: 249 | fputs("missing option ",fp); 250 | arg_print_option(fp,shortopts,longopts,datatype,"\n"); 251 | break; 252 | 253 | case EMAXCOUNT: 254 | fputs("excess option ",fp); 255 | arg_print_option(fp,shortopts,longopts,argval,"\n"); 256 | break; 257 | 258 | case EBADINT: 259 | fprintf(fp,"invalid argument \"%s\" to option ",argval); 260 | arg_print_option(fp,shortopts,longopts,datatype,"\n"); 261 | break; 262 | 263 | case EOVERFLOW: 264 | fputs("integer overflow at option ",fp); 265 | arg_print_option(fp,shortopts,longopts,datatype," "); 266 | fprintf(fp,"(%s is too large)\n",argval); 267 | break; 268 | } 269 | } 270 | 271 | 272 | struct arg_int* arg_int0(const char* shortopts, 273 | const char* longopts, 274 | const char *datatype, 275 | const char *glossary) 276 | { 277 | return arg_intn(shortopts,longopts,datatype,0,1,glossary); 278 | } 279 | 280 | struct arg_int* arg_int1(const char* shortopts, 281 | const char* longopts, 282 | const char *datatype, 283 | const char *glossary) 284 | { 285 | return arg_intn(shortopts,longopts,datatype,1,1,glossary); 286 | } 287 | 288 | 289 | struct arg_int* arg_intn(const char* shortopts, 290 | const char* longopts, 291 | const char *datatype, 292 | int mincount, 293 | int maxcount, 294 | const char *glossary) 295 | { 296 | size_t nbytes; 297 | struct arg_int *result; 298 | 299 | /* foolproof things by ensuring maxcount is not less than mincount */ 300 | maxcount = (maxcounthdr.flag = ARG_HASVALUE; 310 | result->hdr.shortopts = shortopts; 311 | result->hdr.longopts = longopts; 312 | result->hdr.datatype = datatype ? datatype : ""; 313 | result->hdr.glossary = glossary; 314 | result->hdr.mincount = mincount; 315 | result->hdr.maxcount = maxcount; 316 | result->hdr.parent = result; 317 | result->hdr.resetfn = (arg_resetfn*)resetfn; 318 | result->hdr.scanfn = (arg_scanfn*)scanfn; 319 | result->hdr.checkfn = (arg_checkfn*)checkfn; 320 | result->hdr.errorfn = (arg_errorfn*)errorfn; 321 | 322 | /* store the ival[maxcount] array immediately after the arg_int struct */ 323 | result->ival = (int*)(result+1); 324 | result->count = 0; 325 | } 326 | /*printf("arg_intn() returns %p\n",result);*/ 327 | return result; 328 | } 329 | -------------------------------------------------------------------------------- /argtable/arg_lit.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | This file is part of the argtable2 library. 3 | Copyright (C) 1998-2001,2003-2011 Stewart Heitmann 4 | sheitmann@users.sourceforge.net 5 | 6 | The argtable2 library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Library General Public License as 8 | published by the Free Software Foundation; either version 2 of the 9 | License, or (at your option) any later version. 10 | 11 | This software is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Library General Public License for more details. 15 | 16 | You should have received a copy of the GNU Library General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 19 | USA. 20 | **********************************************************************/ 21 | 22 | /* config.h must be included before anything else */ 23 | #ifdef HAVE_CONFIG_H 24 | #include "config.h" 25 | #endif 26 | 27 | #ifdef HAVE_STDLIB_H 28 | #include 29 | #endif 30 | 31 | #include 32 | #include 33 | #include "argtable2.h" 34 | 35 | /* local error codes */ 36 | enum {EMINCOUNT=1,EMAXCOUNT}; 37 | 38 | static void resetfn(struct arg_lit *parent) 39 | { 40 | /*printf("%s:resetfn(%p)\n",__FILE__,parent);*/ 41 | parent->count = 0; 42 | } 43 | 44 | static int scanfn(struct arg_lit *parent, const char *argval) 45 | { 46 | int errorcode = 0; 47 | if (parent->count < parent->hdr.maxcount ) 48 | parent->count++; 49 | else 50 | errorcode = EMAXCOUNT; 51 | /*printf("%s:scanfn(%p,%s) returns %d\n",__FILE__,parent,argval,errorcode);*/ 52 | return errorcode; 53 | } 54 | 55 | static int checkfn(struct arg_lit *parent) 56 | { 57 | int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; 58 | /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ 59 | return errorcode; 60 | } 61 | 62 | static void errorfn(struct arg_lit *parent, FILE *fp, int errorcode, const char *argval, const char *progname) 63 | { 64 | const char *shortopts = parent->hdr.shortopts; 65 | const char *longopts = parent->hdr.longopts; 66 | const char *datatype = parent->hdr.datatype; 67 | 68 | switch(errorcode) 69 | { 70 | case EMINCOUNT: 71 | fprintf(fp,"%s: missing option ",progname); 72 | arg_print_option(fp,shortopts,longopts,datatype,"\n"); 73 | fprintf(fp,"\n"); 74 | break; 75 | 76 | case EMAXCOUNT: 77 | fprintf(fp,"%s: extraneous option ",progname); 78 | arg_print_option(fp,shortopts,longopts,datatype,"\n"); 79 | break; 80 | } 81 | } 82 | 83 | struct arg_lit* arg_lit0(const char* shortopts, 84 | const char* longopts, 85 | const char* glossary) 86 | {return arg_litn(shortopts,longopts,0,1,glossary);} 87 | 88 | struct arg_lit* arg_lit1(const char* shortopts, 89 | const char* longopts, 90 | const char* glossary) 91 | {return arg_litn(shortopts,longopts,1,1,glossary);} 92 | 93 | 94 | struct arg_lit* arg_litn(const char* shortopts, 95 | const char* longopts, 96 | int mincount, 97 | int maxcount, 98 | const char *glossary) 99 | { 100 | struct arg_lit *result; 101 | 102 | /* foolproof things by ensuring maxcount is not less than mincount */ 103 | maxcount = (maxcounthdr.flag = 0; 110 | result->hdr.shortopts = shortopts; 111 | result->hdr.longopts = longopts; 112 | result->hdr.datatype = NULL; 113 | result->hdr.glossary = glossary; 114 | result->hdr.mincount = mincount; 115 | result->hdr.maxcount = maxcount; 116 | result->hdr.parent = result; 117 | result->hdr.resetfn = (arg_resetfn*)resetfn; 118 | result->hdr.scanfn = (arg_scanfn*)scanfn; 119 | result->hdr.checkfn = (arg_checkfn*)checkfn; 120 | result->hdr.errorfn = (arg_errorfn*)errorfn; 121 | 122 | /* init local variables */ 123 | result->count = 0; 124 | } 125 | /*printf("arg_litn() returns %p\n",result);*/ 126 | return result; 127 | } 128 | -------------------------------------------------------------------------------- /argtable/arg_rem.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | This file is part of the argtable2 library. 3 | Copyright (C) 1998-2001,2003-2011 Stewart Heitmann 4 | sheitmann@users.sourceforge.net 5 | 6 | The argtable2 library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Library General Public License as 8 | published by the Free Software Foundation; either version 2 of the 9 | License, or (at your option) any later version. 10 | 11 | This software is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Library General Public License for more details. 15 | 16 | You should have received a copy of the GNU Library General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 19 | USA. 20 | **********************************************************************/ 21 | 22 | /* config.h must be included before anything else */ 23 | #ifdef HAVE_CONFIG_H 24 | #include "config.h" 25 | #endif 26 | 27 | /*#ifdef HAVE_STDLIB_H*/ 28 | #include 29 | /*#endif*/ 30 | 31 | #include "argtable2.h" 32 | 33 | struct arg_rem* arg_rem(const char *datatype, 34 | const char *glossary) 35 | { 36 | struct arg_rem *result = (struct arg_rem*)malloc(sizeof(struct arg_rem)); 37 | if (result) 38 | { 39 | /* init the arg_hdr struct */ 40 | result->hdr.flag = 0; 41 | result->hdr.shortopts = NULL; 42 | result->hdr.longopts = NULL; 43 | result->hdr.datatype = datatype; 44 | result->hdr.glossary = glossary; 45 | result->hdr.mincount = 1; 46 | result->hdr.maxcount = 1; 47 | result->hdr.parent = result; 48 | result->hdr.resetfn = NULL; 49 | result->hdr.scanfn = NULL; 50 | result->hdr.checkfn = NULL; 51 | result->hdr.errorfn = NULL; 52 | } 53 | /*printf("arg_rem() returns %p\n",result);*/ 54 | return result; 55 | } 56 | -------------------------------------------------------------------------------- /argtable/arg_str.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | This file is part of the argtable2 library. 3 | Copyright (C) 1998-2001,2003-2011 Stewart Heitmann 4 | sheitmann@users.sourceforge.net 5 | 6 | The argtable2 library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Library General Public License as 8 | published by the Free Software Foundation; either version 2 of the 9 | License, or (at your option) any later version. 10 | 11 | This software is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Library General Public License for more details. 15 | 16 | You should have received a copy of the GNU Library General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 19 | USA. 20 | **********************************************************************/ 21 | 22 | /* config.h must be included before anything else */ 23 | #ifdef HAVE_CONFIG_H 24 | #include "config.h" 25 | #endif 26 | 27 | #ifdef HAVE_STDLIB_H 28 | #include 29 | #endif 30 | 31 | #include 32 | #include 33 | #include "argtable2.h" 34 | 35 | /* local error codes */ 36 | enum {EMINCOUNT=1,EMAXCOUNT}; 37 | 38 | static void resetfn(struct arg_str *parent) 39 | { 40 | /*printf("%s:resetfn(%p)\n",__FILE__,parent);*/ 41 | parent->count=0; 42 | } 43 | 44 | static int scanfn(struct arg_str *parent, const char *argval) 45 | { 46 | int errorcode = 0; 47 | 48 | if (parent->count == parent->hdr.maxcount) 49 | { 50 | /* maximum number of arguments exceeded */ 51 | errorcode = EMAXCOUNT; 52 | } 53 | else if (!argval) 54 | { 55 | /* a valid argument with no argument value was given. */ 56 | /* This happens when an optional argument value was invoked. */ 57 | /* leave parent arguiment value unaltered but still count the argument. */ 58 | parent->count++; 59 | } 60 | else 61 | { 62 | parent->sval[parent->count++] = argval; 63 | } 64 | 65 | /*printf("%s:scanfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ 66 | return errorcode; 67 | } 68 | 69 | static int checkfn(struct arg_str *parent) 70 | { 71 | int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; 72 | /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ 73 | return errorcode; 74 | } 75 | 76 | static void errorfn(struct arg_str *parent, FILE *fp, int errorcode, const char *argval, const char *progname) 77 | { 78 | const char *shortopts = parent->hdr.shortopts; 79 | const char *longopts = parent->hdr.longopts; 80 | const char *datatype = parent->hdr.datatype; 81 | 82 | /* make argval NULL safe */ 83 | argval = argval ? argval : ""; 84 | 85 | fprintf(fp,"%s: ",progname); 86 | switch(errorcode) 87 | { 88 | case EMINCOUNT: 89 | fputs("missing option ",fp); 90 | arg_print_option(fp,shortopts,longopts,datatype,"\n"); 91 | break; 92 | 93 | case EMAXCOUNT: 94 | fputs("excess option ",fp); 95 | arg_print_option(fp,shortopts,longopts,argval,"\n"); 96 | break; 97 | } 98 | } 99 | 100 | 101 | struct arg_str* arg_str0(const char* shortopts, 102 | const char* longopts, 103 | const char *datatype, 104 | const char *glossary) 105 | { 106 | return arg_strn(shortopts,longopts,datatype,0,1,glossary); 107 | } 108 | 109 | struct arg_str* arg_str1(const char* shortopts, 110 | const char* longopts, 111 | const char *datatype, 112 | const char *glossary) 113 | { 114 | return arg_strn(shortopts,longopts,datatype,1,1,glossary); 115 | } 116 | 117 | 118 | struct arg_str* arg_strn(const char* shortopts, 119 | const char* longopts, 120 | const char *datatype, 121 | int mincount, 122 | int maxcount, 123 | const char *glossary) 124 | { 125 | size_t nbytes; 126 | struct arg_str *result; 127 | 128 | /* foolproof things by ensuring maxcount is not less than mincount */ 129 | maxcount = (maxcounthdr.flag = ARG_HASVALUE; 141 | result->hdr.shortopts = shortopts; 142 | result->hdr.longopts = longopts; 143 | result->hdr.datatype = datatype ? datatype : ""; 144 | result->hdr.glossary = glossary; 145 | result->hdr.mincount = mincount; 146 | result->hdr.maxcount = maxcount; 147 | result->hdr.parent = result; 148 | result->hdr.resetfn = (arg_resetfn*)resetfn; 149 | result->hdr.scanfn = (arg_scanfn*)scanfn; 150 | result->hdr.checkfn = (arg_checkfn*)checkfn; 151 | result->hdr.errorfn = (arg_errorfn*)errorfn; 152 | 153 | /* store the sval[maxcount] array immediately after the arg_str struct */ 154 | result->sval = (const char**)(result+1); 155 | result->count = 0; 156 | 157 | /* foolproof the string pointers by initialising them to reference empty strings */ 158 | for (i=0; isval[i] = ""; } 160 | } 161 | /*printf("arg_strn() returns %p\n",result);*/ 162 | return result; 163 | } 164 | -------------------------------------------------------------------------------- /argtable/argtable2.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | This file is part of the argtable2 library. 3 | Copyright (C) 1998-2001,2003-2011 Stewart Heitmann 4 | sheitmann@users.sourceforge.net 5 | 6 | The argtable2 library is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Library General Public License as 8 | published by the Free Software Foundation; either version 2 of the 9 | License, or (at your option) any later version. 10 | 11 | This software is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Library General Public License for more details. 15 | 16 | You should have received a copy of the GNU Library General Public 17 | License along with this library; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 19 | USA. 20 | **********************************************************************/ 21 | #ifndef ARGTABLE2 22 | #define ARGTABLE2 23 | 24 | #include /* FILE */ 25 | #include /* struct tm */ 26 | #include 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | 33 | /* bit masks for arg_hdr.flag */ 34 | enum 35 | { 36 | ARG_TERMINATOR=0x1, 37 | ARG_HASVALUE=0x2, 38 | ARG_HASOPTVALUE=0x4 39 | }; 40 | 41 | typedef void (arg_resetfn)(void *parent); 42 | typedef int (arg_scanfn)(void *parent, const char *argval); 43 | typedef int (arg_checkfn)(void *parent); 44 | typedef void (arg_errorfn)(void *parent, FILE *fp, int error, const char *argval, const char *progname); 45 | 46 | 47 | /* 48 | * The arg_hdr struct defines properties that are common to all arg_xxx structs. 49 | * The argtable library requires each arg_xxx struct to have an arg_hdr 50 | * struct as its first data member. 51 | * The argtable library functions then use this data to identify the 52 | * properties of the command line option, such as its option tags, 53 | * datatype string, and glossary strings, and so on. 54 | * Moreover, the arg_hdr struct contains pointers to custom functions that 55 | * are provided by each arg_xxx struct which perform the tasks of parsing 56 | * that particular arg_xxx arguments, performing post-parse checks, and 57 | * reporting errors. 58 | * These functions are private to the individual arg_xxx source code 59 | * and are the pointer to them are initiliased by that arg_xxx struct's 60 | * constructor function. The user could alter them after construction 61 | * if desired, but the original intention is for them to be set by the 62 | * constructor and left unaltered. 63 | */ 64 | struct arg_hdr 65 | { 66 | char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */ 67 | const char *shortopts; /* String defining the short options */ 68 | const char *longopts; /* String defiing the long options */ 69 | const char *datatype; /* Description of the argument data type */ 70 | const char *glossary; /* Description of the option as shown by arg_print_glossary function */ 71 | int mincount; /* Minimum number of occurences of this option accepted */ 72 | int maxcount; /* Maximum number of occurences if this option accepted */ 73 | void *parent; /* Pointer to parent arg_xxx struct */ 74 | arg_resetfn *resetfn; /* Pointer to parent arg_xxx reset function */ 75 | arg_scanfn *scanfn; /* Pointer to parent arg_xxx scan function */ 76 | arg_checkfn *checkfn; /* Pointer to parent arg_xxx check function */ 77 | arg_errorfn *errorfn; /* Pointer to parent arg_xxx error function */ 78 | void *priv; /* Pointer to private header data for use by arg_xxx functions */ 79 | }; 80 | 81 | struct arg_rem 82 | { 83 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 84 | }; 85 | 86 | struct arg_lit 87 | { 88 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 89 | int count; /* Number of matching command line args */ 90 | }; 91 | 92 | struct arg_int 93 | { 94 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 95 | int count; /* Number of matching command line args */ 96 | int *ival; /* Array of parsed argument values */ 97 | }; 98 | 99 | struct arg_dbl 100 | { 101 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 102 | int count; /* Number of matching command line args */ 103 | double *dval; /* Array of parsed argument values */ 104 | }; 105 | 106 | struct arg_str 107 | { 108 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 109 | int count; /* Number of matching command line args */ 110 | const char **sval; /* Array of parsed argument values */ 111 | }; 112 | 113 | struct arg_rex 114 | { 115 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 116 | int count; /* Number of matching command line args */ 117 | const char **sval; /* Array of parsed argument values */ 118 | }; 119 | 120 | struct arg_file 121 | { 122 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 123 | int count; /* Number of matching command line args*/ 124 | const char **filename; /* Array of parsed filenames (eg: /home/foo.bar) */ 125 | const char **basename; /* Array of parsed basenames (eg: foo.bar) */ 126 | const char **extension; /* Array of parsed extensions (eg: .bar) */ 127 | }; 128 | 129 | struct arg_date 130 | { 131 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 132 | const char *format; /* strptime format string used to parse the date */ 133 | int count; /* Number of matching command line args */ 134 | struct tm *tmval; /* Array of parsed time values */ 135 | }; 136 | 137 | enum {ARG_ELIMIT=1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG}; 138 | struct arg_end 139 | { 140 | struct arg_hdr hdr; /* The mandatory argtable header struct */ 141 | int count; /* Number of errors encountered */ 142 | int *error; /* Array of error codes */ 143 | void **parent; /* Array of pointers to offending arg_xxx struct */ 144 | const char **argval; /* Array of pointers to offending argv[] string */ 145 | }; 146 | 147 | 148 | /**** arg_xxx constructor functions *********************************/ 149 | 150 | struct arg_rem* arg_rem(const char* datatype, const char* glossary); 151 | 152 | struct arg_lit* arg_lit0(const char* shortopts, 153 | const char* longopts, 154 | const char* glossary); 155 | struct arg_lit* arg_lit1(const char* shortopts, 156 | const char* longopts, 157 | const char *glossary); 158 | struct arg_lit* arg_litn(const char* shortopts, 159 | const char* longopts, 160 | int mincount, 161 | int maxcount, 162 | const char *glossary); 163 | 164 | struct arg_key* arg_key0(const char* keyword, 165 | int flags, 166 | const char* glossary); 167 | struct arg_key* arg_key1(const char* keyword, 168 | int flags, 169 | const char* glossary); 170 | struct arg_key* arg_keyn(const char* keyword, 171 | int flags, 172 | int mincount, 173 | int maxcount, 174 | const char* glossary); 175 | 176 | struct arg_int* arg_int0(const char* shortopts, 177 | const char* longopts, 178 | const char* datatype, 179 | const char* glossary); 180 | struct arg_int* arg_int1(const char* shortopts, 181 | const char* longopts, 182 | const char* datatype, 183 | const char *glossary); 184 | struct arg_int* arg_intn(const char* shortopts, 185 | const char* longopts, 186 | const char *datatype, 187 | int mincount, 188 | int maxcount, 189 | const char *glossary); 190 | 191 | struct arg_dbl* arg_dbl0(const char* shortopts, 192 | const char* longopts, 193 | const char* datatype, 194 | const char* glossary); 195 | struct arg_dbl* arg_dbl1(const char* shortopts, 196 | const char* longopts, 197 | const char* datatype, 198 | const char *glossary); 199 | struct arg_dbl* arg_dbln(const char* shortopts, 200 | const char* longopts, 201 | const char *datatype, 202 | int mincount, 203 | int maxcount, 204 | const char *glossary); 205 | 206 | struct arg_str* arg_str0(const char* shortopts, 207 | const char* longopts, 208 | const char* datatype, 209 | const char* glossary); 210 | struct arg_str* arg_str1(const char* shortopts, 211 | const char* longopts, 212 | const char* datatype, 213 | const char *glossary); 214 | struct arg_str* arg_strn(const char* shortopts, 215 | const char* longopts, 216 | const char* datatype, 217 | int mincount, 218 | int maxcount, 219 | const char *glossary); 220 | 221 | struct arg_rex* arg_rex0(const char* shortopts, 222 | const char* longopts, 223 | const char* pattern, 224 | const char* datatype, 225 | int flags, 226 | const char* glossary); 227 | struct arg_rex* arg_rex1(const char* shortopts, 228 | const char* longopts, 229 | const char* pattern, 230 | const char* datatype, 231 | int flags, 232 | const char *glossary); 233 | struct arg_rex* arg_rexn(const char* shortopts, 234 | const char* longopts, 235 | const char* pattern, 236 | const char* datatype, 237 | int mincount, 238 | int maxcount, 239 | int flags, 240 | const char *glossary); 241 | 242 | struct arg_file* arg_file0(const char* shortopts, 243 | const char* longopts, 244 | const char* datatype, 245 | const char* glossary); 246 | struct arg_file* arg_file1(const char* shortopts, 247 | const char* longopts, 248 | const char* datatype, 249 | const char *glossary); 250 | struct arg_file* arg_filen(const char* shortopts, 251 | const char* longopts, 252 | const char* datatype, 253 | int mincount, 254 | int maxcount, 255 | const char *glossary); 256 | 257 | struct arg_date* arg_date0(const char* shortopts, 258 | const char* longopts, 259 | const char* format, 260 | const char* datatype, 261 | const char* glossary); 262 | struct arg_date* arg_date1(const char* shortopts, 263 | const char* longopts, 264 | const char* format, 265 | const char* datatype, 266 | const char *glossary); 267 | struct arg_date* arg_daten(const char* shortopts, 268 | const char* longopts, 269 | const char* format, 270 | const char* datatype, 271 | int mincount, 272 | int maxcount, 273 | const char *glossary); 274 | 275 | struct arg_end* arg_end(int maxerrors); 276 | 277 | 278 | /**** other functions *******************************************/ 279 | int arg_nullcheck(void **argtable); 280 | int arg_parse(int argc, char **argv, void **argtable); 281 | void arg_print_option(FILE *fp, const char *shortopts, const char *longopts, const char *datatype, const char *suffix); 282 | void arg_print_syntax(FILE *fp, void **argtable, const char *suffix); 283 | void arg_print_syntaxv(FILE *fp, void **argtable, const char *suffix); 284 | void arg_print_glossary(FILE *fp, void **argtable, const char *format); 285 | void arg_print_glossary_gnu(FILE *fp, void **argtable); 286 | void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname); 287 | void arg_freetable(void **argtable, size_t n); 288 | 289 | /**** deprecated functions, for back-compatibility only ********/ 290 | void arg_free(void **argtable); 291 | 292 | #ifdef __cplusplus 293 | } 294 | #endif 295 | #endif 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | -------------------------------------------------------------------------------- /base64.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | base64.cpp and base64.h 3 | 4 | Copyright (C) 2004-2008 René Nyffenegger 5 | 6 | This source code is provided 'as-is', without any express or implied 7 | warranty. In no event will the author be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this source code must not be misrepresented; you must not 15 | claim that you wrote the original source code. If you use this source code 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original source code. 21 | 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | René Nyffenegger rene.nyffenegger@adp-gmbh.ch 25 | 26 | */ 27 | 28 | #include "base64.h" 29 | #include 30 | 31 | static const std::string base64_chars = 32 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 33 | "abcdefghijklmnopqrstuvwxyz" 34 | "0123456789+/"; 35 | 36 | 37 | static inline bool is_base64(unsigned char c) { 38 | return (isalnum(c) || (c == '+') || (c == '/')); 39 | } 40 | 41 | std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { 42 | std::string ret; 43 | int i = 0; 44 | int j = 0; 45 | unsigned char char_array_3[3]; 46 | unsigned char char_array_4[4]; 47 | 48 | while (in_len--) { 49 | char_array_3[i++] = *(bytes_to_encode++); 50 | if (i == 3) { 51 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 52 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 53 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 54 | char_array_4[3] = char_array_3[2] & 0x3f; 55 | 56 | for(i = 0; (i <4) ; i++) 57 | ret += base64_chars[char_array_4[i]]; 58 | i = 0; 59 | } 60 | } 61 | 62 | if (i) 63 | { 64 | for(j = i; j < 3; j++) 65 | char_array_3[j] = '\0'; 66 | 67 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 68 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 69 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 70 | char_array_4[3] = char_array_3[2] & 0x3f; 71 | 72 | for (j = 0; (j < i + 1); j++) 73 | ret += base64_chars[char_array_4[j]]; 74 | 75 | while((i++ < 3)) 76 | ret += '='; 77 | 78 | } 79 | 80 | return ret; 81 | 82 | } 83 | 84 | std::string base64_decode(std::string const& encoded_string) { 85 | int in_len = encoded_string.size(); 86 | int i = 0; 87 | int j = 0; 88 | int in_ = 0; 89 | unsigned char char_array_4[4], char_array_3[3]; 90 | std::string ret; 91 | 92 | while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { 93 | char_array_4[i++] = encoded_string[in_]; in_++; 94 | if (i ==4) { 95 | for (i = 0; i <4; i++) 96 | char_array_4[i] = base64_chars.find(char_array_4[i]); 97 | 98 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 99 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 100 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 101 | 102 | for (i = 0; (i < 3); i++) 103 | ret += char_array_3[i]; 104 | i = 0; 105 | } 106 | } 107 | 108 | if (i) { 109 | for (j = i; j <4; j++) 110 | char_array_4[j] = 0; 111 | 112 | for (j = 0; j <4; j++) 113 | char_array_4[j] = base64_chars.find(char_array_4[j]); 114 | 115 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 116 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 117 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 118 | 119 | for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; 120 | } 121 | 122 | return ret; 123 | } 124 | -------------------------------------------------------------------------------- /base64.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | std::string base64_encode(unsigned char const* , unsigned int len); 4 | std::string base64_decode(std::string const& s); 5 | -------------------------------------------------------------------------------- /common/defines.h: -------------------------------------------------------------------------------- 1 | #ifndef DEFINES_H 2 | #define DEFINES_H 3 | 4 | #define TRUE 1 5 | #define FALSE 0 6 | 7 | #define PATHSIZE 1024 8 | #define SOCKETBUFSIZE 32 // entries in /proc//fd are in the form of socket:[1234567] 9 | #define NFQNUM_OUT 11220 //arbitrary number used for iptables rule 10 | #define NFQNUM_OUTPUT_UDP 11222 //arbitrary number used for iptables rule 11 | #define NFQNUM_OUTPUT_REST 11223 //arbitrary number used for iptables rule 12 | #define NFQNUM_INPUT 11221 13 | #define MAX_LINE_LENGTH 1024 14 | #define DIGEST_SIZE 32 15 | #define CTMARKOUT_BASE 11331 //conntrack marks start with this base number 16 | //(to avoid possible collision with other programs that use conntrack marks 17 | #define CTMARKIN_BASE 21331 18 | #define CTMARK_DELTA 10000 //fixed number CTMARKIN_BASE - CTMARKOUT_BASE. N.B. use a number here, see below 19 | #define MEMBUF_SIZE 65536 //buffer size to fread() /proc/net/tcp*,udp* 20 | 21 | #define CT_ENTRIES_EXPORT_MAX 200 22 | #define REFRESH_INTERVAL 1 23 | #define TEST_FAILED SIGUSR1 24 | #define TEST_SUCCEEDED SIGUSR2 25 | #define MAGIC_NO 1111111111 //a token at the end of tcp_port_and_socket_cache 26 | #define DIRECTION_IN 1 27 | #define DIRECTION_OUT 2 28 | #define PROTO_TCP 6 29 | #define PROTO_UDP 17 30 | #define PROTO_ICMP 5 31 | 32 | #define PIDFILE "/tmp/lpfw.pid" 33 | #define RULESFILE "/etc/lpfw.rules" 34 | #define LPFW_LOGFILE "/tmp/lpfw.log" 35 | #define TEST_LOGFILE "/tmp/lpfw.test_log" 36 | #define TEST_TRAFFIC_LOG "/tmp/lpfw.test_traffic_log" 37 | #define SAVE_IPTABLES_OUTPUT_FILE "/tmp/lpfw.output" 38 | #define SAVE_IPTABLES_INPUT_FILE "/tmp/lpfw.input" 39 | 40 | #define TCPINFO "/proc/net/tcp" 41 | #define UDPINFO "/proc/net/udp" 42 | #define TCP6INFO "/proc/net/tcp6" 43 | #define UDP6INFO "/proc/net/udp6" 44 | #define ICMPINFO "/proc/net/raw" 45 | 46 | #define ALLOW_ONCE "ALLOW_ONCE" 47 | #define ALLOW_ALWAYS "ALLOW_ALWAYS" 48 | #define DENY_ONCE "DENY_ONCE" 49 | #define DENY_ALWAYS "DENY_ALWAYS" 50 | #define KERNEL_PROCESS "KERNEL_PROCESS" 51 | 52 | #define MLOG_INFO 1 53 | #define MLOG_TRAFFIC 2 54 | #define MLOG_DEBUG 3 55 | #define MLOG_DEBUG2 4 56 | #define MLOG_ALERT 5 57 | #define MLOG_ERROR 6 58 | #define MLOG_DEBUG3 7 59 | 60 | #endif // DEFINES_H 61 | -------------------------------------------------------------------------------- /common/includes.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDES_H_ 2 | #define INCLUDES_H_ 3 | 4 | #include "defines.h" 5 | #include 6 | #include //for ino_t 7 | #include //for DIR* 8 | #include //for INET_ADDRSTRLEN 9 | #include 10 | #include 11 | #include 12 | using namespace std; 13 | 14 | void die_syscall(string message); 15 | 16 | //macros enables any thread to use logging concurrently 17 | #define M_PRINTF(loglevel, ...) \ 18 | //pthread_mutex_lock(&logstring_mutex); 19 | //cout << "LOG:" << logstring << "\n"; 20 | //pthread_mutex_unlock(&logstring_mutex); 21 | 22 | 23 | struct rule{ 24 | string path; //path to executable 25 | string pid; //its pid (or IP address for kernel processes) 26 | string perms; // permission in the form "ALLOW ALWAYS" 27 | string sha; //sha256 hexdigest 28 | u_int32_t ctmark_out; 29 | u_int32_t ctmark_in; //conntrack mark assigned to each new connection 30 | //and used when a user deletes a rule to tell conntrack to immediately 31 | //drop any existing connections associated with the mark 32 | bool is_active; //Has process already been seen sending/receiving packets? 33 | bool first_instance; //TRUE for a first instance of an app or a parent process (not in use?) 34 | bool is_fixed_ctmark = false; //the user may assign a fixed netfilter mark for conntrack purposes 35 | unsigned long long stime; // start time of the process 36 | vector sockets;//sockets owned by the processes 37 | string pidfdpath; //path to /proc/PID/fd 38 | DIR *dirstream; //a constantly open stream to /proc/PID/fd 39 | }; 40 | 41 | 42 | enum 43 | { 44 | SOCKET_FOUND_IN_DLIST_ALLOW, 45 | PATH_FOUND_IN_DLIST_ALLOW, 46 | NEW_INSTANCE_ALLOW, 47 | FORKED_CHILD_ALLOW, 48 | CACHE_TRIGGERED_ALLOW, 49 | INKERNEL_RULE_ALLOW, //5 50 | GLOBAL_RULE_ALLOW, 51 | ALLOW_VERDICT_MAX, 52 | 53 | SOCKET_FOUND_IN_DLIST_DENY, 54 | PATH_FOUND_IN_DLIST_DENY, 55 | NEW_INSTANCE_DENY, //10 56 | FORKED_CHILD_DENY, 57 | CACHE_TRIGGERED_DENY, 58 | INKERNEL_RULE_DENY, 59 | GLOBAL_RULE_DENY, 60 | DENY_VERDICT_MAX, //15 61 | 62 | GLOBAL_RULES_VERDICT_MAX, 63 | 64 | SENT_TO_FRONTEND, 65 | FRONTEND_NOT_LAUNCHED, 66 | FRONTEND_BUSY, 67 | ICMP_MORE_THAN_ONE_ENTRY, //20 68 | ICMP_NO_ENTRY, 69 | ICMP_ONLY_ONE_ENTRY, 70 | UNSUPPORTED_PROTOCOL, 71 | SHA_DONT_MATCH, 72 | EXESIZE_DONT_MATCH, //25 (not in use) 73 | EXE_HAS_BEEN_CHANGED, 74 | CANT_READ_EXE, 75 | SPOOFED_PID, 76 | PROCFS_ERROR, 77 | INKERNEL_SOCKET_FOUND, //30 78 | INKERNEL_SOCKET_NOT_FOUND, 79 | INKERNEL_IPADDRESS_NOT_IN_DLIST, 80 | SRCPORT_NOT_FOUND_IN_PROC, //not in use 81 | DSTPORT_NOT_FOUND_IN_PROCNET, 82 | SOCKET_NOT_FOUND_IN_PROCPIDFD, //35 83 | SOCKET_FOUND_IN_PROCPIDFD, 84 | //SRCPORT_NOT_FOUND_IN_PROCNET happens when a process connect()s in non-blocking mode 85 | //and immediately closes the socket. Or when so many new connections happen simultaneously 86 | //that there is a lag in connection appearing in /proc/net/* 87 | LOCALPORT_NOT_FOUND_IN_PROCNET, 88 | SOCKET_IN_CACHE_NOT_FOUND, 89 | PATH_IN_RULES_NOT_FOUND, 90 | PATH_IN_RULES_FOUND_BUT_PERMS_ARE_ONCE, //40 91 | SOCKET_ACTIVE_PROCESSES_NOT_FOUND, 92 | GID_MATCH_ALLOW, 93 | GID_MATCH_DENY, 94 | SOCKET_ZERO_BUT_UID_NOT_ZERO, 95 | SOCKET_CHANGED_FROM_ZERO, //45 96 | SEARCH_ACTIVE_PROCESSES_AGAIN, 97 | PROCPIDSTAT_DOES_NOT_EXIST 98 | }; 99 | 100 | 101 | 102 | #endif /* INCLUDES_H_ */ 103 | -------------------------------------------------------------------------------- /common/syscall_wrappers.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | using namespace std; 26 | 27 | 28 | template FILE * _fopen(Varargs ... varargs); 29 | template DIR * _opendir(Varargs ... varargs); 30 | template int _nfct_query(Varargs ... varargs); 31 | template int _nfct_callback_register(Varargs ... varargs); 32 | template int _fseek(Varargs ... varargs); 33 | template int _fclose(Varargs ... varargs); 34 | template int _fputs(Varargs ... varargs); 35 | template int _fputc(Varargs ... varargs); 36 | template int _stat(Varargs ... varargs); 37 | template int _system(Varargs ... varargs); 38 | template int _nfq_unbind_pf(Varargs ... varargs); 39 | template int _nfq_bind_pf(Varargs ... varargs); 40 | template int _nfq_set_mode(Varargs ... varargs); 41 | template int _nfq_set_queue_maxlen(Varargs ... varargs); 42 | template struct nf_conntrack* _nfct_new(Varargs ... varargs); 43 | template struct nfct_handle* _nfct_open(Varargs ... varargs); 44 | template struct nfq_q_handle * _nfq_create_queue(Varargs ... varargs); 45 | template struct nfq_handle * _nfq_open(Varargs ... varargs); 46 | template int _fileno(Varargs ... varargs); 47 | template int _pthread_mutex_lock(Varargs ... varargs); 48 | template int _pthread_mutex_unlock(Varargs ... varargs); 49 | template cap_t _cap_get_proc(Varargs ... varargs); 50 | template int _cap_set_proc(Varargs ... varargs); 51 | template int _cap_clear(Varargs ... varargs); 52 | template int _cap_free(Varargs ... varargs); 53 | template int _cap_set_flag(Varargs ... varargs); 54 | template int _nfq_close(Varargs ... varargs); 55 | template void * _malloc(Varargs ... varargs); 56 | template int _closedir(Varargs ... varargs); 57 | template int _pthread_cond_signal(Varargs ... varargs); 58 | template int _open(Varargs ... varargs); 59 | template off_t _lseek(Varargs ... varargs); 60 | template ssize_t _read(Varargs ... varargs); 61 | template int _remove(Varargs ... varargs); 62 | template int _readlink(Varargs ... varargs); 63 | template void * _mmap(Varargs ... varargs); 64 | template int _pthread_create(Varargs ... varargs); 65 | template int _close(Varargs ... varargs); 66 | template int _munmap(Varargs ... varargs); 67 | 68 | 69 | //This is a hack. Templates can be included by multiple source file without causing an error 70 | template 71 | int die_syscall(string message){ 72 | fprintf(stderr, "Error %d - %s\n", errno, strerror(errno)); 73 | cout << message << "\n"; 74 | cout << "Dumping a core file. Make sure to sudo chmod 0777 it to make it user-readable \n"; 75 | abort(); //dump corefile - to get a decent backtrace via gcc 76 | } 77 | 78 | 79 | template 80 | FILE * _fopen(Varargs ... varargs) { 81 | FILE *retval = fopen(varargs ...); 82 | if (retval == NULL){ 83 | die_syscall("fopen returned NULL"); 84 | } 85 | return retval; 86 | } 87 | 88 | 89 | template 90 | DIR * _opendir(Varargs ... varargs) { 91 | DIR *retval = opendir(varargs ...); 92 | if (retval == NULL){ 93 | die_syscall ("opendir returned NULL"); 94 | } 95 | return retval; 96 | } 97 | 98 | 99 | template 100 | int _nfct_query(Varargs ... varargs) { 101 | int retval = nfct_query(varargs ...); 102 | if (retval == -1){ 103 | printf("nfct_query: %s,\n", strerror ( errno )); 104 | die_syscall ("nfct_query returned -1"); 105 | } 106 | return retval; 107 | } 108 | 109 | 110 | template 111 | int _nfct_callback_register(Varargs ... varargs) { 112 | int retval = nfct_callback_register(varargs ...); 113 | if (retval == -1){ 114 | printf("nfct_callback_register: %s,\n", strerror ( errno )); 115 | die_syscall ("nfct_callback_register returned -1"); 116 | } 117 | return retval; 118 | } 119 | 120 | 121 | template 122 | int _fseek(Varargs ... varargs) { 123 | int retval = fseek(varargs ...); 124 | if (retval == -1){ 125 | die_syscall ("fseek returned -1"); 126 | } 127 | return retval; 128 | } 129 | 130 | 131 | template 132 | int _fclose(Varargs ... varargs) { 133 | int retval = fclose(varargs ...); 134 | if (retval == -1){ 135 | die_syscall ("fclose returned -1"); 136 | } 137 | return retval; 138 | } 139 | 140 | //can be deleted 141 | template 142 | int _fputs(Varargs ... varargs) { 143 | int retval = fputs(varargs ...); 144 | if (retval == EOF){ 145 | die_syscall ("fputs returned EOF"); 146 | } 147 | return retval; 148 | } 149 | 150 | 151 | //can be deleted 152 | template 153 | int _fputc(Varargs ... varargs) { 154 | int retval = fputc(varargs ...); 155 | if (retval == EOF){ 156 | die_syscall ("fputc returned EOF"); 157 | } 158 | return retval; 159 | } 160 | 161 | 162 | template 163 | int _stat(Varargs ... varargs) { 164 | int retval = stat(varargs ...); 165 | if (retval == -1){ 166 | die_syscall ("stat returned -1"); 167 | } 168 | return retval; 169 | } 170 | 171 | 172 | template 173 | int _system(Varargs ... varargs) { 174 | int retval = system(varargs ...); 175 | if (retval == -1){ 176 | die_syscall ("system returned -1"); 177 | } 178 | return retval; 179 | } 180 | 181 | 182 | template 183 | int _nfq_unbind_pf(Varargs ... varargs) { 184 | int retval = nfq_unbind_pf(varargs ...); 185 | if (retval != 0){ 186 | die_syscall ("nfq_unbind_pf returned non 0"); 187 | } 188 | return retval; 189 | } 190 | 191 | 192 | template 193 | int _nfq_bind_pf(Varargs ... varargs) { 194 | int retval = nfq_bind_pf(varargs ...); 195 | if (retval < 0){ 196 | die_syscall ("nfq_bind_pf returned < 0"); 197 | } 198 | return retval; 199 | } 200 | 201 | 202 | template 203 | int _nfq_set_mode(Varargs ... varargs) { 204 | int retval = nfq_set_mode(varargs ...); 205 | if (retval == -1){ 206 | die_syscall ("nfq_set_mode returned -1"); 207 | } 208 | return retval; 209 | } 210 | 211 | 212 | template 213 | int _nfq_set_queue_maxlen(Varargs ... varargs) { 214 | int retval = nfq_set_queue_maxlen(varargs ...); 215 | if (retval == -1){ 216 | die_syscall ("nfq_set_queue_maxlen returned -1"); 217 | } 218 | return retval; 219 | } 220 | 221 | 222 | template 223 | struct nf_conntrack* _nfct_new(Varargs ... varargs) { 224 | struct nf_conntrack* retval = nfct_new(varargs ...); 225 | if (retval == NULL){ 226 | printf("nfct_new: %s,\n", strerror ( errno )); 227 | die_syscall ("nfct_new returned NULL"); 228 | } 229 | return retval; 230 | } 231 | 232 | 233 | template 234 | struct nfct_handle* _nfct_open(Varargs ... varargs) { 235 | struct nfct_handle* retval = nfct_open(varargs ...); 236 | if (retval == NULL){ 237 | printf("nfct_open: %s,\n", strerror ( errno )); 238 | die_syscall ("nfct_open returned NULL"); 239 | } 240 | return retval; 241 | } 242 | 243 | 244 | template 245 | struct nfq_q_handle * _nfq_create_queue(Varargs ... varargs) { 246 | struct nfq_q_handle *retval = nfq_create_queue(varargs ...); 247 | if (retval == NULL){ 248 | die_syscall ("nfq_create_queue returned NULL"); 249 | } 250 | return retval; 251 | } 252 | 253 | 254 | template 255 | struct nfq_handle * _nfq_open(Varargs ... varargs) { 256 | struct nfq_handle *retval = nfq_open(varargs ...); 257 | if (retval == NULL){ 258 | die_syscall ("nfq_open returned NULL"); 259 | } 260 | return retval; 261 | } 262 | 263 | 264 | template 265 | int _fileno(Varargs ... varargs) { 266 | int retval = fileno(varargs ...); 267 | if (retval == -1){ 268 | die_syscall ("fileno returned -1"); 269 | } 270 | return retval; 271 | } 272 | 273 | 274 | template 275 | int _pthread_mutex_lock(Varargs ... varargs) { 276 | int retval = pthread_mutex_lock(varargs ...); 277 | if (retval != 0){ 278 | die_syscall ("pthread_mutex_lock returned non 0"); 279 | } 280 | return retval; 281 | } 282 | 283 | 284 | template 285 | int _pthread_mutex_unlock(Varargs ... varargs) { 286 | int retval = pthread_mutex_unlock(varargs ...); 287 | if (retval != 0){ 288 | die_syscall ("pthread_mutex_unlock returned non 0"); 289 | } 290 | return retval; 291 | } 292 | 293 | 294 | template 295 | cap_t _cap_get_proc(Varargs ... varargs) { 296 | cap_t retval = cap_get_proc(varargs ...); 297 | if (retval == NULL){ 298 | die_syscall ("cap_get_proc returned NULL"); 299 | } 300 | return retval; 301 | } 302 | 303 | 304 | template 305 | int _cap_set_proc(Varargs ... varargs) { 306 | int retval = cap_set_proc(varargs ...); 307 | if (retval == -1){ 308 | die_syscall ("cap_set_proc returned -1"); 309 | } 310 | return retval; 311 | } 312 | 313 | 314 | template 315 | int _cap_clear(Varargs ... varargs) { 316 | int retval = cap_clear(varargs ...); 317 | if (retval == -1){ 318 | die_syscall ("cap_clear returned -1"); 319 | } 320 | return retval; 321 | } 322 | 323 | 324 | template 325 | int _cap_free(Varargs ... varargs) { 326 | int retval = cap_free(varargs ...); 327 | if (retval == -1){ 328 | die_syscall ("cap_free returned -1"); 329 | } 330 | return retval; 331 | } 332 | 333 | 334 | template 335 | int _cap_set_flag(Varargs ... varargs) { 336 | int retval = cap_set_flag(varargs ...); 337 | if (retval == -1){ 338 | die_syscall ("cap_set_flag returned -1"); 339 | } 340 | return retval; 341 | } 342 | 343 | 344 | template 345 | int _nfq_close(Varargs ... varargs) { 346 | int retval = nfq_close(varargs ...); 347 | if (retval != 0){ 348 | die_syscall ("nfq_close returned non 0"); 349 | } 350 | return retval; 351 | } 352 | 353 | 354 | template 355 | void * _malloc(Varargs ... varargs) { 356 | void * retval = malloc(varargs ...); 357 | if (retval == NULL){ 358 | die_syscall ("malloc returned NULL"); 359 | } 360 | return retval; 361 | } 362 | 363 | 364 | template 365 | int _closedir(Varargs ... varargs) { 366 | int retval = closedir(varargs ...); 367 | if (retval == -1){ 368 | die_syscall ("closedir returned -1"); 369 | } 370 | return retval; 371 | } 372 | 373 | 374 | template 375 | int _pthread_cond_signal(Varargs ... varargs) { 376 | int retval = pthread_cond_signal(varargs ...); 377 | if (retval == -1){ 378 | die_syscall ("pthread_cond_signal returned -1"); 379 | } 380 | return retval; 381 | } 382 | 383 | 384 | template 385 | int _open(Varargs ... varargs) { 386 | int retval = open(varargs ...); 387 | if (retval == -1){ 388 | die_syscall ("open returned -1"); 389 | } 390 | return retval; 391 | } 392 | 393 | 394 | template 395 | off_t _lseek(Varargs ... varargs) { 396 | off_t retval = lseek(varargs ...); 397 | if ((off_t)retval == -1){ 398 | die_syscall ("lseek returned -1"); 399 | } 400 | return retval; 401 | } 402 | 403 | 404 | template 405 | ssize_t _read(Varargs ... varargs) { 406 | ssize_t retval = read(varargs ...); 407 | if (int(retval) == -1){ 408 | die_syscall ("read returned -1"); 409 | } 410 | return retval; 411 | } 412 | 413 | 414 | template 415 | int _remove(Varargs ... varargs) { 416 | int retval = remove(varargs ...); 417 | if (int(retval) == -1){ 418 | die_syscall ("remove returned -1"); 419 | } 420 | return retval; 421 | } 422 | 423 | 424 | template 425 | int _readlink(Varargs ... varargs) { 426 | int retval = readlink(varargs ...); 427 | if (retval == -1){ 428 | die_syscall ("readlink returned -1"); 429 | } 430 | return retval; 431 | } 432 | 433 | 434 | template 435 | void * _mmap(Varargs ... varargs) { 436 | void * retval = mmap(varargs ...); 437 | if (retval == MAP_FAILED){ 438 | die_syscall ("mmap returned MAP_FAILED"); 439 | } 440 | return retval; 441 | } 442 | 443 | 444 | template 445 | int _pthread_create(Varargs ... varargs) { 446 | int retval = pthread_create(varargs ...); 447 | if (retval != 0){ 448 | die_syscall ("pthread_create returned non 0"); 449 | } 450 | return retval; 451 | } 452 | 453 | 454 | template 455 | int _close(Varargs ... varargs) { 456 | int retval = close(varargs ...); 457 | if (retval != 0){ 458 | die_syscall ("close returned non 0"); 459 | } 460 | return retval; 461 | } 462 | 463 | 464 | template 465 | int _munmap(Varargs ... varargs) { 466 | int retval = munmap(varargs ...); 467 | if (retval != 0){ 468 | die_syscall ("munmap returned non 0"); 469 | } 470 | return retval; 471 | } 472 | 473 | -------------------------------------------------------------------------------- /conntrack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include //for memcpy 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include /* time */ 19 | #include 20 | #include 21 | 22 | #include "conntrack.h" 23 | #include "lpfw.h" 24 | #include "common/includes.h" 25 | #include "common/syscall_wrappers.h" 26 | 27 | using namespace std; 28 | 29 | queue ctmsgQueue; 30 | 31 | pthread_t tcp_export_thr; 32 | //ct_delete_mark_thread uses waiting on condition 33 | pthread_cond_t condvar = PTHREAD_COND_INITIALIZER; 34 | pthread_mutex_t condvar_mutex = PTHREAD_MUTEX_INITIALIZER; 35 | char predicate = FALSE; 36 | //two NFCT_Q_DUMP simultaneous operations can produce an error 37 | pthread_mutex_t ct_dump_mutex = PTHREAD_MUTEX_INITIALIZER; 38 | pthread_mutex_t ct_entries_mutex = PTHREAD_MUTEX_INITIALIZER; 39 | pthread_t ct_dump_thr, ct_destroy_hook_thr, ct_delete_ctmark_thr; 40 | 41 | int ctmark_to_delete_in, ctmark_to_delete_out; 42 | struct nfct_handle *setmark_handle; 43 | extern bool conntrack_send_anyway; 44 | 45 | //this array is used internally by lpfw to prepare for export 46 | ulong ct_array[CT_ENTRIES_EXPORT_MAX][9] = {}; 47 | //this array is built for export to frontend based on ct_array 48 | ulong ct_array_export[CT_ENTRIES_EXPORT_MAX][5] = {}; 49 | /* 50 | [0] ctmark (export[0]) 51 | [1] bytes in allowed 52 | [2] bytes out allowed 53 | [3] bytes in from all previously destroyed conntracks which had this ctmark 54 | [4] bytes out from all previously destroyed conntracks which had this ctmark 55 | [5] [1] + [3] (export[1]) 56 | [6] [2] + [4] (export[2]) 57 | [7] total bytes in denied so far (export[3]) 58 | [8] total bytes out denied so far (export[4]) 59 | */ 60 | 61 | 62 | //Register a callback ct_destroy_cb that gets triggered whenever conntrack tries to destroy a connection 63 | //TODO: this is a weirdly-written old function. there is no need to block here. 64 | void * thread_ct_destroy( void *ptr) 65 | { 66 | struct nfct_handle *traffic_handle = _nfct_open(NFNL_SUBSYS_CTNETLINK, NF_NETLINK_CONNTRACK_DESTROY); 67 | _nfct_callback_register(traffic_handle, NFCT_T_DESTROY, ct_destroy_cb, (void *)NULL); 68 | nfct_catch(traffic_handle); //the thread should block here 69 | } 70 | 71 | 72 | //lpfw triggers condvar condition when a rule is deleted. 73 | //this thread will DUMP all conntracks onto ct_delete_mark_cb one by one 74 | void* thread_ct_delete_mark ( void* ptr ) 75 | { 76 | u_int8_t family = AF_INET; 77 | struct nfct_handle *deletemark_handle = _nfct_open(NFNL_SUBSYS_CTNETLINK, 0); 78 | _nfct_callback_register(deletemark_handle, NFCT_T_ALL, ct_delete_mark_cb, (void *)NULL); 79 | 80 | while(1){ 81 | _pthread_mutex_lock(&condvar_mutex); 82 | while(predicate == FALSE){ 83 | pthread_cond_wait(&condvar, &condvar_mutex); 84 | } 85 | predicate = FALSE; 86 | _pthread_mutex_unlock(&condvar_mutex); 87 | _pthread_mutex_lock(&ct_dump_mutex); 88 | _nfct_query(deletemark_handle, NFCT_Q_DUMP, &family); 89 | _pthread_mutex_unlock(&ct_dump_mutex); 90 | } 91 | } 92 | 93 | 94 | //Set netfilter mark on a connection 95 | int setmark (enum nf_conntrack_msg_type type, struct nf_conntrack *mct,void *data) 96 | { 97 | static nfct_handle *handle = _nfct_open (NFNL_SUBSYS_CTNETLINK, 0); 98 | nfct_set_attr_u32(mct, ATTR_MARK, ctmark_to_set); 99 | nfct_query(handle, NFCT_Q_UPDATE, mct); 100 | return NFCT_CB_CONTINUE; 101 | } 102 | 103 | 104 | void init_conntrack(){ 105 | //enable byte accounting in conntrack 106 | ofstream file("/proc/sys/net/netfilter/nf_conntrack_acct"); 107 | file << "1"; 108 | file.close(); 109 | //Flush all conntrack entries so that we're getting a fresh start 110 | u_int8_t family = AF_INET; 111 | nfct_handle *handle_flush = _nfct_open (NFNL_SUBSYS_CTNETLINK, 0); 112 | _nfct_query (handle_flush, NFCT_Q_FLUSH, &family); 113 | //register a callback which nfq_handler will call to set netfilter marks on connection 114 | setmark_handle = _nfct_open (NFNL_SUBSYS_CTNETLINK, 0); 115 | _nfct_callback_register (setmark_handle, NFCT_T_ALL, setmark, (void *)NULL); 116 | 117 | _pthread_create ( &tcp_export_thr, (pthread_attr_t *)NULL, tcp_export_thread, (void *)NULL); 118 | _pthread_create ( &ct_dump_thr, (pthread_attr_t *)NULL, thread_ct_dump, (void *)NULL ); 119 | _pthread_create ( &ct_destroy_hook_thr, (pthread_attr_t *)NULL, thread_ct_destroy, (void *)NULL); 120 | _pthread_create ( &ct_delete_ctmark_thr, (pthread_attr_t *)NULL, thread_ct_delete_mark, (void *)NULL); 121 | } 122 | 123 | 124 | void* tcp_export_thread ( void *ptr ) { 125 | ptr = 0; 126 | int sockfd, newsockfd; 127 | socklen_t clilen; 128 | struct sockaddr_in serv_addr, cli_addr; 129 | int n; 130 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 131 | if (sockfd < 0) perror("ERROR opening socket"); 132 | bzero((char *) &serv_addr, sizeof(serv_addr)); 133 | 134 | serv_addr.sin_family = AF_INET; 135 | serv_addr.sin_addr.s_addr = INADDR_ANY; 136 | serv_addr.sin_port = htons(0); 137 | if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { 138 | perror("ERROR on binding"); 139 | } 140 | int local_port; 141 | struct sockaddr_in sin; 142 | socklen_t addrlen = sizeof(sin); 143 | if(getsockname(sockfd, (struct sockaddr *)&sin, &addrlen) == 0 && 144 | sin.sin_family == AF_INET && addrlen == sizeof(sin)) { 145 | local_port = ntohs(sin.sin_port); 146 | } 147 | cout << "Conntrack port:" << to_string(local_port) << "\n"; 148 | ofstream myfile("/tmp/lpfwctport"); 149 | myfile << std::to_string(local_port); 150 | myfile.close(); 151 | 152 | string dispatch; 153 | while (true) { 154 | listen(sockfd,1); 155 | clilen = sizeof(cli_addr); 156 | newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); 157 | if (newsockfd < 0) perror("ERROR on accept"); 158 | if(fcntl(newsockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK) < 0) { 159 | printf ("Couldn't set socket to non-blocking"); } 160 | 161 | while(true){ 162 | if (ctmsgQueue.empty()) { 163 | sleep(1); 164 | continue; 165 | } 166 | try { //TODO a race condition is possible when ct_dump_thread clears the queue 167 | dispatch = ctmsgQueue.front(); 168 | } catch (...) {continue;} 169 | ctmsgQueue.pop(); 170 | n = send(newsockfd, dispatch.c_str(), dispatch.length(), MSG_NOSIGNAL); 171 | if (n < 0) {break;}; 172 | } 173 | } 174 | } 175 | 176 | 177 | //delete conntracks which have the mark 178 | int ct_delete_mark_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *mct,void *data) 179 | { 180 | static nfct_handle *handle_delete = _nfct_open (NFNL_SUBSYS_CTNETLINK, 0); 181 | int mark = nfct_get_attr_u32(mct, ATTR_MARK); 182 | if ( mark == ctmark_to_delete_in || mark == ctmark_to_delete_out){ 183 | if (nfct_query(handle_delete, NFCT_Q_DESTROY, mct) == -1){ 184 | printf("Error: nfct_query DESTROY %s,%s,%d\n", strerror ( errno ), __FILE__, __LINE__ ); 185 | return NFCT_CB_CONTINUE; 186 | } 187 | cout << "deleted ct mark" << mark << "\n"; 188 | return NFCT_CB_CONTINUE; 189 | } 190 | return NFCT_CB_CONTINUE; 191 | } 192 | 193 | 194 | //Receive one-by-one all conntracks and add current byte count 195 | //to the previous 196 | int ct_dump_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *mct,void *data) 197 | { 198 | int i,mark; 199 | ulong in_bytes, out_bytes; 200 | if ((mark = nfct_get_attr_u32(mct, ATTR_MARK)) == 0){ 201 | return NFCT_CB_CONTINUE;} 202 | out_bytes = nfct_get_attr_u64(mct, ATTR_ORIG_COUNTER_BYTES); 203 | in_bytes = nfct_get_attr_u64(mct, ATTR_REPL_COUNTER_BYTES); 204 | 205 | //No need to lock mutex here b/c it's being held by thread_ct_dump which called us 206 | for (i = 0; ct_array[i][0] != 0; ++i) 207 | { 208 | if (ct_array[i][0] != mark) continue; 209 | ct_array[i][1] += in_bytes; 210 | ct_array[i][2] += out_bytes; 211 | return NFCT_CB_CONTINUE; 212 | } 213 | //the entry is not yet in array, adding now 214 | ct_array[i][0] = mark; 215 | ct_array[i][1] = in_bytes; 216 | ct_array[i][2] = out_bytes; 217 | return NFCT_CB_CONTINUE; 218 | } 219 | 220 | 221 | //When conntrack deletes an entry, we get called so we could 222 | //correctly work out the in/out bytes statistics 223 | int ct_destroy_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *mct,void *data) 224 | { 225 | int i,mark; 226 | bool scanned_twice = false; 227 | ulong in_bytes, out_bytes; 228 | if ((mark = nfct_get_attr_u32(mct, ATTR_MARK)) == 0){ 229 | u_int32_t src_addr = nfct_get_attr_u32(mct, ATTR_ORIG_IPV4_SRC); 230 | u_int32_t dst_addr = nfct_get_attr_u32(mct, ATTR_ORIG_IPV4_DST); 231 | if (src_addr == dst_addr){ 232 | //This is assumed to be local traffic. This looks to be a safe assumption 233 | //Ideally, we should query what our local interfaces are 234 | return NFCT_CB_CONTINUE; 235 | } 236 | //addr is in BE byte order. If MSB == 127, we are dealing with loopback range 237 | if ((src_addr & 0xFF) == 127 && (dst_addr & 0xFF) == 127){ 238 | return NFCT_CB_CONTINUE; 239 | } 240 | //TODO: find out if it is OK if some conntracks dont have a mark 241 | //TODO check the conntracks timestamp 242 | out_bytes = nfct_get_attr_u64(mct, ATTR_ORIG_COUNTER_BYTES); 243 | in_bytes = nfct_get_attr_u64(mct, ATTR_REPL_COUNTER_BYTES); 244 | if (in_bytes != 0 && out_bytes != 0){ 245 | printf ("Error: conntrack with mark 0 detected with leaked bytes \n"); 246 | cout << "src_addr: " << src_addr << "\n"; 247 | cout << "dst_addr: " << dst_addr << "\n"; 248 | //TODO figure out a long-term solution for this rare problem 249 | //abort(); 250 | } 251 | //else 252 | return NFCT_CB_CONTINUE; 253 | } 254 | //orig/repl will be treated as in/out later depending on the direction 255 | out_bytes = nfct_get_attr_u64(mct, ATTR_ORIG_COUNTER_BYTES); 256 | in_bytes = nfct_get_attr_u64(mct, ATTR_REPL_COUNTER_BYTES); 257 | 258 | scan_again: 259 | pthread_mutex_lock ( &ct_entries_mutex); 260 | for (i = 0; ct_array[i][0] != 0; ++i){ 261 | if (ct_array[i][0] != mark) continue; 262 | ct_array[i][3] += in_bytes; 263 | ct_array[i][4] += out_bytes; 264 | pthread_mutex_unlock ( &ct_entries_mutex); 265 | return NFCT_CB_CONTINUE; 266 | } 267 | pthread_mutex_unlock ( &ct_entries_mutex); 268 | //We have a mark that is not yet in ct_array. Maybe the dump thread (which sleeps every second) 269 | //hasn't added it yet. Give it another chance 270 | if (!scanned_twice){ 271 | scanned_twice = true; 272 | sleep(1); 273 | cout << "************Scanning again in ct_destroy_cb \n"; 274 | goto scan_again; 275 | } 276 | cout << "Error: unknown conntrack mark in ct_destroy_cb even after scanning again: " << mark << "\n"; 277 | return NFCT_CB_CONTINUE; 278 | //TODO this error should be logged with a dump and analyzed 279 | } 280 | 281 | 282 | //Periodically dump all conntrack stats so we could tell the frontend 283 | //per-process how many bytes went in/out and were allowed/denied 284 | void * thread_ct_dump( void *ptr) 285 | { 286 | u_int8_t family = AF_INET; 287 | struct nfct_handle *ct_dump_handle = _nfct_open(NFNL_SUBSYS_CTNETLINK, 0); 288 | _nfct_callback_register(ct_dump_handle, NFCT_T_ALL, ct_dump_cb, (void *)NULL); 289 | 290 | int i,j; 291 | string export_string; 292 | string prev_export_string; 293 | while(1){ 294 | _pthread_mutex_lock(&ct_entries_mutex); 295 | for (i=0; i= CTMARKIN_BASE) { 313 | //find its OUT ctmark 314 | int delta = ct_array[i][0] - CTMARK_DELTA; 315 | if (delta == ct_array_export[j][0]){ 316 | //bytes in for IN ctmark are bytes out for OUT ctmark 317 | ct_array_export[j][1] += ct_array[i][6]; 318 | ct_array_export[j][2] += ct_array[i][5]; 319 | ct_array_export[j][3] += ct_array[i][8]; 320 | ct_array_export[j][4] += ct_array[i][7]; 321 | goto next; 322 | } 323 | } 324 | //else if this is a OUT ctmark 325 | if (ct_array[i][0] == ct_array_export[j][0]){ 326 | ct_array_export[j][1] += ct_array[i][5]; 327 | ct_array_export[j][2] += ct_array[i][6]; 328 | ct_array_export[j][3] += ct_array[i][7]; 329 | ct_array_export[j][4] += ct_array[i][8]; 330 | goto next; 331 | } 332 | } 333 | //Doesn't exist in export list, create an entry 334 | if (ct_array[i][0] >= CTMARKIN_BASE){ 335 | ct_array_export[j][0] = ct_array[i][0] - CTMARK_DELTA; 336 | ct_array_export[j][1] = ct_array[i][6]; 337 | ct_array_export[j][2] = ct_array[i][5]; 338 | ct_array_export[j][3] = ct_array[i][8]; 339 | ct_array_export[j][4] = ct_array[i][7]; 340 | } 341 | else{ 342 | ct_array_export[j][0] = ct_array[i][0]; 343 | ct_array_export[j][1] = ct_array[i][5]; 344 | ct_array_export[j][2] = ct_array[i][6]; 345 | ct_array_export[j][3] = ct_array[i][7]; 346 | ct_array_export[j][4] = ct_array[i][8]; 347 | } 348 | next: 349 | ; 350 | } 351 | _pthread_mutex_unlock(&ct_entries_mutex); 352 | export_string.clear(); 353 | for (j=0; ct_array_export[j][0] !=0; ++j) { 354 | export_string += std::to_string(ct_array_export[j][0]) + " " + 355 | std::to_string(ct_array_export[j][1]) + " " + 356 | std::to_string(ct_array_export[j][2]) + " " + 357 | std::to_string(ct_array_export[j][3]) + " " + 358 | std::to_string(ct_array_export[j][4]) + string(" CRLF "); 359 | } 360 | export_string += "EOL "; 361 | //Only send updates to frontend when stats changed 362 | if ((export_string != prev_export_string) || (conntrack_send_anyway)){ 363 | ctmsgQueue = queue(); //clear the queue 364 | ctmsgQueue.push(export_string); 365 | prev_export_string = export_string; 366 | if (conntrack_send_anyway) { 367 | cout << "toggling conntrack_send_anyway to false \n"; 368 | conntrack_send_anyway = false;} 369 | } 370 | sleep(1); 371 | } 372 | } 373 | 374 | 375 | void denied_traffic_add (const int direction, const int mark, const int bytes) 376 | { 377 | int i; 378 | _pthread_mutex_lock ( &ct_entries_mutex); 379 | for (i = 0; ct_array[i][0] != 0; ++i){ 380 | if (ct_array[i][0] != mark) continue; 381 | if (direction == DIRECTION_OUT){ 382 | ct_array[i][8] += bytes; 383 | } 384 | else if (direction == DIRECTION_IN){ 385 | ct_array[i][7] += bytes; 386 | } 387 | _pthread_mutex_unlock ( &ct_entries_mutex); 388 | return; 389 | } 390 | //the entry is not yet in array, adding now 391 | ct_array[i][0] = mark; 392 | if (direction == DIRECTION_OUT){ 393 | ct_array[i][8] += bytes; 394 | } 395 | else if (direction == DIRECTION_IN){ 396 | ct_array[i][7] += bytes; 397 | } 398 | _pthread_mutex_unlock ( &ct_entries_mutex); 399 | return ; 400 | } 401 | -------------------------------------------------------------------------------- /conntrack.h: -------------------------------------------------------------------------------- 1 | #ifndef CONNTRACK_H 2 | #define CONNTRACK_H 3 | 4 | #include 5 | #include 6 | 7 | //ct_delete_mark_thread uses waiting on condition 8 | extern pthread_cond_t condvar; 9 | extern pthread_mutex_t condvar_mutex; 10 | extern char predicate; 11 | extern int ctmark_to_delete_in, ctmark_to_delete_out; 12 | extern int ctmark_to_set; 13 | 14 | //Register a callback to delete ctmark and wait on condition to be triggered. 15 | void* thread_ct_delete_mark ( void* ptr ); 16 | //delete ct entry according to mark (e.g. when process exits and we don't want any of its established 17 | //connections to linger in ct 18 | int ct_delete_mark_cb (enum nf_conntrack_msg_type type, struct nf_conntrack *mct,void *data); 19 | 20 | //dump all ct entries every second, extract the traffic statistics and send it to frontend 21 | void * thread_ct_dump( void *ptr); 22 | //callback gets called on every packet that is dumped from ct. It build ct_array which is later 23 | //exported to frontend 24 | int ct_dump_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *mct,void *data); 25 | 26 | //Register a callback that gets triggered whenever conntrack tries to destroy a connection 27 | void * thread_ct_destroy( void *ptr); 28 | //callback gets called when conntrack deletes a ct entry internally (e.g. when TCP connection closes) 29 | //we want the deleted connections traffic statistics 30 | int ct_destroy_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *mct,void *data); 31 | 32 | //sets the same mark on a process's connections in conntrack. This way we always know which conntrack 33 | //entries belong to which process and we can collect traffic statistics 34 | int setmark_out_tcp (enum nf_conntrack_msg_type type, struct nf_conntrack *mct,void *data); 35 | int setmark_out_udp (enum nf_conntrack_msg_type type, struct nf_conntrack *mct,void *data); 36 | int setmark_out_icmp (enum nf_conntrack_msg_type type, struct nf_conntrack *mct,void *data); 37 | int setmark_in (enum nf_conntrack_msg_type type, struct nf_conntrack *mct,void *data); 38 | //---END CONNTRACK ROUTINES 39 | 40 | 41 | //modify traffic stats in ct_array for denied packets 42 | void denied_traffic_add (const int direction, const int mark, const int bytes); 43 | 44 | void* tcp_export_thread ( void *ptr ); 45 | 46 | 47 | 48 | #endif // LPFW_H 49 | -------------------------------------------------------------------------------- /gui/frontend.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 793 10 | 388 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | MainWindow 21 | 22 | 23 | 24 | 25 | 26 | 27 | 0 28 | 29 | 30 | QLayout::SetNoConstraint 31 | 32 | 33 | 0 34 | 35 | 36 | 37 | 38 | 39 | 0 40 | 0 41 | 42 | 43 | 44 | Qt::LeftToRight 45 | 46 | 47 | QAbstractItemView::NoEditTriggers 48 | 49 | 50 | false 51 | 52 | 53 | true 54 | 55 | 56 | QAbstractItemView::SingleSelection 57 | 58 | 59 | QAbstractItemView::SelectRows 60 | 61 | 62 | Qt::ElideRight 63 | 64 | 65 | false 66 | 67 | 68 | false 69 | 70 | 71 | true 72 | 73 | 74 | false 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 0 86 | 0 87 | 793 88 | 25 89 | 90 | 91 | 92 | 93 | &File 94 | 95 | 96 | 97 | 98 | 99 | 100 | &Rules 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | &Exit 115 | 116 | 117 | 118 | 119 | &About 120 | 121 | 122 | 123 | 124 | &Delete selected 125 | 126 | 127 | 128 | 129 | true 130 | 131 | 132 | false 133 | 134 | 135 | &Show active only 136 | 137 | 138 | 139 | 140 | true 141 | 142 | 143 | true 144 | 145 | 146 | false 147 | 148 | 149 | Show &all 150 | 151 | 152 | 153 | 154 | Preferences 155 | 156 | 157 | 158 | 159 | Save all to Rulesfile 160 | 161 | 162 | 163 | 164 | Preferences 165 | 166 | 167 | 168 | 169 | true 170 | 171 | 172 | true 173 | 174 | 175 | &Process ID 176 | 177 | 178 | 179 | 180 | true 181 | 182 | 183 | true 184 | 185 | 186 | P&ermission 187 | 188 | 189 | 190 | 191 | true 192 | 193 | 194 | true 195 | 196 | 197 | &Full path 198 | 199 | 200 | 201 | 202 | true 203 | 204 | 205 | true 206 | 207 | 208 | IN &allowed 209 | 210 | 211 | 212 | 213 | IN &denied 214 | 215 | 216 | 217 | 218 | true 219 | 220 | 221 | true 222 | 223 | 224 | &OUT allowed 225 | 226 | 227 | 228 | 229 | O&UT denied 230 | 231 | 232 | 233 | 234 | tableView 235 | 236 | 237 | 238 | 239 | actionProcess_ID 240 | changed() 241 | actionProcess_ID 242 | toggle() 243 | 244 | 245 | -1 246 | -1 247 | 248 | 249 | -1 250 | -1 251 | 252 | 253 | 254 | 255 | 256 | -------------------------------------------------------------------------------- /gui/gui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys, os, thread, time, string, threading, subprocess 4 | from PyQt4.QtGui import QApplication, QStandardItem, QDialog, QIcon, QMenu, QSystemTrayIcon, QStandardItemModel, QAction, QMainWindow, QListWidget, QListWidgetItem, QWidget, QIntValidator, QStyledItemDelegate, QPainter, QStyleOptionViewItem, QFont, QTableWidgetItem, QPalette, QColor, QSortFilterProxyModel 5 | import resource 6 | from PyQt4.QtCore import pyqtSignal, Qt, QModelIndex, QRect, pyqtSlot, QVariant, QString 7 | from PyQt4.QtNetwork import QHostInfo 8 | from multiprocessing import Pipe, Process, Lock 9 | from base64 import b64encode, b64decode 10 | import socket 11 | import Queue 12 | import resource_rc 13 | 14 | from PyQt4 import QtCore, QtGui, uic 15 | 16 | data_dir = os.path.dirname(os.path.realpath(__file__)) 17 | msgQueue = Queue.Queue() 18 | 19 | #global vars 20 | modellock = Lock() 21 | 22 | 23 | class Ui_MainWindow(object): 24 | def setupUi(self, MainWindow): 25 | uic.loadUi(os.path.join(data_dir, 'frontend.ui'), self) 26 | 27 | 28 | class Ui_Dialog(object): 29 | def setupUi(self, DialogOut): 30 | uic.loadUi(os.path.join(data_dir, 'popup.ui'), self) 31 | 32 | 33 | class queryDialog(QDialog): 34 | dns_lookup_id = 0 35 | host = 0 36 | path = '' 37 | pid = '' 38 | def __init__(self, direction): 39 | self.dns_lookup_id = 0 40 | QDialog.__init__(self) 41 | self.setupUi(self) 42 | if (direction == 'OUT'): 43 | text = 'is trying to connect to' 44 | if (direction == 'IN'): 45 | text = 'is being connected to from' 46 | self.setWindowTitle("Leopard Flower firewall") 47 | self.label_text.setText(text) 48 | self.pushButton_allow.clicked.connect(self.allowClicked) 49 | self.pushButton_deny.clicked.connect(self.denyClicked) 50 | self.pushButton_hide.setVisible(False) 51 | self.tableWidget_details.setVisible(False) 52 | self.rejected.connect(self.escapePressed) 53 | self.finished.connect(self.dialogFinished) 54 | 55 | fullpath_text = QTableWidgetItem("Full path") 56 | self.tableWidget_details.setItem(0,0,fullpath_text) 57 | pid_text = QTableWidgetItem("Process ID") 58 | self.tableWidget_details.setItem(1,0,pid_text) 59 | remoteip_text = QTableWidgetItem("Remote IP") 60 | self.tableWidget_details.setItem(2,0,remoteip_text) 61 | remotedomain_text = QTableWidgetItem("Remote domain") 62 | self.tableWidget_details.setItem(3,0,remotedomain_text) 63 | remoteport_text = QTableWidgetItem("Remote port") 64 | self.tableWidget_details.setItem(4,0,remoteport_text) 65 | localport_text = QTableWidgetItem("Local port") 66 | self.tableWidget_details.setItem(5,0,localport_text) 67 | #make the incoming dialog stand out. It is not common to receive incoming connections 68 | if (direction == 'IN'): 69 | pal = QPalette() 70 | col = QColor(255, 0, 0, 127) 71 | pal.setColor(QPalette.Window, col) 72 | self.setPalette(pal) 73 | 74 | 75 | def escapePressed(self): 76 | "in case when user pressed Escape" 77 | print "in escapePressed" 78 | msgQueue.put('ADD ' + b64encode(bytearray(self.path, encoding='utf-8')) + ' ' + self.pid + ' IGNORED') 79 | 80 | 81 | def closeEvent(self, event): 82 | "in case when user closed the dialog without pressing allow or deny" 83 | print "in closeEvent" 84 | msgQueue.put('ADD ' + b64encode(bytearray(self.path, encoding='utf-8')) + ' ' + self.pid + ' IGNORED') 85 | 86 | 87 | def allowClicked(self): 88 | print "allow clicked" 89 | if (self.checkBox.isChecked()): verdict = "ALLOW_ALWAYS" 90 | else: verdict = "ALLOW_ONCE" 91 | msgQueue.put('ADD ' + b64encode(bytearray(self.path, encoding='utf-8')) + ' ' + self.pid + ' ' + verdict) 92 | 93 | 94 | def denyClicked(self): 95 | print "deny clicked" 96 | if (self.checkBox.isChecked()): verdict = "DENY_ALWAYS" 97 | else: verdict = "DENY_ONCE" 98 | msgQueue.put('ADD ' + b64encode(bytearray(self.path, encoding='utf-8')) + ' ' + self.pid + ' ' + verdict) 99 | 100 | 101 | def dialogFinished(self): 102 | QHostInfo.abortHostLookup(self.dns_lookup_id) 103 | 104 | def dnsLookupFinished(self, host): 105 | if ( host.error() != QHostInfo.NoError): 106 | print "Lookup failed %s" %(host.errorString()) 107 | return 108 | hostname = host.hostName() 109 | item = QTableWidgetItem(hostname) 110 | self.tableWidget_details.setItem(3,1,item) 111 | self.label_domain.setText(hostname) 112 | 113 | 114 | class myDialogOut(queryDialog, Ui_Dialog): 115 | def __init__(self): 116 | Ui_Dialog.__init__(self) 117 | queryDialog.__init__(self, 'OUT') 118 | 119 | 120 | class myDialogIn(queryDialog, Ui_Dialog): 121 | def __init__(self): 122 | Ui_Dialog.__init__(self) 123 | queryDialog.__init__(self, 'IN') 124 | 125 | 126 | class myMainWindow(QMainWindow, Ui_MainWindow): 127 | askusersig = pyqtSignal(str, str, str, str, str, str) #connected to askUserOUT 128 | refreshmodelsig = pyqtSignal(str) 129 | update_bytestatssig = pyqtSignal(str) 130 | prevstats = '' 131 | model = None 132 | sourcemodel = None 133 | 134 | def __init__(self): 135 | QMainWindow.__init__(self) 136 | self.setupUi(self) 137 | self.setWindowTitle("Leopard Flower firewall") 138 | self.setWindowIcon(QIcon(":/pics/pic.jpg")) 139 | self.tableView.setShowGrid(False) 140 | self.menuRules.aboutToShow.connect(self.rulesMenuTriggered) 141 | self.menuRules.actions()[0].triggered.connect(self.deleteMenuTriggered) 142 | self.actionShow_active_only.triggered.connect(self.showActiveOnly) 143 | self.actionShow_all.triggered.connect(self.showAll) 144 | self.actionExit.triggered.connect(self.realQuit) 145 | self.askusersig.connect(self.askUser) 146 | self.update_bytestatssig.connect(self.update_bytestats) 147 | self.refreshmodelsig.connect(self.refreshmodel) 148 | msgQueue.put('LIST') 149 | 150 | def showActiveOnly(self): 151 | self.model.toggle_mode_sig.emit('SHOW ACTIVE ONLY') 152 | self.actionShow_active_only.setEnabled(False) 153 | self.actionShow_all.setEnabled(True) 154 | self.actionShow_all.setChecked(False) 155 | 156 | def showAll(self): 157 | self.model.toggle_mode_sig.emit('SHOW ALL') 158 | self.actionShow_active_only.setEnabled(True) 159 | self.actionShow_all.setEnabled(False) 160 | self.actionShow_active_only.setChecked(False) 161 | 162 | 163 | @pyqtSlot(str, str, str, str, str, str) 164 | def askUser(self, req_str_in, path_in, pid_in, addr_in, dport_in, sport_in): 165 | print "In askUser" 166 | #Convert all incoming QString into normal python strings 167 | req_str = str(req_str_in) 168 | path = unicode(QString.fromUtf8(path_in)) 169 | pid = str(pid_in) 170 | addr = str(addr_in) 171 | dport = str(dport_in) 172 | sport = str(sport_in) 173 | if (req_str == 'REQUEST_OUT'): 174 | dialog = dialogOut 175 | rport = sport 176 | lport = dport 177 | elif (req_str == 'REQUEST_IN'): 178 | dialog = dialogOut 179 | rport = dport 180 | lport = sport 181 | 182 | name = string.rsplit(path,"/",1) 183 | dialog.path = path 184 | dialog.pid = pid 185 | dialog.label_name.setText(name[1]) 186 | dialog.label_ip.setText(addr) 187 | dialog.label_domain.setText("Looking up DNS...") 188 | fullpath = QTableWidgetItem(unicode(QString.fromUtf8(path))) 189 | dialog.tableWidget_details.setItem(0,1,fullpath) 190 | pid_item = QTableWidgetItem(pid) 191 | dialog.tableWidget_details.setItem(1,1,pid_item) 192 | remoteip = QTableWidgetItem(addr) 193 | dialog.tableWidget_details.setItem(2,1,remoteip) 194 | dns = QTableWidgetItem("Looking up DNS...") 195 | dialog.tableWidget_details.setItem(3,1,dns) 196 | rport_item = QTableWidgetItem(rport) 197 | dialog.tableWidget_details.setItem(4,1,rport_item) 198 | lport_item = QTableWidgetItem(lport) 199 | dialog.tableWidget_details.setItem(5,1,lport_item) 200 | QHostInfo.lookupHost(addr, dialog.dnsLookupFinished) 201 | #we don't want the user to accidentally trigger ALLOW 202 | dialog.pushButton_deny.setFocus() 203 | dialog.show() 204 | 205 | 206 | 207 | def rulesMenuTriggered(self): 208 | "If no rules are selected in the view, grey out the Delete... item" 209 | if (len(self.tableView.selectedIndexes()) == 0): 210 | self.menuRules.actions()[0].setEnabled(False) 211 | else: 212 | self.menuRules.actions()[0].setEnabled(True) 213 | 214 | 215 | def deleteMenuTriggered(self): 216 | "send delete request to backend" 217 | selected_indexes = self.tableView.selectedIndexes() 218 | bFound = False 219 | for index in selected_indexes: 220 | if index.column() != 3: continue 221 | path = unicode(index.data().toPyObject()) 222 | bFound = True 223 | break 224 | if not bFound: 225 | print 'Could not find the path to delete' 226 | return 227 | msgQueue.put('DELETE ' + b64encode(bytearray(path, encoding='utf-8'))) 228 | 229 | 230 | def closeEvent(self, event): 231 | event.ignore() 232 | self.hide() 233 | 234 | 235 | def realQuit(self): 236 | print "see you later..." 237 | msgQueue.put('UNREGISTER') 238 | time.sleep(2) #allow queue to be processed 239 | exit(1) 240 | 241 | @pyqtSlot(unicode) 242 | def refreshmodel(self, data_in): 243 | "Fill the frontend with rules data" 244 | rawstr = unicode(QString.fromUtf8(data_in)) 245 | 246 | export_list = [] 247 | rules = rawstr[len('RULES_LIST'):].split('CRLF') 248 | for one_rule in rules: 249 | split_rule = one_rule.split() 250 | if len(split_rule) != 5: continue 251 | export_list.append(split_rule) 252 | export_list.append("EOF") 253 | ruleslist = export_list 254 | #empty the model, we're filling it anew, we can't use clear() cause it flushes headers too: 255 | modellock.acquire() 256 | self.sourcemodel.layoutAboutToBeChanged.emit() 257 | self.sourcemodel.removeRows(0, self.sourcemodel.rowCount()) 258 | 259 | #if there's only one element, it's EOF; dont go through iterations,just leave the model empty 260 | if (len(ruleslist) == 1): 261 | self.sourcemodel.layoutChanged.emit() 262 | modellock.release() 263 | return 264 | for item in ruleslist[0:-1]:#leave out the last EOF from iteration 265 | path_u = b64decode(item[0]).decode('utf-8') 266 | fullpath = QStandardItem(path_u) 267 | #item[4] contains nfmark 268 | fullpath.setData(item[4]) 269 | if (item[1] == "0"): 270 | pid_string = "N/A" 271 | else: 272 | pid_string = item[1] 273 | pid = QStandardItem(pid_string) 274 | perms = QStandardItem(item[2]) 275 | #only the name of the executable after the last / 276 | m_list = string.rsplit(path_u,"/",1) 277 | m_name = m_list[1] 278 | name = QStandardItem(m_name) 279 | in_allow_traf = QStandardItem() 280 | out_allow_traf = QStandardItem() 281 | in_deny_traf = QStandardItem() 282 | out_deny_traf = QStandardItem() 283 | self.sourcemodel.appendRow( (name, pid, perms, fullpath, 284 | in_allow_traf, out_allow_traf, in_deny_traf, out_deny_traf) ) 285 | #print "Received: %s" %(item[0]) 286 | self.sourcemodel.layoutChanged.emit() 287 | modellock.release() 288 | self.update_bytestats() 289 | 290 | 291 | #Update and remember the stats. The stats may be restored from 292 | #a previous save when model refreshes 293 | @pyqtSlot(str) 294 | def update_bytestats(self, data_in = ''): 295 | data = str(data_in) 296 | items = [] 297 | if (data): 298 | message = data.split('EOL')[-2] #discard the last empty one and take the one before it 299 | #take the last message with 5 elements as it is the most recent one 300 | items = message.split('CRLF')[:-1] #discard the last empty one 301 | if (items): 302 | self.prevstats = items 303 | else: 304 | items = self.prevstats 305 | #one item looks like '1212 3232 3243 4343 43434' 306 | modellock.acquire() 307 | self.sourcemodel.layoutAboutToBeChanged.emit() 308 | for one_item in items: 309 | fields = one_item.split() 310 | for j in range(self.sourcemodel.rowCount()): 311 | #4th element of each line has nfmark in its data field 312 | if (self.sourcemodel.item(j,3).data().toString() != fields[0]): continue 313 | #else 314 | self.sourcemodel.item(j,4).setText(fields[1]) 315 | self.sourcemodel.item(j,5).setText(fields[2]) 316 | self.sourcemodel.item(j,6).setText(fields[3]) 317 | self.sourcemodel.item(j,7).setText(fields[4]) 318 | break 319 | self.sourcemodel.layoutChanged.emit() 320 | modellock.release() 321 | 322 | 323 | 324 | class CustomDelegate (QStyledItemDelegate): 325 | def __init__ (self): 326 | QStyledItemDelegate.__init__(self) 327 | def paint (self, painter, option, index): 328 | model = index.model() 329 | item = model.data(index).toPyObject() 330 | if item == None: 331 | return 332 | text = str(item) 333 | if (len(text) > 6): 334 | # take only megabytes -->12<--345678 335 | mb = text[:len(text)-6] 336 | bytes = text[len(text)-6:] 337 | painter.setPen (Qt.red) 338 | painter.drawText (option.rect,Qt.AlignHCenter and Qt.AlignVCenter, mb) 339 | painter.setPen (Qt.black) 340 | rect = QRect() 341 | rect = option.rect 342 | rect.setX(rect.x()+8*(len(mb))) 343 | painter.drawText (rect, Qt.AlignHCenter and Qt.AlignVCenter, bytes) 344 | else: 345 | painter.drawText (option.rect, Qt.AlignHCenter and Qt.AlignVCenter, text) 346 | 347 | 348 | 349 | 350 | def conntrackThread(): 351 | with open('/tmp/lpfwctport', 'r') as f: ctport = f.read() 352 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 353 | sock.connect(('127.0.0.1', int(ctport))) 354 | sock.settimeout(1) 355 | while True: 356 | data = '' 357 | try: data = sock.recv(8192) 358 | except: continue 359 | if not data: 360 | time.sleep(1) 361 | continue 362 | #print ('RECEIVED: ' + data) 363 | window.update_bytestatssig.emit(data) 364 | 365 | 366 | 367 | def communicationThread(): 368 | with open('/tmp/lpfwcommport', 'r') as f: commport = f.read() 369 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 370 | sock.connect(('127.0.0.1', int(commport))) 371 | sock.settimeout(1) 372 | while True: 373 | try: 374 | send_data = '' 375 | send_data = msgQueue.get_nowait() 376 | if send_data == '': 377 | raise Exception ('no data to send') 378 | print ('Sending:', send_data) 379 | sock.send(bytearray(send_data, encoding='utf-8')) 380 | except: 381 | pass #no data in msgQueue 382 | data = '' 383 | try: 384 | data = sock.recv(8192) 385 | except: 386 | continue 387 | if not data: 388 | time.sleep(1) 389 | continue 390 | 391 | #print ('RECEIVED: ' + data) 392 | messages = data.split('EOL') 393 | rules_list = '' 394 | request = '' 395 | #the last rules_list it is the most recent one 396 | for msg in messages[::-1]: 397 | if not msg.startswith('RULES_LIST'): continue 398 | #else 399 | rules_list = msg 400 | break 401 | #take the first REQUEST (there should be only one anyway) 402 | for msg in messages: 403 | if not msg.startswith('REQUEST'): continue 404 | #else 405 | request = msg 406 | break 407 | if (rules_list): 408 | window.refreshmodelsig.emit(rules_list) 409 | if (request): 410 | split_request = request.split() 411 | req_str = split_request[0] 412 | path = b64decode(split_request[1]) 413 | pid = split_request[2] 414 | addr = split_request[4] 415 | dport = split_request[5] 416 | sport = split_request[6] 417 | window.askusersig.emit(req_str, path, pid, addr, dport, sport) 418 | 419 | 420 | class myModel(QStandardItemModel): 421 | layout_changed_sig = pyqtSignal() 422 | 423 | def __init__(self): 424 | QStandardItemModel.__init__(self) 425 | self.layout_changed_sig.connect(self.layout_changed) 426 | self.setHorizontalHeaderLabels(("Name","Process ID","Permission", 427 | "Full path","Allowed in","Allowed out", 428 | "Denied in","Denied out")) 429 | 430 | @pyqtSlot() 431 | def layout_changed(self): 432 | self.layoutAboutToBeChanged.emit() 433 | self.layoutChanged.emit() 434 | 435 | 436 | 437 | class mySortFilterProxyModel(QSortFilterProxyModel): 438 | toggle_mode_sig = pyqtSignal(str) 439 | mode = 'SHOW ALL' 440 | 441 | def __init__(self): 442 | QSortFilterProxyModel.__init__(self) 443 | self.toggle_mode_sig.connect(self.toggle_mode) 444 | 445 | @pyqtSlot(str) 446 | def toggle_mode(self, mode_in): 447 | mode = str(mode_in) 448 | self.mode = mode 449 | self.sourceModel().layoutAboutToBeChanged.emit() 450 | self.sourceModel().layoutChanged.emit() 451 | 452 | 453 | def headerData(self, section, orientation, role): 454 | if orientation != Qt.Vertical or role != Qt.DisplayRole: 455 | return QSortFilterProxyModel.headerData(self, section, orientation, role) 456 | return section+1 457 | 458 | 459 | def filterAcceptsRow(self, row, parent): 460 | if self.mode == 'SHOW ALL': 461 | return True 462 | #else mode == 'SHOW ACTIVE ONLY' 463 | smodel = self.sourceModel() 464 | pid = str(smodel.itemFromIndex(smodel.index(row,1)).text()) 465 | if (pid == 'N/A'): 466 | return False 467 | else: 468 | return True 469 | 470 | 471 | def lessThan(self, left, right): 472 | if left.column() not in (1,4,5,6,7): 473 | return QSortFilterProxyModel.lessThan(self, left, right) 474 | model = self.sourceModel() 475 | try: 476 | leftint = int(model.data(left).toPyObject()) 477 | except: 478 | leftint = 0 479 | try: 480 | rightint = int(model.data(right).toPyObject()) 481 | except: 482 | rightint = 0 483 | return leftint < rightint 484 | 485 | 486 | 487 | 488 | if __name__ == "__main__": 489 | #don't clutter console with debuginfo 490 | if (len(sys.argv) <= 1 or sys.argv[1] != "debug"): 491 | #I don't know how to redirect output to /dev/null so just make a tmp file until I figure out 492 | #logfile = open("/dev/null", "w") 493 | #sys.stdout = logfile 494 | pass 495 | elif (sys.argv[1] != "debug"): 496 | import wingdbstub 497 | 498 | app=QApplication(sys.argv) 499 | app.setQuitOnLastWindowClosed(False) 500 | window = myMainWindow() 501 | 502 | tray = QSystemTrayIcon(QIcon(":/pics/pic24.png")) 503 | menu = QMenu() 504 | actionShow = QAction("Show Leopard Flower",menu) 505 | actionExit = QAction("Exit",menu) 506 | menu.addAction(actionShow) 507 | menu.addAction(actionExit) 508 | tray.setContextMenu(menu) 509 | tray.show() 510 | actionShow.triggered.connect(window.show) 511 | actionExit.triggered.connect(window.realQuit) 512 | 513 | sourcemodel = myModel() 514 | model = mySortFilterProxyModel() 515 | model.setSourceModel(sourcemodel) 516 | model.setDynamicSortFilter(True) 517 | 518 | window.tableView.setSortingEnabled(True) 519 | window.tableView.setModel(model) 520 | window.model = model 521 | window.sourcemodel = sourcemodel 522 | 523 | delegate = CustomDelegate() 524 | window.tableView.setItemDelegateForColumn(4,delegate) 525 | window.tableView.setItemDelegateForColumn(5,delegate) 526 | window.tableView.setItemDelegateForColumn(6,delegate) 527 | window.tableView.setItemDelegateForColumn(7,delegate) 528 | 529 | dialogOut = myDialogOut() 530 | dialogIn = myDialogIn() 531 | 532 | thread = threading.Thread(target= communicationThread) 533 | thread.daemon = True 534 | thread.start() 535 | thread = threading.Thread(target= conntrackThread) 536 | thread.daemon = True 537 | thread.start() 538 | 539 | window.show() 540 | sys.exit(app.exec_()) -------------------------------------------------------------------------------- /gui/popup.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | DialogOut 4 | 5 | 6 | 7 | 0 8 | 0 9 | 523 10 | 459 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 255 26 | 255 27 | 255 28 | 29 | 30 | 31 | 32 | 33 | 34 | 170 35 | 170 36 | 127 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 255 46 | 255 47 | 255 48 | 49 | 50 | 51 | 52 | 53 | 54 | 170 55 | 170 56 | 127 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 170 66 | 170 67 | 127 68 | 69 | 70 | 71 | 72 | 73 | 74 | 170 75 | 170 76 | 127 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | Dialog 85 | 86 | 87 | 88 | :/pics/pic.jpg:/pics/pic.jpg 89 | 90 | 91 | 92 | QLayout::SetFixedSize 93 | 94 | 95 | 0 96 | 97 | 98 | 0 99 | 100 | 101 | 102 | 103 | QLayout::SetFixedSize 104 | 105 | 106 | 0 107 | 108 | 109 | 110 | 111 | QLayout::SetMinimumSize 112 | 113 | 114 | 13 115 | 116 | 117 | 0 118 | 119 | 120 | 121 | 122 | 123 | Ubuntu 124 | 16 125 | 75 126 | true 127 | 128 | 129 | 130 | Application name 131 | 132 | 133 | true 134 | 135 | 136 | Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 0 151 | 0 152 | 0 153 | 154 | 155 | 156 | 157 | 158 | 159 | 170 160 | 170 161 | 127 162 | 163 | 164 | 165 | 166 | 167 | 168 | 170 169 | 170 170 | 255 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 0 180 | 0 181 | 0 182 | 183 | 184 | 185 | 186 | 187 | 188 | 170 189 | 170 190 | 127 191 | 192 | 193 | 194 | 195 | 196 | 197 | 170 198 | 170 199 | 255 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 106 209 | 104 210 | 100 211 | 212 | 213 | 214 | 215 | 216 | 217 | 170 218 | 170 219 | 255 220 | 221 | 222 | 223 | 224 | 225 | 226 | 170 227 | 170 228 | 255 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | Ubuntu 238 | 12 239 | 50 240 | false 241 | 242 | 243 | 244 | is trying to connect to 245 | 246 | 247 | Qt::PlainText 248 | 249 | 250 | true 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 50 259 | false 260 | 261 | 262 | 263 | IP address 264 | 265 | 266 | Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse 267 | 268 | 269 | 270 | 271 | 272 | 273 | Qt::Horizontal 274 | 275 | 276 | 277 | 40 278 | 20 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | Ubuntu 292 | 12 293 | 75 294 | true 295 | 296 | 297 | 298 | Looking up DNS... 299 | 300 | 301 | Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse 302 | 303 | 304 | 305 | 306 | 307 | 308 | Qt::Horizontal 309 | 310 | 311 | 312 | 40 313 | 20 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 0 325 | 0 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | QLayout::SetFixedSize 334 | 335 | 336 | 337 | 338 | &Allow 339 | 340 | 341 | 342 | 343 | 344 | 345 | Qt::Horizontal 346 | 347 | 348 | 349 | 40 350 | 20 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | true 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 0 370 | 0 371 | 127 372 | 373 | 374 | 375 | 376 | 377 | 378 | 0 379 | 0 380 | 127 381 | 382 | 383 | 384 | 385 | 386 | 387 | 0 388 | 0 389 | 127 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 0 399 | 0 400 | 127 401 | 402 | 403 | 404 | 405 | 406 | 407 | 0 408 | 0 409 | 127 410 | 411 | 412 | 413 | 414 | 415 | 416 | 0 417 | 0 418 | 127 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 106 428 | 104 429 | 100 430 | 431 | 432 | 433 | 434 | 435 | 436 | 106 437 | 104 438 | 100 439 | 440 | 441 | 442 | 443 | 444 | 445 | 106 446 | 104 447 | 100 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | true 457 | 458 | 459 | 460 | PointingHandCursor 461 | 462 | 463 | Qt::TabFocus 464 | 465 | 466 | &Show details... 467 | 468 | 469 | 470 | 1 471 | 16 472 | 473 | 474 | 475 | false 476 | 477 | 478 | true 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 0 491 | 0 492 | 127 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 0 502 | 0 503 | 127 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 106 513 | 104 514 | 100 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | true 524 | 525 | 526 | 527 | PointingHandCursor 528 | 529 | 530 | Qt::NoFocus 531 | 532 | 533 | &Hide details... 534 | 535 | 536 | false 537 | 538 | 539 | true 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | Qt::Horizontal 550 | 551 | 552 | 553 | 40 554 | 20 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | &Deny 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | &Remeber my choice for all future connection attempts 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | Qt::Horizontal 582 | 583 | 584 | QSizePolicy::Minimum 585 | 586 | 587 | 588 | 0 589 | 0 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | QLayout::SetMaximumSize 598 | 599 | 600 | 601 | 602 | 603 | 0 604 | 0 605 | 606 | 607 | 608 | 609 | 610 | 611 | :/pics/tux.jpg 612 | 613 | 614 | 615 | 616 | 617 | 618 | Qt::Vertical 619 | 620 | 621 | 622 | 0 623 | 0 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 0 636 | 637 | 638 | QLayout::SetMinimumSize 639 | 640 | 641 | 13 642 | 643 | 644 | 0 645 | 646 | 647 | 648 | 649 | 650 | 0 651 | 0 652 | 653 | 654 | 655 | Qt::NoFocus 656 | 657 | 658 | false 659 | 660 | 661 | QFrame::StyledPanel 662 | 663 | 664 | Qt::ScrollBarAlwaysOff 665 | 666 | 667 | Qt::ScrollBarAlwaysOff 668 | 669 | 670 | QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed 671 | 672 | 673 | true 674 | 675 | 676 | Qt::SolidLine 677 | 678 | 679 | true 680 | 681 | 682 | 7 683 | 684 | 685 | 2 686 | 687 | 688 | false 689 | 690 | 691 | 141 692 | 693 | 694 | true 695 | 696 | 697 | false 698 | 699 | 700 | 27 701 | 702 | 703 | false 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | Qt::Vertical 722 | 723 | 724 | 725 | 20 726 | 1 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | pushButton_show 735 | pushButton_allow 736 | pushButton_deny 737 | checkBox 738 | 739 | 740 | 741 | 742 | 743 | 744 | pushButton_show 745 | clicked() 746 | pushButton_show 747 | hide() 748 | 749 | 750 | 192 751 | 107 752 | 753 | 754 | 192 755 | 107 756 | 757 | 758 | 759 | 760 | pushButton_show 761 | clicked() 762 | pushButton_hide 763 | show() 764 | 765 | 766 | 192 767 | 107 768 | 769 | 770 | 192 771 | 139 772 | 773 | 774 | 775 | 776 | pushButton_hide 777 | clicked() 778 | pushButton_hide 779 | hide() 780 | 781 | 782 | 205 783 | 147 784 | 785 | 786 | 205 787 | 147 788 | 789 | 790 | 791 | 792 | pushButton_hide 793 | clicked() 794 | pushButton_show 795 | show() 796 | 797 | 798 | 205 799 | 147 800 | 801 | 802 | 205 803 | 115 804 | 805 | 806 | 807 | 808 | pushButton_hide 809 | clicked() 810 | tableWidget_details 811 | hide() 812 | 813 | 814 | 205 815 | 146 816 | 817 | 818 | 258 819 | 310 820 | 821 | 822 | 823 | 824 | pushButton_show 825 | clicked() 826 | tableWidget_details 827 | show() 828 | 829 | 830 | 205 831 | 115 832 | 833 | 834 | 258 835 | 310 836 | 837 | 838 | 839 | 840 | pushButton_allow 841 | clicked() 842 | DialogOut 843 | hide() 844 | 845 | 846 | 60 847 | 130 848 | 849 | 850 | 261 851 | 210 852 | 853 | 854 | 855 | 856 | pushButton_deny 857 | clicked() 858 | DialogOut 859 | hide() 860 | 861 | 862 | 357 863 | 130 864 | 865 | 866 | 261 867 | 210 868 | 869 | 870 | 871 | 872 | 873 | -------------------------------------------------------------------------------- /gui/resource.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | pic.jpg 4 | tux.jpg 5 | pic24.png 6 | 7 | 8 | -------------------------------------------------------------------------------- /lpfw.h: -------------------------------------------------------------------------------- 1 | #ifndef LPFW_H 2 | #define LPFW_H 3 | 4 | #include 5 | #include "common/defines.h" 6 | #include "common/includes.h" 7 | #include 8 | #include // for FILE* 9 | using namespace std; 10 | 11 | //PID of currently active frontend 12 | extern pid_t fe_pid; 13 | //Pointer to function which does the logging 14 | extern int (*m_printf)(const int loglevel, const char *logstring); 15 | //enables other files to use the logging facility 16 | extern pthread_mutex_t logstring_mutex; 17 | extern char logstring[PATHSIZE]; 18 | 19 | //add a new rule and if ctmark is not specified, return a new ctmark for the newly added rule 20 | int ruleslist_add ( const char *path, const char *pid, const char *perms, const bool current, const char *sha, 21 | const unsigned long long stime, const off_t size, const int ctmark, const unsigned char first_instance ); 22 | 23 | //remove rule from ruleslist 24 | void ruleslist_delete_one ( const char *path, const char *pid ); 25 | 26 | //builds correlation tables between ports and sockets for variour /proc/net/* files and at the same time 27 | //checks if port_to_find is in the table. Returns socket corresponding to port_to_find or -1 28 | //if port_to_find was not found 29 | int build_tcp_port_and_socket_cache(long &socket_out, const int port_in); 30 | int build_tcp6_port_and_socket_cache(long &socket_out, const int port_in); 31 | int build_udp_port_and_socket_cache(long &socket_out, const int port_in); 32 | int build_udp6_port_and_socket_cache(long &socket_out, const int port_in); 33 | //returns socket corresponding to port or -1 if not found 34 | unsigned long is_port_in_cache(const int port, const int proto); 35 | unsigned long is_udp_port_in_cache(const int port); 36 | 37 | //print logstring to the preffered logging facility. A pointer to one of these functions is assigned 38 | //to m_printf 39 | int m_printf_stdout ( const int loglevel, const char * logstring ); 40 | int m_printf_file ( const int loglevel, const char * logstring ); 41 | int m_printf_syslog (const int loglevel, const char * logstring); 42 | 43 | //find socket in pid_and_socket cache of active rules only and return path,pid,ctmark if found 44 | int search_pid_and_socket_cache(const long socket_in, string &path_out, 45 | string &pid_out, int &ctmark_out); 46 | //build a correlation of pid to socket of only the active rules, excluding inkernel rules 47 | void* thread_build_pid_and_socket_cache ( void *ptr ); 48 | 49 | //thread-safe getter and setter for fe_active_flag 50 | int fe_active_flag_get(); 51 | void fe_active_flag_set (const unsigned char boolean); 52 | 53 | //threads which receive packets from NFQUEUE and call handlers on those pakets 54 | //NB nfq_out_tcp loop is at the end of main() 55 | void* thread_nfq_in ( void *ptr ); 56 | void* thread_nfq_out ( void *ptr ); 57 | 58 | //handlers for NFQUEUE traffic 59 | int nfq_handle ( struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfad, void *mdata ); 60 | 61 | //find the process which owns the socket and return ctmark,path,pid,stime, otherwise SOCKET_NOT_FOUND_IN_PROCPIDFD 62 | int socket_handle ( const long *socket, int *ctmark_to_set, char *path, char *pid, u_int64_t *stime); 63 | 64 | //determine if port belongs to a in-kernel process. Kernel modules can open sockets but the have no PID 65 | int inkernel_check_udp(const int port); 66 | int inkernel_check(const int port, const int proto); 67 | //if in-kernel socket found, see if there is a rule for it already 68 | int inkernel_get_verdict(const char *ipaddr_in, int &ctmark_out); 69 | 70 | //SET/CLEAR capability of a set 71 | void capabilities_modify(const int capability, const int set, const int action); 72 | 73 | //Get pid's starttime 74 | unsigned long long starttimeGet ( const int pid ); 75 | 76 | //check which active rules are still running and remove them if they are not running anymore 77 | void* thread_refresh ( void* ptr ); 78 | 79 | //process rules.conf 80 | void global_rule_add( const char *str_direction, char *str_ports); 81 | void rules_load(); 82 | void rules_write(bool mutex_being_held = false); 83 | void add_to_rulesfile( const char *executable); 84 | 85 | //chack if path+pid combo is already in ruleslist 86 | int path_find_in_rules (int &ctmark_out, const string path_in, 87 | const string pid_in, unsigned long long stime_in, bool going_out); 88 | 89 | //search socket in /proc//fd of the active rules 90 | int socket_active_processes_search (const long mysocket_in, string &m_path_out, string &m_pid_out, int &ctmark_out); 91 | 92 | //search socket in the whole of /proc/s 93 | int socket_procpidfd_search (const long mysocket_in, char *m_path_out, char *m_pid_out, u_int64_t stime_out ); 94 | 95 | //if there's >1 entry in /proc/net/raw for icmp, it's impossible to tell which process is sending the packet 96 | int icmp_check_only_one_socket ( long *socket ); 97 | 98 | //contruct a string to print to logging facility 99 | void print_traffic_log(const int proto, const int direction, const string ip, const int srcport, 100 | const int dstport, const string path, const string pid, const int verdict); 101 | 102 | //setup logging facility 103 | void init_log(); 104 | 105 | void pidfile_check(); 106 | void setup_signal_handlers(); 107 | void SIGTERM_handler ( int signal ); 108 | int parse_command_line(int argc, char* argv[]); 109 | 110 | // chack that we have the needed capabilities and if we do, then drop all the other capabilities 111 | void capabilities_setup(); 112 | 113 | // Create group lpfwuser. Backend and frontend both should belong to this group to communicate over IPC 114 | void setgid_lpfwuser(); 115 | 116 | // uid == 0. It is not full-fledged root because it has stripped capabilities 117 | void setuid_root(); 118 | void init_iptables(); 119 | void* iptables_check_thread (void *ptr); 120 | void init_nfqueue(); 121 | void init_ruleslist(); 122 | void open_proc_net_files(); 123 | void init_conntrack(); 124 | 125 | // USED FOR TESTING AND DEBUGGING 126 | int port2socket_udp ( int *portint, int *socketint ); 127 | int port2socket_tcp ( int *portint, int *socketint ); 128 | //dump all rules to a files 129 | void* rules_dump_thread ( void *ptr ); 130 | 131 | extern void* thread_test ( void *ptr ); //from testmain.cpp 132 | int send_request(const string path, const string pid, const string starttime, 133 | const string raddr, const string rport, const string lport, const int direction); 134 | string get_sha256_hexdigest(string exe_path); 135 | 136 | #endif // LPFW_H 137 | -------------------------------------------------------------------------------- /sha256/sha256.c: -------------------------------------------------------------------------------- 1 | /* sha256.c - Functions to compute SHA256 and SHA224 message digest of files or 2 | memory blocks according to the NIST specification FIPS-180-2. 3 | 4 | Copyright (C) 2005-2006, 2008-2013 Free Software Foundation, Inc. 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . */ 18 | 19 | /* Written by David Madore, considerably copypasting from 20 | Scott G. Miller's sha1.c 21 | */ 22 | 23 | 24 | #if HAVE_OPENSSL_SHA256 25 | # define GL_OPENSSL_INLINE _GL_EXTERN_INLINE 26 | #endif 27 | #include "sha256.h" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #if USE_UNLOCKED_IO 35 | # include "unlocked-io.h" 36 | #endif 37 | 38 | #ifdef WORDS_BIGENDIAN 39 | # define SWAP(n) (n) 40 | #else 41 | # define SWAP(n) \ 42 | (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) 43 | #endif 44 | 45 | #define BLOCKSIZE 32768 46 | #if BLOCKSIZE % 64 != 0 47 | # error "invalid BLOCKSIZE" 48 | #endif 49 | 50 | #if ! HAVE_OPENSSL_SHA256 51 | /* This array contains the bytes used to pad the buffer to the next 52 | 64-byte boundary. */ 53 | static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; 54 | 55 | 56 | /* 57 | Takes a pointer to a 256 bit block of data (eight 32 bit ints) and 58 | initializes it to the start constants of the SHA256 algorithm. This 59 | must be called before using hash in the call to sha256_hash 60 | */ 61 | void 62 | sha256_init_ctx (struct sha256_ctx *ctx) 63 | { 64 | ctx->state[0] = 0x6a09e667UL; 65 | ctx->state[1] = 0xbb67ae85UL; 66 | ctx->state[2] = 0x3c6ef372UL; 67 | ctx->state[3] = 0xa54ff53aUL; 68 | ctx->state[4] = 0x510e527fUL; 69 | ctx->state[5] = 0x9b05688cUL; 70 | ctx->state[6] = 0x1f83d9abUL; 71 | ctx->state[7] = 0x5be0cd19UL; 72 | 73 | ctx->total[0] = ctx->total[1] = 0; 74 | ctx->buflen = 0; 75 | } 76 | 77 | void 78 | sha224_init_ctx (struct sha256_ctx *ctx) 79 | { 80 | ctx->state[0] = 0xc1059ed8UL; 81 | ctx->state[1] = 0x367cd507UL; 82 | ctx->state[2] = 0x3070dd17UL; 83 | ctx->state[3] = 0xf70e5939UL; 84 | ctx->state[4] = 0xffc00b31UL; 85 | ctx->state[5] = 0x68581511UL; 86 | ctx->state[6] = 0x64f98fa7UL; 87 | ctx->state[7] = 0xbefa4fa4UL; 88 | 89 | ctx->total[0] = ctx->total[1] = 0; 90 | ctx->buflen = 0; 91 | } 92 | 93 | /* Copy the value from v into the memory location pointed to by *cp, 94 | If your architecture allows unaligned access this is equivalent to 95 | * (uint32_t *) cp = v */ 96 | static void 97 | set_uint32 (char *cp, uint32_t v) 98 | { 99 | memcpy (cp, &v, sizeof v); 100 | } 101 | 102 | /* Put result from CTX in first 32 bytes following RESBUF. The result 103 | must be in little endian byte order. */ 104 | void * 105 | sha256_read_ctx (const struct sha256_ctx *ctx, void *resbuf) 106 | { 107 | int i; 108 | char *r = (char *)resbuf; 109 | 110 | for (i = 0; i < 8; i++) 111 | set_uint32 (r + i * sizeof ctx->state[0], SWAP (ctx->state[i])); 112 | 113 | return resbuf; 114 | } 115 | 116 | void * 117 | sha224_read_ctx (const struct sha256_ctx *ctx, void *resbuf) 118 | { 119 | int i; 120 | char *r = (char *)resbuf; 121 | 122 | for (i = 0; i < 7; i++) 123 | set_uint32 (r + i * sizeof ctx->state[0], SWAP (ctx->state[i])); 124 | 125 | return resbuf; 126 | } 127 | 128 | /* Process the remaining bytes in the internal buffer and the usual 129 | prolog according to the standard and write the result to RESBUF. */ 130 | static void 131 | sha256_conclude_ctx (struct sha256_ctx *ctx) 132 | { 133 | /* Take yet unprocessed bytes into account. */ 134 | size_t bytes = ctx->buflen; 135 | size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; 136 | 137 | /* Now count remaining bytes. */ 138 | ctx->total[0] += bytes; 139 | if (ctx->total[0] < bytes) 140 | ++ctx->total[1]; 141 | 142 | /* Put the 64-bit file length in *bits* at the end of the buffer. 143 | Use set_uint32 rather than a simple assignment, to avoid risk of 144 | unaligned access. */ 145 | set_uint32 ((char *) &ctx->buffer[size - 2], 146 | SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29))); 147 | set_uint32 ((char *) &ctx->buffer[size - 1], 148 | SWAP (ctx->total[0] << 3)); 149 | 150 | memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); 151 | 152 | /* Process last bytes. */ 153 | sha256_process_block (ctx->buffer, size * 4, ctx); 154 | } 155 | 156 | void * 157 | sha256_finish_ctx (struct sha256_ctx *ctx, void *resbuf) 158 | { 159 | sha256_conclude_ctx (ctx); 160 | return sha256_read_ctx (ctx, resbuf); 161 | } 162 | 163 | void * 164 | sha224_finish_ctx (struct sha256_ctx *ctx, void *resbuf) 165 | { 166 | sha256_conclude_ctx (ctx); 167 | return sha224_read_ctx (ctx, resbuf); 168 | } 169 | #endif 170 | 171 | /* Compute SHA256 message digest for bytes read from STREAM. The 172 | resulting message digest number will be written into the 32 bytes 173 | beginning at RESBLOCK. */ 174 | int 175 | sha256_stream (FILE *stream, void *resblock) 176 | { 177 | struct sha256_ctx ctx; 178 | size_t sum; 179 | 180 | char *buffer = (char *)malloc (BLOCKSIZE + 72); 181 | if (!buffer) 182 | return 1; 183 | 184 | /* Initialize the computation context. */ 185 | sha256_init_ctx (&ctx); 186 | 187 | /* Iterate over full file contents. */ 188 | while (1) 189 | { 190 | /* We read the file in blocks of BLOCKSIZE bytes. One call of the 191 | computation function processes the whole buffer so that with the 192 | next round of the loop another block can be read. */ 193 | size_t n; 194 | sum = 0; 195 | 196 | /* Read block. Take care for partial reads. */ 197 | while (1) 198 | { 199 | n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); 200 | 201 | sum += n; 202 | 203 | if (sum == BLOCKSIZE) 204 | break; 205 | 206 | if (n == 0) 207 | { 208 | /* Check for the error flag IFF N == 0, so that we don't 209 | exit the loop after a partial read due to e.g., EAGAIN 210 | or EWOULDBLOCK. */ 211 | if (ferror (stream)) 212 | { 213 | free (buffer); 214 | return 1; 215 | } 216 | goto process_partial_block; 217 | } 218 | 219 | /* We've read at least one byte, so ignore errors. But always 220 | check for EOF, since feof may be true even though N > 0. 221 | Otherwise, we could end up calling fread after EOF. */ 222 | if (feof (stream)) 223 | goto process_partial_block; 224 | } 225 | 226 | /* Process buffer with BLOCKSIZE bytes. Note that 227 | BLOCKSIZE % 64 == 0 228 | */ 229 | sha256_process_block (buffer, BLOCKSIZE, &ctx); 230 | } 231 | 232 | process_partial_block:; 233 | 234 | /* Process any remaining bytes. */ 235 | if (sum > 0) 236 | sha256_process_bytes (buffer, sum, &ctx); 237 | 238 | /* Construct result in desired memory. */ 239 | sha256_finish_ctx (&ctx, resblock); 240 | free (buffer); 241 | return 0; 242 | } 243 | 244 | /* FIXME: Avoid code duplication */ 245 | int 246 | sha224_stream (FILE *stream, void *resblock) 247 | { 248 | struct sha256_ctx ctx; 249 | size_t sum; 250 | 251 | char *buffer = (char *)malloc (BLOCKSIZE + 72); 252 | if (!buffer) 253 | return 1; 254 | 255 | /* Initialize the computation context. */ 256 | sha224_init_ctx (&ctx); 257 | 258 | /* Iterate over full file contents. */ 259 | while (1) 260 | { 261 | /* We read the file in blocks of BLOCKSIZE bytes. One call of the 262 | computation function processes the whole buffer so that with the 263 | next round of the loop another block can be read. */ 264 | size_t n; 265 | sum = 0; 266 | 267 | /* Read block. Take care for partial reads. */ 268 | while (1) 269 | { 270 | n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); 271 | 272 | sum += n; 273 | 274 | if (sum == BLOCKSIZE) 275 | break; 276 | 277 | if (n == 0) 278 | { 279 | /* Check for the error flag IFF N == 0, so that we don't 280 | exit the loop after a partial read due to e.g., EAGAIN 281 | or EWOULDBLOCK. */ 282 | if (ferror (stream)) 283 | { 284 | free (buffer); 285 | return 1; 286 | } 287 | goto process_partial_block; 288 | } 289 | 290 | /* We've read at least one byte, so ignore errors. But always 291 | check for EOF, since feof may be true even though N > 0. 292 | Otherwise, we could end up calling fread after EOF. */ 293 | if (feof (stream)) 294 | goto process_partial_block; 295 | } 296 | 297 | /* Process buffer with BLOCKSIZE bytes. Note that 298 | BLOCKSIZE % 64 == 0 299 | */ 300 | sha256_process_block (buffer, BLOCKSIZE, &ctx); 301 | } 302 | 303 | process_partial_block:; 304 | 305 | /* Process any remaining bytes. */ 306 | if (sum > 0) 307 | sha256_process_bytes (buffer, sum, &ctx); 308 | 309 | /* Construct result in desired memory. */ 310 | sha224_finish_ctx (&ctx, resblock); 311 | free (buffer); 312 | return 0; 313 | } 314 | 315 | #if ! HAVE_OPENSSL_SHA256 316 | /* Compute SHA512 message digest for LEN bytes beginning at BUFFER. The 317 | result is always in little endian byte order, so that a byte-wise 318 | output yields to the wanted ASCII representation of the message 319 | digest. */ 320 | void * 321 | sha256_buffer (const char *buffer, size_t len, void *resblock) 322 | { 323 | struct sha256_ctx ctx; 324 | 325 | /* Initialize the computation context. */ 326 | sha256_init_ctx (&ctx); 327 | 328 | /* Process whole buffer but last len % 64 bytes. */ 329 | sha256_process_bytes (buffer, len, &ctx); 330 | 331 | /* Put result in desired memory area. */ 332 | return sha256_finish_ctx (&ctx, resblock); 333 | } 334 | 335 | void * 336 | sha224_buffer (const char *buffer, size_t len, void *resblock) 337 | { 338 | struct sha256_ctx ctx; 339 | 340 | /* Initialize the computation context. */ 341 | sha224_init_ctx (&ctx); 342 | 343 | /* Process whole buffer but last len % 64 bytes. */ 344 | sha256_process_bytes (buffer, len, &ctx); 345 | 346 | /* Put result in desired memory area. */ 347 | return sha224_finish_ctx (&ctx, resblock); 348 | } 349 | 350 | void 351 | sha256_process_bytes (const void *buffer, size_t len, struct sha256_ctx *ctx) 352 | { 353 | /* When we already have some bits in our internal buffer concatenate 354 | both inputs first. */ 355 | if (ctx->buflen != 0) 356 | { 357 | size_t left_over = ctx->buflen; 358 | size_t add = 128 - left_over > len ? len : 128 - left_over; 359 | 360 | memcpy (&((char *) ctx->buffer)[left_over], buffer, add); 361 | ctx->buflen += add; 362 | 363 | if (ctx->buflen > 64) 364 | { 365 | sha256_process_block (ctx->buffer, ctx->buflen & ~63, ctx); 366 | 367 | ctx->buflen &= 63; 368 | /* The regions in the following copy operation cannot overlap. */ 369 | memcpy (ctx->buffer, 370 | &((char *) ctx->buffer)[(left_over + add) & ~63], 371 | ctx->buflen); 372 | } 373 | 374 | buffer = (const char *) buffer + add; 375 | len -= add; 376 | } 377 | 378 | /* Process available complete blocks. */ 379 | if (len >= 64) 380 | { 381 | #if !_STRING_ARCH_unaligned 382 | # define UNALIGNED_P(p) ((uintptr_t) (p) % alignof (uint32_t) != 0) 383 | if (UNALIGNED_P (buffer)) 384 | while (len > 64) 385 | { 386 | sha256_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); 387 | buffer = (const char *) buffer + 64; 388 | len -= 64; 389 | } 390 | else 391 | #endif 392 | { 393 | sha256_process_block (buffer, len & ~63, ctx); 394 | buffer = (const char *) buffer + (len & ~63); 395 | len &= 63; 396 | } 397 | } 398 | 399 | /* Move remaining bytes in internal buffer. */ 400 | if (len > 0) 401 | { 402 | size_t left_over = ctx->buflen; 403 | 404 | memcpy (&((char *) ctx->buffer)[left_over], buffer, len); 405 | left_over += len; 406 | if (left_over >= 64) 407 | { 408 | sha256_process_block (ctx->buffer, 64, ctx); 409 | left_over -= 64; 410 | memcpy (ctx->buffer, &ctx->buffer[16], left_over); 411 | } 412 | ctx->buflen = left_over; 413 | } 414 | } 415 | 416 | /* --- Code below is the primary difference between sha1.c and sha256.c --- */ 417 | 418 | /* SHA256 round constants */ 419 | #define K(I) sha256_round_constants[I] 420 | static const uint32_t sha256_round_constants[64] = { 421 | 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 422 | 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 423 | 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, 424 | 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, 425 | 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, 426 | 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 427 | 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 428 | 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, 429 | 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, 430 | 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, 431 | 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 432 | 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 433 | 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, 434 | 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, 435 | 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, 436 | 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL, 437 | }; 438 | 439 | /* Round functions. */ 440 | #define F2(A,B,C) ( ( A & B ) | ( C & ( A | B ) ) ) 441 | #define F1(E,F,G) ( G ^ ( E & ( F ^ G ) ) ) 442 | 443 | /* Process LEN bytes of BUFFER, accumulating context into CTX. 444 | It is assumed that LEN % 64 == 0. 445 | Most of this code comes from GnuPG's cipher/sha1.c. */ 446 | 447 | void 448 | sha256_process_block (const void *buffer, size_t len, struct sha256_ctx *ctx) 449 | { 450 | const uint32_t *words = (uint32_t *)buffer; 451 | size_t nwords = len / sizeof (uint32_t); 452 | const uint32_t *endp = words + nwords; 453 | uint32_t x[16]; 454 | uint32_t a = ctx->state[0]; 455 | uint32_t b = ctx->state[1]; 456 | uint32_t c = ctx->state[2]; 457 | uint32_t d = ctx->state[3]; 458 | uint32_t e = ctx->state[4]; 459 | uint32_t f = ctx->state[5]; 460 | uint32_t g = ctx->state[6]; 461 | uint32_t h = ctx->state[7]; 462 | uint32_t lolen = len; 463 | 464 | /* First increment the byte count. FIPS PUB 180-2 specifies the possible 465 | length of the file up to 2^64 bits. Here we only compute the 466 | number of bytes. Do a double word increment. */ 467 | ctx->total[0] += lolen; 468 | ctx->total[1] += (len >> 31 >> 1) + (ctx->total[0] < lolen); 469 | 470 | #define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) 471 | #define S0(x) (rol(x,25)^rol(x,14)^(x>>3)) 472 | #define S1(x) (rol(x,15)^rol(x,13)^(x>>10)) 473 | #define SS0(x) (rol(x,30)^rol(x,19)^rol(x,10)) 474 | #define SS1(x) (rol(x,26)^rol(x,21)^rol(x,7)) 475 | 476 | #define M(I) ( tm = S1(x[(I-2)&0x0f]) + x[(I-7)&0x0f] \ 477 | + S0(x[(I-15)&0x0f]) + x[I&0x0f] \ 478 | , x[I&0x0f] = tm ) 479 | 480 | #define R(A,B,C,D,E,F,G,H,K,M) do { t0 = SS0(A) + F2(A,B,C); \ 481 | t1 = H + SS1(E) \ 482 | + F1(E,F,G) \ 483 | + K \ 484 | + M; \ 485 | D += t1; H = t0 + t1; \ 486 | } while(0) 487 | 488 | while (words < endp) 489 | { 490 | uint32_t tm; 491 | uint32_t t0, t1; 492 | int t; 493 | /* FIXME: see sha1.c for a better implementation. */ 494 | for (t = 0; t < 16; t++) 495 | { 496 | x[t] = SWAP (*words); 497 | words++; 498 | } 499 | 500 | R( a, b, c, d, e, f, g, h, K( 0), x[ 0] ); 501 | R( h, a, b, c, d, e, f, g, K( 1), x[ 1] ); 502 | R( g, h, a, b, c, d, e, f, K( 2), x[ 2] ); 503 | R( f, g, h, a, b, c, d, e, K( 3), x[ 3] ); 504 | R( e, f, g, h, a, b, c, d, K( 4), x[ 4] ); 505 | R( d, e, f, g, h, a, b, c, K( 5), x[ 5] ); 506 | R( c, d, e, f, g, h, a, b, K( 6), x[ 6] ); 507 | R( b, c, d, e, f, g, h, a, K( 7), x[ 7] ); 508 | R( a, b, c, d, e, f, g, h, K( 8), x[ 8] ); 509 | R( h, a, b, c, d, e, f, g, K( 9), x[ 9] ); 510 | R( g, h, a, b, c, d, e, f, K(10), x[10] ); 511 | R( f, g, h, a, b, c, d, e, K(11), x[11] ); 512 | R( e, f, g, h, a, b, c, d, K(12), x[12] ); 513 | R( d, e, f, g, h, a, b, c, K(13), x[13] ); 514 | R( c, d, e, f, g, h, a, b, K(14), x[14] ); 515 | R( b, c, d, e, f, g, h, a, K(15), x[15] ); 516 | R( a, b, c, d, e, f, g, h, K(16), M(16) ); 517 | R( h, a, b, c, d, e, f, g, K(17), M(17) ); 518 | R( g, h, a, b, c, d, e, f, K(18), M(18) ); 519 | R( f, g, h, a, b, c, d, e, K(19), M(19) ); 520 | R( e, f, g, h, a, b, c, d, K(20), M(20) ); 521 | R( d, e, f, g, h, a, b, c, K(21), M(21) ); 522 | R( c, d, e, f, g, h, a, b, K(22), M(22) ); 523 | R( b, c, d, e, f, g, h, a, K(23), M(23) ); 524 | R( a, b, c, d, e, f, g, h, K(24), M(24) ); 525 | R( h, a, b, c, d, e, f, g, K(25), M(25) ); 526 | R( g, h, a, b, c, d, e, f, K(26), M(26) ); 527 | R( f, g, h, a, b, c, d, e, K(27), M(27) ); 528 | R( e, f, g, h, a, b, c, d, K(28), M(28) ); 529 | R( d, e, f, g, h, a, b, c, K(29), M(29) ); 530 | R( c, d, e, f, g, h, a, b, K(30), M(30) ); 531 | R( b, c, d, e, f, g, h, a, K(31), M(31) ); 532 | R( a, b, c, d, e, f, g, h, K(32), M(32) ); 533 | R( h, a, b, c, d, e, f, g, K(33), M(33) ); 534 | R( g, h, a, b, c, d, e, f, K(34), M(34) ); 535 | R( f, g, h, a, b, c, d, e, K(35), M(35) ); 536 | R( e, f, g, h, a, b, c, d, K(36), M(36) ); 537 | R( d, e, f, g, h, a, b, c, K(37), M(37) ); 538 | R( c, d, e, f, g, h, a, b, K(38), M(38) ); 539 | R( b, c, d, e, f, g, h, a, K(39), M(39) ); 540 | R( a, b, c, d, e, f, g, h, K(40), M(40) ); 541 | R( h, a, b, c, d, e, f, g, K(41), M(41) ); 542 | R( g, h, a, b, c, d, e, f, K(42), M(42) ); 543 | R( f, g, h, a, b, c, d, e, K(43), M(43) ); 544 | R( e, f, g, h, a, b, c, d, K(44), M(44) ); 545 | R( d, e, f, g, h, a, b, c, K(45), M(45) ); 546 | R( c, d, e, f, g, h, a, b, K(46), M(46) ); 547 | R( b, c, d, e, f, g, h, a, K(47), M(47) ); 548 | R( a, b, c, d, e, f, g, h, K(48), M(48) ); 549 | R( h, a, b, c, d, e, f, g, K(49), M(49) ); 550 | R( g, h, a, b, c, d, e, f, K(50), M(50) ); 551 | R( f, g, h, a, b, c, d, e, K(51), M(51) ); 552 | R( e, f, g, h, a, b, c, d, K(52), M(52) ); 553 | R( d, e, f, g, h, a, b, c, K(53), M(53) ); 554 | R( c, d, e, f, g, h, a, b, K(54), M(54) ); 555 | R( b, c, d, e, f, g, h, a, K(55), M(55) ); 556 | R( a, b, c, d, e, f, g, h, K(56), M(56) ); 557 | R( h, a, b, c, d, e, f, g, K(57), M(57) ); 558 | R( g, h, a, b, c, d, e, f, K(58), M(58) ); 559 | R( f, g, h, a, b, c, d, e, K(59), M(59) ); 560 | R( e, f, g, h, a, b, c, d, K(60), M(60) ); 561 | R( d, e, f, g, h, a, b, c, K(61), M(61) ); 562 | R( c, d, e, f, g, h, a, b, K(62), M(62) ); 563 | R( b, c, d, e, f, g, h, a, K(63), M(63) ); 564 | 565 | a = ctx->state[0] += a; 566 | b = ctx->state[1] += b; 567 | c = ctx->state[2] += c; 568 | d = ctx->state[3] += d; 569 | e = ctx->state[4] += e; 570 | f = ctx->state[5] += f; 571 | g = ctx->state[6] += g; 572 | h = ctx->state[7] += h; 573 | } 574 | } 575 | #endif 576 | -------------------------------------------------------------------------------- /sha256/sha256.h: -------------------------------------------------------------------------------- 1 | /* Declarations of functions and data types used for SHA256 and SHA224 sum 2 | library functions. 3 | Copyright (C) 2005-2006, 2008-2013 Free Software Foundation, Inc. 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . */ 17 | 18 | #ifndef SHA256_H 19 | # define SHA256_H 1 20 | 21 | # include 22 | # include 23 | 24 | # if HAVE_OPENSSL_SHA256 25 | # include 26 | # endif 27 | 28 | # ifdef __cplusplus 29 | extern "C" { 30 | # endif 31 | 32 | enum { SHA224_DIGEST_SIZE = 224 / 8 }; 33 | enum { SHA256_DIGEST_SIZE = 256 / 8 }; 34 | 35 | # if HAVE_OPENSSL_SHA256 36 | # define GL_OPENSSL_NAME 224 37 | # include "gl_openssl.h" 38 | # define GL_OPENSSL_NAME 256 39 | # include "gl_openssl.h" 40 | # else 41 | /* Structure to save state of computation between the single steps. */ 42 | struct sha256_ctx 43 | { 44 | uint32_t state[8]; 45 | 46 | uint32_t total[2]; 47 | size_t buflen; 48 | uint32_t buffer[32]; 49 | }; 50 | 51 | /* Initialize structure containing state of computation. */ 52 | extern void sha256_init_ctx (struct sha256_ctx *ctx); 53 | extern void sha224_init_ctx (struct sha256_ctx *ctx); 54 | 55 | /* Starting with the result of former calls of this function (or the 56 | initialization function update the context for the next LEN bytes 57 | starting at BUFFER. 58 | It is necessary that LEN is a multiple of 64!!! */ 59 | extern void sha256_process_block (const void *buffer, size_t len, 60 | struct sha256_ctx *ctx); 61 | 62 | /* Starting with the result of former calls of this function (or the 63 | initialization function update the context for the next LEN bytes 64 | starting at BUFFER. 65 | It is NOT required that LEN is a multiple of 64. */ 66 | extern void sha256_process_bytes (const void *buffer, size_t len, 67 | struct sha256_ctx *ctx); 68 | 69 | /* Process the remaining bytes in the buffer and put result from CTX 70 | in first 32 (28) bytes following RESBUF. The result is always in little 71 | endian byte order, so that a byte-wise output yields to the wanted 72 | ASCII representation of the message digest. */ 73 | extern void *sha256_finish_ctx (struct sha256_ctx *ctx, void *resbuf); 74 | extern void *sha224_finish_ctx (struct sha256_ctx *ctx, void *resbuf); 75 | 76 | 77 | /* Put result from CTX in first 32 (28) bytes following RESBUF. The result is 78 | always in little endian byte order, so that a byte-wise output yields 79 | to the wanted ASCII representation of the message digest. */ 80 | extern void *sha256_read_ctx (const struct sha256_ctx *ctx, void *resbuf); 81 | extern void *sha224_read_ctx (const struct sha256_ctx *ctx, void *resbuf); 82 | 83 | 84 | /* Compute SHA256 (SHA224) message digest for LEN bytes beginning at BUFFER. The 85 | result is always in little endian byte order, so that a byte-wise 86 | output yields to the wanted ASCII representation of the message 87 | digest. */ 88 | extern void *sha256_buffer (const char *buffer, size_t len, void *resblock); 89 | extern void *sha224_buffer (const char *buffer, size_t len, void *resblock); 90 | 91 | # endif 92 | /* Compute SHA256 (SHA224) message digest for bytes read from STREAM. The 93 | resulting message digest number will be written into the 32 (28) bytes 94 | beginning at RESBLOCK. */ 95 | extern int sha256_stream (FILE *stream, void *resblock); 96 | extern int sha224_stream (FILE *stream, void *resblock); 97 | 98 | 99 | # ifdef __cplusplus 100 | } 101 | # endif 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /sha256/u64.h: -------------------------------------------------------------------------------- 1 | /* uint64_t-like operations that work even on hosts lacking uint64_t 2 | 3 | Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc. 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . */ 17 | 18 | /* Written by Paul Eggert. */ 19 | 20 | #include 21 | 22 | #ifndef _GL_INLINE_HEADER_BEGIN 23 | #error "Please include config.h first." 24 | #endif 25 | _GL_INLINE_HEADER_BEGIN 26 | #ifndef _GL_U64_INLINE 27 | # define _GL_U64_INLINE _GL_INLINE 28 | #endif 29 | 30 | /* Return X rotated left by N bits, where 0 < N < 64. */ 31 | #define u64rol(x, n) u64or (u64shl (x, n), u64shr (x, 64 - n)) 32 | 33 | #ifdef UINT64_MAX 34 | 35 | /* Native implementations are trivial. See below for comments on what 36 | these operations do. */ 37 | typedef uint64_t u64; 38 | # define u64hilo(hi, lo) ((u64) (((u64) (hi) << 32) + (lo))) 39 | # define u64init(hi, lo) u64hilo (hi, lo) 40 | # define u64lo(x) ((u64) (x)) 41 | # define u64size(x) u64lo (x) 42 | # define u64lt(x, y) ((x) < (y)) 43 | # define u64and(x, y) ((x) & (y)) 44 | # define u64or(x, y) ((x) | (y)) 45 | # define u64xor(x, y) ((x) ^ (y)) 46 | # define u64plus(x, y) ((x) + (y)) 47 | # define u64shl(x, n) ((x) << (n)) 48 | # define u64shr(x, n) ((x) >> (n)) 49 | 50 | #else 51 | 52 | /* u64 is a 64-bit unsigned integer value. 53 | u64init (HI, LO), is like u64hilo (HI, LO), but for use in 54 | initializer contexts. */ 55 | # ifdef WORDS_BIGENDIAN 56 | typedef struct { uint32_t hi, lo; } u64; 57 | # define u64init(hi, lo) { hi, lo } 58 | # else 59 | typedef struct { uint32_t lo, hi; } u64; 60 | # define u64init(hi, lo) { lo, hi } 61 | # endif 62 | 63 | /* Given the high and low-order 32-bit quantities HI and LO, return a u64 64 | value representing (HI << 32) + LO. */ 65 | _GL_U64_INLINE u64 66 | u64hilo (uint32_t hi, uint32_t lo) 67 | { 68 | u64 r; 69 | r.hi = hi; 70 | r.lo = lo; 71 | return r; 72 | } 73 | 74 | /* Return a u64 value representing LO. */ 75 | _GL_U64_INLINE u64 76 | u64lo (uint32_t lo) 77 | { 78 | u64 r; 79 | r.hi = 0; 80 | r.lo = lo; 81 | return r; 82 | } 83 | 84 | /* Return a u64 value representing SIZE. */ 85 | _GL_U64_INLINE u64 86 | u64size (size_t size) 87 | { 88 | u64 r; 89 | r.hi = size >> 31 >> 1; 90 | r.lo = size; 91 | return r; 92 | } 93 | 94 | /* Return X < Y. */ 95 | _GL_U64_INLINE int 96 | u64lt (u64 x, u64 y) 97 | { 98 | return x.hi < y.hi || (x.hi == y.hi && x.lo < y.lo); 99 | } 100 | 101 | /* Return X & Y. */ 102 | _GL_U64_INLINE u64 103 | u64and (u64 x, u64 y) 104 | { 105 | u64 r; 106 | r.hi = x.hi & y.hi; 107 | r.lo = x.lo & y.lo; 108 | return r; 109 | } 110 | 111 | /* Return X | Y. */ 112 | _GL_U64_INLINE u64 113 | u64or (u64 x, u64 y) 114 | { 115 | u64 r; 116 | r.hi = x.hi | y.hi; 117 | r.lo = x.lo | y.lo; 118 | return r; 119 | } 120 | 121 | /* Return X ^ Y. */ 122 | _GL_U64_INLINE u64 123 | u64xor (u64 x, u64 y) 124 | { 125 | u64 r; 126 | r.hi = x.hi ^ y.hi; 127 | r.lo = x.lo ^ y.lo; 128 | return r; 129 | } 130 | 131 | /* Return X + Y. */ 132 | _GL_U64_INLINE u64 133 | u64plus (u64 x, u64 y) 134 | { 135 | u64 r; 136 | r.lo = x.lo + y.lo; 137 | r.hi = x.hi + y.hi + (r.lo < x.lo); 138 | return r; 139 | } 140 | 141 | /* Return X << N. */ 142 | _GL_U64_INLINE u64 143 | u64shl (u64 x, int n) 144 | { 145 | u64 r; 146 | if (n < 32) 147 | { 148 | r.hi = (x.hi << n) | (x.lo >> (32 - n)); 149 | r.lo = x.lo << n; 150 | } 151 | else 152 | { 153 | r.hi = x.lo << (n - 32); 154 | r.lo = 0; 155 | } 156 | return r; 157 | } 158 | 159 | /* Return X >> N. */ 160 | _GL_U64_INLINE u64 161 | u64shr (u64 x, int n) 162 | { 163 | u64 r; 164 | if (n < 32) 165 | { 166 | r.hi = x.hi >> n; 167 | r.lo = (x.hi << (32 - n)) | (x.lo >> n); 168 | } 169 | else 170 | { 171 | r.hi = 0; 172 | r.lo = x.hi >> (n - 32); 173 | } 174 | return r; 175 | } 176 | 177 | #endif 178 | 179 | _GL_INLINE_HEADER_END 180 | -------------------------------------------------------------------------------- /testprocess.cpp: -------------------------------------------------------------------------------- 1 | #include //for ntohl() 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include //for strerror() 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "common/syscall_wrappers.h" 27 | using namespace std; 28 | 29 | 30 | string random_str = "empty"; 31 | int tcp_server_port = 0; 32 | int udp_server_port = 0; 33 | bool bLocalTCPServerStarted = false; 34 | bool bLocalUDPServerStarted = false; 35 | string iface_str; //contains my external interface IP 36 | //forward declaration 37 | int bind_tcp_client(bool bWrite); 38 | void fork_setup(); 39 | 40 | bool fileExists(const string filename){ 41 | struct stat buf; 42 | if (stat(filename.c_str(), &buf) != -1){return true;} 43 | return false; 44 | } 45 | 46 | 47 | void die2(string message){ 48 | cout << message << "\n"; 49 | cout << "in testprocess \n"; 50 | exit(1); 51 | } 52 | 53 | 54 | //split on a delimiter and return chunks 55 | vector split_string(string arg, string delimiter = " "){ 56 | vector output; 57 | int pos = 0; 58 | string token; 59 | while (true){ 60 | pos = arg.find(delimiter); 61 | if (pos == string::npos){ //last element 62 | token = arg.substr(0, arg.length()); 63 | output.push_back(token); 64 | break; 65 | } 66 | token = arg.substr(0, pos); 67 | output.push_back(token); 68 | arg.erase(0, pos + 1); 69 | } 70 | return output; 71 | } 72 | 73 | 74 | //Create/(check existence of) lpfwtest group and add ourselves to it 75 | //After that set this process's gid to lpfwtest group 76 | void setgid_lpfwtest() { 77 | errno = 0; 78 | struct group *m_group; 79 | m_group = getgrnam("lpfwtest"); 80 | if (errno != 0) { die2("getgrnam error"); } 81 | if (!m_group) { //group doesnt yet exist 82 | cout << "lpfwtest group does not exist, creating...\n"; 83 | if (system("groupadd lpfwtest") == -1) { die2("error in system(groupadd)\n"); } 84 | //else call getgrnam again after group creation 85 | errno = 0; 86 | m_group = getgrnam("lpfwtest"); 87 | if(!m_group) { die2("failed to create lpfwtest group"); } 88 | } 89 | if (setgid(m_group->gr_gid) == -1) { die2(strerror(errno)); } 90 | } 91 | 92 | 93 | //let testdriver know my socket's local port number 94 | int write_port_file(int sockfd, string protocol){ 95 | struct sockaddr_in sin; 96 | socklen_t addrlen = sizeof(sin); 97 | if(getsockname(sockfd, (struct sockaddr *)&sin, &addrlen) == 0 && 98 | sin.sin_family == AF_INET && addrlen == sizeof(sin)) { 99 | int local_port = ntohs(sin.sin_port); 100 | string suffix = ".none"; 101 | if (protocol == "TCP"){suffix = ".tcp";} 102 | else if (protocol == "UDP"){suffix = ".udp";} 103 | ofstream socket_file("/tmp/lpfwtest/" + random_str + suffix); 104 | socket_file << to_string(local_port); 105 | socket_file.close(); 106 | ofstream portready("/tmp/lpfwtest/"+random_str+".port-file-is-ready"); 107 | portready.close(); 108 | return local_port; 109 | } 110 | else{ die2("testprocess: could not get my local port number"); } 111 | } 112 | 113 | 114 | //Try to connect to a server and optionally wait for the server 115 | //to respond before returning. Not waiting for the server to respond 116 | //can be usefull when testing offline 117 | void connect_tcp(int sockfd, string host, string port_str, bool bWait = true, string perms = "NONE") { 118 | if (bWait){ //doing full tcp conn, at this point the conn from quicktcp might have been 119 | //shutdown. We must shutdown socket and get a new one. TODO this is a code mess for now. 120 | shutdown(sockfd, SHUT_RDWR); 121 | sockfd = bind_tcp_client(false); 122 | } 123 | 124 | struct sockaddr_in serv_addr; 125 | int port = stoi(port_str); 126 | 127 | memset(&serv_addr, '0', sizeof(serv_addr)); 128 | serv_addr.sin_family = AF_INET; 129 | serv_addr.sin_port = htons(port); 130 | if(inet_pton(AF_INET, host.c_str(), &serv_addr.sin_addr)<=0) { 131 | die2("testprocess: inet_pton() failed"); } 132 | 133 | fd_set myset; 134 | struct timeval tv; 135 | int timeout = 0; 136 | int valopt; 137 | socklen_t lon; 138 | bool connected = false; 139 | 140 | if (bWait) timeout = 5; 141 | tv.tv_sec = timeout; 142 | tv.tv_usec = 0; 143 | 144 | cout << "testprocess: connecting to server \n"; 145 | int res = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 146 | if ( (res != -1 && (errno != EINPROGRESS || errno != EALREADY)) && (res != 0) ) { 147 | //EINPROGRESS happens when *this* connect is now in progress 148 | //EALREADY can happen when a previous (NOT *this*) connect is still in progress 149 | //res == 0 on non-blocking socket happens if the socket was already connected 150 | //to the remote host 151 | cout << "Unexpected return value: " << res << " sockfd was: " << sockfd << "\n"; 152 | fprintf(stderr, "The errno is %d - %s\n", errno, strerror(errno)); 153 | exit(1); 154 | } 155 | write_port_file(sockfd, "TCP"); 156 | do { 157 | FD_ZERO(&myset); 158 | FD_SET(sockfd, &myset); 159 | res = 0; 160 | res = select(sockfd+1, NULL, &myset, NULL, &tv); 161 | if (res < 0 && errno != EINTR) { 162 | fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno)); 163 | exit(1); 164 | } 165 | else if (res > 0) { 166 | // Socket selected for write 167 | lon = sizeof(int); 168 | if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0) { 169 | fprintf(stderr, "Error in getsockopt() %d - %s\n", errno, strerror(errno)); 170 | exit(1); 171 | } 172 | // Check the value returned... 173 | if (valopt) { 174 | if (valopt == 110){ //TODO what's the name for this error code? 175 | //Connection timed out 176 | fprintf(stderr, "Timeout in select() via valopt - Cancelling!\n"); 177 | break; 178 | } 179 | fprintf(stderr, "Error in delayed connection() %d - %s\n", valopt, strerror(valopt)); 180 | exit(1); 181 | } 182 | //else successfully connected 183 | cout << "TCP connection established...\n"; 184 | connected = true; 185 | ofstream f("/tmp/lpfwtest/"+random_str+".connected"); 186 | f.close(); 187 | break; 188 | } 189 | else { 190 | cout << "timeout for host " << host << "\n"; 191 | fprintf(stderr, "Timeout in select() - Cancelling!\n"); 192 | break; 193 | } 194 | } while (1); 195 | if (connected && (perms == "DENY_ALWAYS" || perms == "DENY_ONCE")){ 196 | cout << "ERROR******************* DENY* rule was able to connect \n"; 197 | abort(); 198 | } 199 | else if (!connected && (perms == "ALLOW_ALWAYS" || perms == "ALLOW_ONCE")){ 200 | cout << "ERROR******************* ALLOW* rule was unable to connect \n"; 201 | abort(); 202 | } 203 | } 204 | 205 | 206 | void connect_udp(int sockfd, string host, string port_str, bool bWait = true, string perms = "NONE"){ 207 | struct sockaddr_in serv_addr; 208 | int port = stoi(port_str); 209 | int timeout = 0; 210 | if (bWait) timeout = 5; 211 | 212 | memset(&serv_addr, '0', sizeof(serv_addr)); 213 | serv_addr.sin_family = AF_INET; 214 | serv_addr.sin_port = htons(port); 215 | if(inet_pton(AF_INET, host.c_str(), &serv_addr.sin_addr)<=0) { 216 | die2("testprocess: inet_pton() failed"); } 217 | 218 | int res = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 219 | if (res != 0){ 220 | cout << "Unknown return value \n"; 221 | abort(); 222 | } 223 | //No traffic has been sent at this point. We need to send a dummy packet to trigger netfilter. 224 | unsigned char msg[48]={010,0,0,0,0,0,0,0,0}; // NTP packet 225 | int rv = send(sockfd, msg, sizeof(msg), 0); 226 | if (rv == -1){ 227 | fprintf(stderr, "Error in send() %d - %s\n", errno, strerror(errno)); 228 | exit(1); 229 | } 230 | if (!bWait){ return;} //we dont care about the response 231 | //else //we need to get some data from the server to know that we indeed connected 232 | char buf[1024]; 233 | bool bResponded = false; 234 | for (int i=0; i < timeout; ++i){ 235 | rv = recv(sockfd, buf, sizeof(buf), 0); 236 | if (rv > 0){ 237 | bResponded = true; 238 | break; 239 | } 240 | else if (rv == -1 && errno != EAGAIN){ 241 | fprintf(stderr, "Error in recv() %d - %s\n", errno, strerror(errno)); 242 | } 243 | sleep(1); 244 | } 245 | if (bResponded && (perms == "DENY_ALWAYS" || perms == "DENY_ONCE")){ 246 | cout << "ERROR******************* UDP DENY* rule was able to connect \n"; 247 | abort(); 248 | } 249 | else if (!bResponded && (perms == "ALLOW_ALWAYS" || perms == "ALLOW_ONCE")){ 250 | cout << "ERROR******************* UDP ALLOW* rule was unable to connect \n"; 251 | cout << "while connecting to: " << host << "\n"; 252 | abort(); 253 | } 254 | else if (bResponded){ 255 | cout << "UDP got data from server...\n"; 256 | ofstream signal_file("/tmp/lpfwtest/"+random_str+".connected"); 257 | signal_file.close(); 258 | } 259 | //shutdown(sockfd, SHUT_RDWR); 260 | } 261 | 262 | 263 | void *thread_tcp_server (void *ptr){ 264 | int list_s; /* listening socket */ 265 | struct sockaddr_in servaddr; /* socket address structure */ 266 | 267 | if ( (list_s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) { 268 | fprintf(stderr, "ECHOSERV: Error creating listening socket.\n"); 269 | exit(EXIT_FAILURE); 270 | } 271 | 272 | memset(&servaddr, 0, sizeof(servaddr)); 273 | servaddr.sin_family = AF_INET; 274 | string host = iface_str; 275 | if(inet_pton(AF_INET, host.c_str(), &servaddr.sin_addr)<=0) { 276 | die2("testprocess: error inet_pton() failed"); } 277 | 278 | servaddr.sin_port = htons(0); 279 | if ( bind(list_s, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) { 280 | fprintf(stderr, "bind error is %d - %s\n", errno, strerror(errno)); 281 | } 282 | tcp_server_port = write_port_file(list_s, "TCP"); 283 | //we need to add an iptables rule for this specific INPUT port 284 | _system (string("iptables -A INPUT -p tcp --dport " + to_string(tcp_server_port) + 285 | " -m state --state NEW -j NFQUEUE --queue-num 11221").c_str()); 286 | 287 | if ( listen(list_s, 500) < 0 ) { 288 | fprintf(stderr, "ECHOSERV: Error calling listen()\n"); 289 | exit(EXIT_FAILURE); 290 | } 291 | cout << "TCPserver listening on port " << tcp_server_port << "\n"; 292 | bLocalTCPServerStarted = true; 293 | 294 | if ( (accept(list_s, NULL, NULL) ) < 0 ) { 295 | fprintf(stderr, "ECHOSERV: Error calling accept()\n"); 296 | exit(EXIT_FAILURE); 297 | } 298 | //we get here when server accept()ed the connection 299 | cout << "TCP server accepted connection...\n"; 300 | //just create the file to let the testdriver know that we connected OK 301 | ofstream signal_file("/tmp/lpfwtest/" + random_str + ".connected"); 302 | signal_file.close(); 303 | } 304 | 305 | 306 | void *thread_udp_server (void *ptr){ 307 | int list_s; /* listening socket */ 308 | struct sockaddr_in servaddr; /* socket address structure */ 309 | 310 | if ( (list_s = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) { 311 | fprintf(stderr, "ECHOSERV: Error creating listening socket.\n"); 312 | abort(); 313 | } 314 | memset(&servaddr, 0, sizeof(servaddr)); 315 | servaddr.sin_family = AF_INET; 316 | string host = iface_str; 317 | if(inet_pton(AF_INET, host.c_str(), &servaddr.sin_addr)<=0) { 318 | die2("testprocess: inet_pton() failed"); } 319 | 320 | servaddr.sin_port = htons(0); 321 | if ( bind(list_s, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) { 322 | fprintf(stderr, "ECHOSERV: Error calling bind()\n"); 323 | abort(); 324 | } 325 | udp_server_port = write_port_file(list_s, "UDP"); 326 | _system (string("iptables -A INPUT -p udp --dport " + to_string(udp_server_port) + 327 | " -m state --state NEW -j NFQUEUE --queue-num 11221").c_str()); 328 | 329 | cout << "UDPserver listening on port " << udp_server_port << "\n"; 330 | bLocalUDPServerStarted = true; 331 | char msg[1000]; 332 | int n = recv(list_s, msg, 1000, 0); //block until data 333 | 334 | cout << "UDP server received data on port " << udp_server_port << "\n"; 335 | //let the testdriver know that we were connected to 336 | ofstream signal_file("/tmp/lpfwtest/" + random_str + ".connected"); 337 | signal_file.close(); 338 | } 339 | 340 | 341 | 342 | 343 | void localtcpserver(){ 344 | pthread_t thr_tcpserver; 345 | pthread_create(&thr_tcpserver ,(pthread_attr_t*)NULL, thread_tcp_server, (void *)NULL); 346 | } 347 | 348 | void localudpserver(){ 349 | pthread_t thr_udpserver; 350 | pthread_create(&thr_udpserver ,(pthread_attr_t*)NULL, thread_udp_server, (void *)NULL); 351 | } 352 | 353 | 354 | //send a UDP packet to a local UDP server. This function doesn't wait for the response 355 | //because we are testing the UDP server. The UDP server will register whether the conn 356 | //to it succeeded 357 | void localudpsend(){ 358 | //send a packet (i.e. connect to the server) 359 | struct sockaddr_in serv_addr; 360 | int sockfd = 0; 361 | int timeout = 0; 362 | string host = iface_str; 363 | 364 | if ((sockfd = socket(AF_INET, SOCK_DGRAM , 0)) < 0) { 365 | die2("testprocess: socket() error"); } 366 | long arg; 367 | if ((arg = fcntl(sockfd, F_GETFL, NULL)) == -1){ 368 | die2("testprocess: fcntl() error"); 369 | } 370 | arg |= O_NONBLOCK; 371 | if (fcntl(sockfd, F_SETFL, arg) == -1){ 372 | die2("testprocess: fcntl() error"); 373 | } 374 | memset(&serv_addr, '0', sizeof(serv_addr)); 375 | serv_addr.sin_family = AF_INET; 376 | 377 | while (!bLocalUDPServerStarted){} //spin. hopefully < 1 second 378 | serv_addr.sin_port = htons(udp_server_port); 379 | if(inet_pton(AF_INET, host.c_str(), &serv_addr.sin_addr)<=0) { 380 | die2("testprocess: inet_pton() error"); } 381 | 382 | int res = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 383 | if (res != 0){ 384 | cout << "Unknown return value:" << res << "\n"; 385 | fprintf(stderr, "Error in connect() %d - %s\n", errno, strerror(errno)); 386 | abort(); 387 | } 388 | //No traffic has been sent at this point. We need to send a dummy packet to trigger netfilter. 389 | unsigned char msg[48]={010,0,0,0,0,0,0,0,0}; // NTP packet 390 | int rv = send(sockfd, msg, sizeof(msg), 0); 391 | if (rv == -1){ 392 | fprintf(stderr, "Error in send() %d - %s\n", errno, strerror(errno)); 393 | exit(1); 394 | } 395 | //Can shutdown the socket because the packet is on the way to the server. 396 | //Unline TCP, we dont have to wait for the handshake to complete. 397 | //shutdown(sockfd, SHUT_RDWR); 398 | } 399 | 400 | 401 | void localtcpsend(){ 402 | //send a packet (i.e. connect to the server) 403 | struct sockaddr_in serv_addr; 404 | int sockfd = 0; 405 | string host = iface_str; 406 | 407 | if ((sockfd = socket(AF_INET, SOCK_STREAM , 0)) < 0) { 408 | die2("testprocess: socket() failed"); } 409 | long arg = fcntl(sockfd, F_GETFL, NULL); 410 | arg |= O_NONBLOCK; 411 | fcntl(sockfd, F_SETFL, arg); 412 | memset(&serv_addr, '0', sizeof(serv_addr)); 413 | serv_addr.sin_family = AF_INET; 414 | 415 | while (!bLocalTCPServerStarted){} //spin. hopefully < 1 second 416 | serv_addr.sin_port = htons(tcp_server_port); 417 | if(inet_pton(AF_INET, host.c_str(), &serv_addr.sin_addr)<=0) { 418 | die2("testprocess: inet_pton() failed"); } 419 | 420 | fd_set myset; 421 | struct timeval tv; 422 | int timeout = 0; 423 | int valopt; 424 | socklen_t lon; 425 | 426 | tv.tv_sec = timeout; 427 | tv.tv_usec = 0; 428 | 429 | cout << "connecting to local server on port " << tcp_server_port << "\n"; 430 | int res = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 431 | if ( !(res == -1 && errno == EINPROGRESS) ) { 432 | //res==0 is an error b/c it cant happen on connect() with non-blocking socket 433 | cout << "Unexpected return value: " << res << "\n"; 434 | fprintf(stderr, "The errno is %d - %s\n", errno, strerror(errno)); 435 | exit(1); 436 | } 437 | cout << "selecting \n"; 438 | do { 439 | FD_ZERO(&myset); 440 | FD_SET(sockfd, &myset); 441 | res = select(sockfd+1, NULL, &myset, NULL, &tv); 442 | if (res < 0 && errno != EINTR) { 443 | fprintf(stderr, "Error connecting %d - %s\n", errno, strerror(errno)); 444 | exit(1); 445 | } 446 | else if (res > 0) { 447 | // Socket selected for write 448 | lon = sizeof(int); 449 | if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0) { 450 | fprintf(stderr, "Error in getsockopt() %d - %s\n", errno, strerror(errno)); 451 | exit(1); 452 | } 453 | // Check the value returned... 454 | if (valopt) { 455 | fprintf(stderr, "Error in delayed connection() %d - %s\n", valopt, strerror(valopt)); 456 | exit(1); 457 | } 458 | //else successfully connected 459 | cout << "local TCP connection established...\n"; 460 | //just create the file to let the testdriver know that we connected OK 461 | ofstream f("/tmp/lpfwtest/"+random_str+".connected"); 462 | f.close(); 463 | break; 464 | } 465 | else { 466 | fprintf(stderr, "Timeout in select() - Cancelling!\n"); 467 | break; 468 | } 469 | } while (1); 470 | 471 | //whether we connected or not will be signalled by the server itself 472 | //we need to immediately close the socket, otherwise it may retry connection 473 | //and mess up the testing logic 474 | 475 | //TODO if we shutdown the socket immediately then the TCP handshake will be interrupted 476 | //if we do not shutdown it, we gonna leak it. Find a workaround. 477 | //shutdown(sockfd, SHUT_RDWR); 478 | } 479 | 480 | //wait for timeout seconds for the file to appear on filesystem 481 | void wait_for_semaphore_file(string path, int timeout=7){ 482 | struct timespec refresh_timer; 483 | refresh_timer.tv_sec=0; 484 | refresh_timer.tv_nsec=1000000000/100; 485 | int loops = 0; 486 | while (true) { 487 | fstream f(path); 488 | if (f.good()){ 489 | f.close(); 490 | return; 491 | } 492 | f.close(); 493 | //else file does not yet exist 494 | ++loops; 495 | if (loops > timeout*100){ //timeout after 7 seconds 496 | cout << "Timeout waiting on the semaphore file " << path << "\n"; 497 | exit(1); 498 | } 499 | while(nanosleep(&refresh_timer, &refresh_timer)); 500 | } 501 | } 502 | 503 | 504 | //Only bind to a local port and dont send anything yet 505 | //Because we have to report the local port to frontend, so it knows 506 | //from which port to expect a new request 507 | int bind_tcp_client(bool bWrite = true){ 508 | struct sockaddr_in sa_loc; 509 | int fd = socket(AF_INET, SOCK_STREAM, 0); 510 | if (fd == -1) { die2("testprocess: socket() failed"); } 511 | long arg = fcntl(fd, F_GETFL, NULL); 512 | arg |= O_NONBLOCK; 513 | fcntl(fd, F_SETFL, arg); 514 | memset(&sa_loc, 0, sizeof(struct sockaddr_in)); 515 | sa_loc.sin_family = AF_INET; 516 | //no need to expilictely set local address, otherwise I got errors on connect() 517 | sa_loc.sin_port = htons(0); //kernel chooses a free port 518 | if ( bind(fd, (struct sockaddr *)&sa_loc, sizeof(struct sockaddr_in)) < 0){ 519 | fprintf(stderr, "Error calling bind()\n"); 520 | fprintf(stderr, "The errno is %d - %s\n", errno, strerror(errno)); 521 | exit(1); 522 | } 523 | if (bWrite){ 524 | write_port_file(fd, "TCP"); 525 | } 526 | return fd; 527 | } 528 | 529 | 530 | //Only bind to a local port and dont send anything yet 531 | //Because we have to report the local port to frontend, so it knows 532 | //from which port to expect a new request 533 | int bind_udp_client(bool bWrite = true){ 534 | struct sockaddr_in sa_loc; 535 | int fd = socket(AF_INET, SOCK_DGRAM, 0); 536 | if (fd == -1) { die2("testprocess: udp socket() failed"); } 537 | long arg = fcntl(fd, F_GETFL, NULL); 538 | arg |= O_NONBLOCK; 539 | fcntl(fd, F_SETFL, arg); 540 | memset(&sa_loc, 0, sizeof(struct sockaddr_in)); 541 | sa_loc.sin_family = AF_INET; 542 | //no need to expilictely set local address, otherwise I got errors on connect() 543 | sa_loc.sin_port = htons(0); //kernel chooses a free port 544 | if ( bind(fd, (struct sockaddr *)&sa_loc, sizeof(struct sockaddr_in)) < 0){ 545 | fprintf(stderr, "Error calling bind()\n"); 546 | fprintf(stderr, "The errno is %d - %s\n", errno, strerror(errno)); 547 | exit(1); 548 | } 549 | if (bWrite){ 550 | write_port_file(fd, "UDP"); 551 | } 552 | return fd; 553 | } 554 | 555 | 556 | void flood_tcp(){ 557 | int sock_num = 10; 558 | vector open_sockets; 559 | for (int i=0; i args = split_string(comm_str); 603 | for (int i=0; i