├── autogen.sh ├── contrib ├── protos ├── domains ├── ssl_host └── hosts ├── Makefile.am ├── include ├── Makefile.am ├── reloadtask.h ├── qdpi.h ├── nfqstatistictask.h ├── patr.h ├── packet.h ├── ndpiwrapper.h ├── sender.h ├── pktanalyzer.h ├── config.h.in ├── AhoCorasickPlus.h ├── node.h ├── sendertask.h ├── ahocorasick.h ├── nfqthread.h ├── main.h ├── patricia.h └── actypes.h ├── src ├── Makefile.am ├── packet.cpp ├── sendertask.cpp ├── qdpi.cpp ├── AhoCorasickPlus.cpp ├── reloadtask.cpp ├── patr.cpp ├── nfqstatistictask.cpp ├── nfqthread.cpp ├── sender.cpp ├── node.cpp ├── main.cpp ├── pktanalyzer.cpp ├── ahocorasick.cpp └── patricia.c ├── etc ├── systemd │ └── nfqfilter.service └── nfqfilter.ini ├── README ├── nDPI-1.7-bugfixes.patch ├── configure.ac └── COPYING /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -f configure 4 | 5 | autoreconf -ivf 6 | 7 | -------------------------------------------------------------------------------- /contrib/protos: -------------------------------------------------------------------------------- 1 | # здесь располагаются доп. данные о протоколах 2 | tcp:24680,tcp:8080@HTTP 3 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = foreign 2 | SUBDIRS = src include 3 | EXTRA_DIST = etc contrib COPYING 4 | -------------------------------------------------------------------------------- /contrib/domains: -------------------------------------------------------------------------------- 1 | # в данном случае будет заблокирован только этот хост 2 | sex-money.ru 3 | # будет заблокирован домен play-gmslots.com и все его поддомены 4 | *.play-gmslots.com 5 | -------------------------------------------------------------------------------- /contrib/ssl_host: -------------------------------------------------------------------------------- 1 | # в данном случае будет заблокирован только этот домен 2 | www.akiba-online.com 3 | # будет заблокирован домен mister-x.com и все его поддомены 4 | *.mister-x.com 5 | -------------------------------------------------------------------------------- /include/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | noinst_HEADERS = main.h nfqstatistictask.h nfqthread.h config.h.in qdpi.h sender.h actypes.h ahocorasick.h node.h AhoCorasickPlus.h sendertask.h ndpiwrapper.h packet.h pktanalyzer.h reloadtask.h patricia.h patr.h 3 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CPPFLAGS = -I$(top_srcdir)/nDPI/src/include 2 | LDADD = $(top_srcdir)/nDPI/src/lib/.libs/libndpi.a 3 | 4 | bin_PROGRAMS = nfqfilter 5 | 6 | nfqfilter_SOURCES = main.cpp nfqstatistictask.cpp nfqthread.cpp qdpi.cpp sender.cpp ahocorasick.cpp node.cpp AhoCorasickPlus.cpp sendertask.cpp packet.cpp pktanalyzer.cpp reloadtask.cpp patricia.c patr.cpp 7 | 8 | -------------------------------------------------------------------------------- /etc/systemd/nfqfilter.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Nfqilter filtering daemon 3 | Requires=network.target 4 | After=network.target 5 | 6 | [Service] 7 | Type=forking 8 | ExecStart=/usr/local/bin/nfqfilter --daemon --pidfile=/var/run/nfqfilter.pid -c /usr/local/etc/nfqfilter.ini 9 | PIDFile=/var/run/nfqfilter.pid 10 | ExecReload=/bin/kill -HUP $MAINPID 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /include/reloadtask.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __RELOAD_TASK_H 3 | #define __RELOAD_TASK_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | class nfqFilter; 10 | 11 | class ReloadTask: public Poco::Task 12 | { 13 | 14 | public: 15 | ReloadTask(nfqFilter *parent); 16 | ~ReloadTask(); 17 | 18 | void runTask(); 19 | void OutStatistic(); 20 | 21 | 22 | static Poco::Event _event; 23 | 24 | private: 25 | nfqFilter *_parent; 26 | Poco::Logger& _logger; 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/qdpi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | #ifndef __QDPI_H 20 | #define __QDPI_H 21 | 22 | #include 23 | 24 | struct ndpi_detection_module_struct* init_ndpi(); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /include/nfqstatistictask.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #ifndef __NFQSTATISTICTASK_H 21 | #define __NFQSTATISTICTASK_H 22 | 23 | class NFQStatisticTask: public Poco::Task 24 | { 25 | public: 26 | NFQStatisticTask(int sec); 27 | void runTask(); 28 | void OutStatistic(); 29 | 30 | private: 31 | // через сколько секунд выводить инфо о потреблении память. 0 - не выводить 32 | int _sec; 33 | }; 34 | 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/patr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #ifndef __PATR_H 21 | #define __PATR_H 22 | 23 | #include 24 | #include 25 | #include "patricia.h" 26 | 27 | class Patricia 28 | { 29 | public: 30 | Patricia(); 31 | ~Patricia(); 32 | 33 | patricia_node_t *make_and_lookup(std::string &addr); 34 | /// Поиск только по адресу 35 | patricia_node_t *try_search_exact_ip(Poco::Net::IPAddress &address); 36 | void print_all_nodes(); 37 | private: 38 | bool fill_prefix(int family, void *dest, int bitlen, prefix_t &prefix); 39 | patricia_tree_t *tree_ipv4; 40 | patricia_tree_t *tree_ipv6; 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ВНИМАНИЕ! Данный проект больше не поддерживается. Рекомендуется использовать extFilter https://github.com/max197616/extfilter . 2 | 3 | Программа основана на разработке https://github.com/ircop/nfq_filter. 4 | 5 | Программа компилируется и работает под CentOS 7. Для сборки программы необходимы следующие программы и библиотеки: 6 | 7 | libnetfilter_queue 8 | libnfnetlink 9 | Poco >= 1.6 10 | nDPI = 1.7-stable (ставится автоматически с github с наложением необходимых патчей) 11 | git 12 | patch 13 | 14 | Сборка программы: 15 | 16 | 1 ./autogen.sh 17 | 2 ./configure 18 | 3 make 19 | 20 | 21 | Для запуска программы необходимо указать путь к конфигурационному .ini файлу (-c в командой строке). Для запуска в режиме daemon необходимо указать ключи --daemon и --pidfile=/path/to/file.pid 22 | 23 | 24 | Для перенаправления трафика на фильтр необходимо использовать следующую конструкцию iptables: 25 | 26 | iptables -t mangle -A PREROUTING -s x.x.x.x/y -p tcp -m tcp -j NFQUEUE --queue-num 0 --queue-bypass 27 | 28 | где, x.x.x.x/y сеть, которую необходимо проверять. 29 | 30 | Для блокировки SSL/ip:port трафика необходимо использовать следующее правило: 31 | 32 | iptables -A FORWARD -m mark --mark 17 -p tcp -j REJECT --reject-with tcp-reset 33 | 34 | где 17, это значение из конфига nfqfilter. 35 | 36 | Так же вместо маркировки трафика можно сразу посылать TCP RST клиенту и серверу, если указать в конфиге send_rst = true . 37 | 38 | В каталоге contrib находятся примеры файлов для работы фильтра. 39 | 40 | Чтобы заставить фильтр перечитать конфиги, необходимо послать ему сигнал HUP. 41 | -------------------------------------------------------------------------------- /include/packet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #ifndef __PACKET_H 21 | #define __PACKET_H 22 | 23 | #include 24 | #include 25 | 26 | #define DYNAMIC_MEM 27 | 28 | class Packet 29 | { 30 | public: 31 | Packet(struct nfq_q_handle *qh, int packet_id, struct nfq_data *nfa); 32 | ~Packet(); 33 | inline int get_id() 34 | { 35 | return _id; 36 | } 37 | inline uint8_t *get_payload() 38 | { 39 | #ifdef DYNAMIC_MEM 40 | return _ext_pkt; 41 | #else 42 | return &_ext_pkt[0]; 43 | #endif 44 | } 45 | inline uint32_t get_size() 46 | { 47 | return _pktlen; 48 | } 49 | inline struct nfq_q_handle *get_qh() 50 | { 51 | return _qh; 52 | } 53 | private: 54 | 55 | struct nfq_q_handle *_qh; 56 | 57 | int _id; /* номер пакета в nfq*/ 58 | 59 | uint32_t _pktlen; 60 | 61 | #ifdef DYNAMIC_MEM 62 | uint8_t *_ext_pkt; 63 | #else 64 | uint8_t _ext_pkt[2000]; 65 | #endif 66 | 67 | struct timeval _ts; 68 | }; 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /include/ndpiwrapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #ifndef __NDPI_WRAPPER_H 21 | #define __NDPI_WRAPPER_H 22 | 23 | #include "main.h" 24 | 25 | class nDPIWrapper 26 | { 27 | public: 28 | nDPIWrapper() 29 | { 30 | src = (struct ndpi_id_struct*)calloc(1,nfqFilter::ndpi_size_id_struct); 31 | dst = (struct ndpi_id_struct*)calloc(1,nfqFilter::ndpi_size_id_struct); 32 | flow = (struct ndpi_flow_struct *)calloc(1,nfqFilter::ndpi_size_flow_struct); 33 | } 34 | ~nDPIWrapper() 35 | { 36 | ndpi_free_flow(flow); 37 | if(src) 38 | free(src); 39 | if(dst) 40 | free(dst); 41 | } 42 | 43 | inline struct ndpi_flow_struct *get_flow() 44 | { 45 | return flow; 46 | } 47 | inline struct ndpi_id_struct *get_src() 48 | { 49 | return src; 50 | } 51 | inline struct ndpi_id_struct *get_dst() 52 | { 53 | return dst; 54 | } 55 | private: 56 | struct ndpi_id_struct *src; 57 | struct ndpi_id_struct *dst; 58 | struct ndpi_flow_struct *flow; 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/packet.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | 21 | 22 | #include "packet.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | 31 | Packet::Packet(struct nfq_q_handle *qh, int packet_id, struct nfq_data *nfa) : _qh(qh) 32 | { 33 | int ret; 34 | #ifdef DYNAMIC_MEM 35 | _ext_pkt=NULL; 36 | #endif 37 | unsigned char *pktdata; 38 | _id=packet_id; 39 | ret = nfq_get_payload(nfa, &pktdata); 40 | if (ret > 0) 41 | { 42 | if (ret > 65536) 43 | { 44 | _pktlen=0; 45 | } else { 46 | _pktlen=ret; 47 | #ifdef DYNAMIC_MEM 48 | _ext_pkt = (uint8_t *) calloc(1,ret); 49 | #endif 50 | memcpy(_ext_pkt,pktdata,ret); 51 | } 52 | } else if (ret == -1) 53 | { 54 | _pktlen=0; 55 | } 56 | ret = nfq_get_timestamp(nfa, &_ts); 57 | if(ret != 0) 58 | { 59 | memset(&_ts,0,sizeof(struct timeval)); 60 | gettimeofday(&_ts,NULL); 61 | } 62 | } 63 | 64 | Packet::~Packet() 65 | { 66 | #ifdef DYNAMIC_MEM 67 | if(_ext_pkt) 68 | { 69 | free(_ext_pkt); 70 | } 71 | #endif 72 | } 73 | 74 | -------------------------------------------------------------------------------- /src/sendertask.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #include "sendertask.h" 21 | 22 | #include "sender.h" 23 | 24 | Poco::FastMutex SenderTask::_mutex; 25 | Poco::NotificationQueue SenderTask::queue; 26 | 27 | SenderTask::SenderTask(struct CSender::params &prm): 28 | Task("SenderTask"), 29 | sender(new CSender(prm)), 30 | _logger(Poco::Logger::get("SenderTask")) 31 | { 32 | 33 | } 34 | 35 | 36 | SenderTask::~SenderTask() 37 | { 38 | delete sender; 39 | } 40 | 41 | void SenderTask::runTask() 42 | { 43 | _logger.debug("Starting SenderTask..."); 44 | 45 | while(!isCancelled()) 46 | { 47 | Poco::Notification::Ptr pNf(queue.waitDequeueNotification()); 48 | if (pNf) 49 | { 50 | RedirectNotification::Ptr pRedirectNf = pNf.cast(); 51 | if (pRedirectNf) 52 | { 53 | if(pRedirectNf->is_rst()) 54 | sender->SendRST(pRedirectNf->user_port(), pRedirectNf->dst_port(),pRedirectNf->user_ip(),pRedirectNf->dst_ip(), pRedirectNf->acknum(), pRedirectNf->seqnum(), pRedirectNf->f_psh()); 55 | else 56 | sender->Redirect(pRedirectNf->user_port(), pRedirectNf->dst_port(),pRedirectNf->user_ip(),pRedirectNf->dst_ip(), pRedirectNf->acknum(), pRedirectNf->seqnum(), pRedirectNf->f_psh(), pRedirectNf->additional_param()); 57 | } 58 | } 59 | } 60 | 61 | _logger.debug("Stopping SenderTask..."); 62 | } 63 | 64 | -------------------------------------------------------------------------------- /include/sender.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | #ifndef __SENDER_H 20 | #define __SENDER_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | 38 | 39 | class CSender { 40 | public: 41 | struct params 42 | { 43 | std::string redirect_url; 44 | std::string code="302 Moved Temporarily"; 45 | }; 46 | CSender( std::string url ); 47 | CSender(struct params &prm); 48 | ~CSender(); 49 | void Redirect(int user_port, int dst_port, Poco::Net::IPAddress &src_ip, Poco::Net::IPAddress &dst_ip, uint32_t acknum, uint32_t seqnum, int f_psh, std::string &additional_param); 50 | void sendPacket(Poco::Net::IPAddress &ip_from, Poco::Net::IPAddress &ip_to, int port_from, int port_to, uint32_t acknum, uint32_t seqnum, std::string &dt, int f_reset, int f_psh); 51 | void SendRST(int user_port, int dst_port, Poco::Net::IPAddress &user_ip, Poco::Net::IPAddress &dst_ip, uint32_t acknum, uint32_t seqnum, int f_psh); 52 | private: 53 | unsigned short csum(unsigned short *ptr, int nbytes); 54 | int s; 55 | int s6; 56 | std::string rHeader; 57 | Poco::Logger& _logger; 58 | struct params _parameters; 59 | }; 60 | 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /etc/nfqfilter.ini: -------------------------------------------------------------------------------- 1 | ; номер очереди 2 | queue = 0 3 | ; файл с доменами для блокировки 4 | domainlist = /path/to/domains 5 | ; файл с url для блокировки 6 | urllist = /path/to/urls 7 | ; файл с ssl доменами для блокировки 8 | ssllist = /path/to/ssl_host 9 | ; файл с ip:port для блокировки 10 | hostlist = contrib/hosts 11 | 12 | ; куда редиректить в случае наличия в списках 13 | redirect_url = http://notify.example.com/? 14 | ; HTTP код ответа. default: 302 Moved Temporarily 15 | http_code = 302 Moved Temporarily 16 | ; Что добавлять в redirect_url, line - строка из файла url, url - запрещенный url, none - ничего 17 | url_additional_info=url 18 | 19 | ; дополнительные порты для протоколов (nDPI) 20 | protocols = contrib/protos 21 | ; периодичность вывода статистики в лог, секунд 22 | statistic_interval = 300 23 | ; если установлено в true, то сам nfqfilter отправляет tcp rst клиенту и серверу (тогда mark_value не используется) в случае фильтрации https и ip:port 24 | send_rst = true 25 | ; каким значением метить пакеты для iptables в случае наличия в списках ssl и hosts 26 | mark_value = 17 27 | ; количество обрабатываемых пакетов (default: 1024) 28 | ; max_pending_packets = 1024 29 | ; сохранять пакеты ошибками в /tmp 30 | ; save_bad_packets = false 31 | ; блокировать ssl client hello по первому пакету, если в ssl пакете нет server_name. Обязательно наличие файла sslips с ip адресами. 32 | ; block_undetected_ssl = false 33 | ; Путь к файлу с ip адресами для блокировки ssl по ip, если в ssl пакете нет server_name (block_undetected_ssl = true) 34 | ; sslips = /path/to/file 35 | ; перевод host: в строчные символы 36 | ; lower_host = false 37 | ; 38 | ; match_url_exactly = false 39 | ; Если в url встречаются escape последовательности символов 0-9, a-z, A-Z, то переводить их в символы 40 | ; url_decode = false 41 | ; Количество потоков обработки пакетов 42 | ; num_threads = 2 43 | 44 | [logging] 45 | loggers.root.level = information 46 | ;loggers.root.level = debug 47 | loggers.root.channel = fileChannel 48 | channels.fileChannel.class = FileChannel 49 | channels.fileChannel.path = /var/log/nfqfilter.log 50 | channels.fileChannel.rotation = 1 M 51 | channels.fileChannel.archive = timestamp 52 | channels.fileChannel.formatter.class = PatternFormatter 53 | channels.fileChannel.formatter.pattern = %Y-%m-%d %H:%M:%S.%i [%P] %p %s - %t 54 | channels.fileChannel.formatter.times = local 55 | -------------------------------------------------------------------------------- /include/pktanalyzer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #ifndef __PKTANALYZER_H 21 | #define __PKTANALYZER_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "packet.h" 31 | #include "nfqthread.h" 32 | 33 | class PktNotification: public Poco::Notification 34 | // The notification sent to worker threads. 35 | { 36 | public: 37 | typedef Poco::AutoPtr Ptr; 38 | 39 | PktNotification(Packet &pkt): 40 | _pkt(pkt) 41 | { 42 | } 43 | 44 | PktNotification(struct nfq_q_handle *qh, int packet_id, struct nfq_data *nfa): 45 | _pkt(qh,packet_id,nfa) 46 | { 47 | } 48 | 49 | Packet &pkt() 50 | { 51 | return _pkt; 52 | } 53 | 54 | private: 55 | Packet _pkt; 56 | }; 57 | 58 | 59 | class PktAnalyzer: public Poco::Runnable 60 | // A worker thread that gets work items 61 | // from a NotificationQueue. 62 | { 63 | public: 64 | PktAnalyzer(const std::string& name, Poco::NotificationQueue& queue, struct nfqConfig& cfg, nfqThread *parent); 65 | void run(); 66 | char from_hex(char ch); 67 | std::string url_decode(std::string text); 68 | void stop(); 69 | void analyzer(Packet &pkt); 70 | void dump_file(unsigned char *buf,uint32_t rv, int pkt_id); 71 | private: 72 | std::string _name; 73 | Poco::NotificationQueue& _queue; 74 | Poco::Logger& _logger; 75 | mutable Poco::FastMutex _mutex; 76 | struct nfqConfig _config; 77 | 78 | nfqThread *_parent; 79 | int _idleTime; 80 | bool _stopped; 81 | }; 82 | 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /contrib/hosts: -------------------------------------------------------------------------------- 1 | 186.2.162.77:3001 2 | 186.2.162.63:3001 3 | 186.2.162.77:4004 4 | 186.2.162.63:4004 5 | 186.2.162.77:1001 6 | 186.2.162.63:1001 7 | 186.2.162.77:1002 8 | 186.2.162.63:1002 9 | 5.196.168.249:3049 10 | 5.196.168.249:4001 11 | 149.202.17.160:10000 12 | 186.2.162.66:3001 13 | 5.196.168.249:4002 14 | 5.196.168.249:4003 15 | 5.196.168.249:1001 16 | 5.196.168.249:1002 17 | 5.196.168.249:4004 18 | 178.32.89.249:3002 19 | 178.32.89.249:3004 20 | 186.2.162.77:3001 21 | 186.2.162.63:3001 22 | 186.2.162.77:3049 23 | 186.2.162.63:3049 24 | 186.2.162.77:4001 25 | 186.2.162.63:4001 26 | 186.2.162.77:4002 27 | 186.2.162.63:4002 28 | 186.2.162.77:4003 29 | 186.2.162.63:4003 30 | 149.202.17.161:10000 31 | 186.2.162.66:3004 32 | 178.32.89.249:3001 33 | 186.2.162.17:4001 34 | 186.2.162.17:4002 35 | 5.196.168.249:1003 36 | 186.2.162.77:1003 37 | 186.2.162.63:1003 38 | 186.2.162.66:3002 39 | 190.115.26.42:3001 40 | 190.115.26.42:3049 41 | 190.115.26.42:4001 42 | 190.115.26.42:4002 43 | 190.115.26.42:4003 44 | 190.115.26.42:4004 45 | 190.115.26.42:1001 46 | 190.115.26.42:1002 47 | 190.115.26.42:1003 48 | 190.115.26.42:3001 49 | 93.170.92.48:4930 50 | 93.170.92.48:4938 51 | 149.202.99.210:10000 52 | 149.202.99.208:10000 53 | 149.202.99.215:10000 54 | 5.196.168.249:3001 55 | 149.202.99.233:10000 56 | 62.149.24.67:4930 57 | 62.149.24.150:4930 58 | 62.149.24.67:4938 59 | 62.149.24.150:4938 60 | 186.2.162.17:3001 61 | 149.202.99.214:10000 62 | 149.202.99.232:10000 63 | 186.2.162.17:1001 64 | 186.2.162.17:1002 65 | 186.2.162.17:1003 66 | 149.202.99.211:10000 67 | 149.202.99.213:10000 68 | 149.202.99.111:10000 69 | 149.202.99.209:10000 70 | 149.202.99.110:10000 71 | 37.187.172.228:3001 72 | 37.187.172.228:3049 73 | 37.187.172.228:4001 74 | 37.187.172.228:4002 75 | 37.187.172.228:4003 76 | 37.187.172.228:4004 77 | 37.187.172.228:1001 78 | 37.187.172.228:1002 79 | 37.187.172.228:1003 80 | 37.187.172.228:3001 81 | 149.202.99.109:10000 82 | 186.2.162.17:3049 83 | 149.202.99.212:10000 84 | 62.149.24.152:4930 85 | 62.149.24.74:4930 86 | 62.149.24.152:4938 87 | 62.149.24.74:4938 88 | 186.2.162.17:4003 89 | 186.2.162.17:4004 90 | 178.32.89.251:3001 91 | 178.32.89.251:3049 92 | 178.32.89.251:4001 93 | 178.32.89.251:4002 94 | 178.32.89.251:4003 95 | 178.32.89.251:4004 96 | 178.32.89.251:1001 97 | 178.32.89.251:1002 98 | 178.32.89.251:1003 99 | 178.32.89.251:3001 100 | -------------------------------------------------------------------------------- /include/config.h.in: -------------------------------------------------------------------------------- 1 | /* include/config.h.in. Generated from configure.ac by autoheader. */ 2 | 3 | /* Define to 1 if you have the header file. */ 4 | #undef HAVE_INTTYPES_H 5 | 6 | /* Define to 1 if you have the `netfilter_queue' library (-lnetfilter_queue). 7 | */ 8 | #undef HAVE_LIBNETFILTER_QUEUE 9 | 10 | /* Define to 1 if you have the `nfnetlink' library (-lnfnetlink). */ 11 | #undef HAVE_LIBNFNETLINK 12 | 13 | /* Define to 1 if you have the header file. */ 14 | #undef HAVE_MEMORY_H 15 | 16 | /* Define to 1 if you have the header file. */ 17 | #undef HAVE_NETINET_IN_H 18 | 19 | /* Define to 1 if you have the header file. */ 20 | #undef HAVE_POCO_FOUNDATION_H 21 | 22 | /* Define to 1 if you have the header file. */ 23 | #undef HAVE_POCO_NET_HTTPCOOKIE_H 24 | 25 | /* Define to 1 if you have the header file. */ 26 | #undef HAVE_POCO_UTIL_TIMER_H 27 | 28 | /* Define to 1 if you have the header file. */ 29 | #undef HAVE_STDINT_H 30 | 31 | /* Define to 1 if you have the header file. */ 32 | #undef HAVE_STDLIB_H 33 | 34 | /* Define to 1 if you have the `strerror' function. */ 35 | #undef HAVE_STRERROR 36 | 37 | /* Define to 1 if you have the header file. */ 38 | #undef HAVE_STRINGS_H 39 | 40 | /* Define to 1 if you have the header file. */ 41 | #undef HAVE_STRING_H 42 | 43 | /* Define to 1 if you have the header file. */ 44 | #undef HAVE_SYS_STAT_H 45 | 46 | /* Define to 1 if you have the header file. */ 47 | #undef HAVE_SYS_TYPES_H 48 | 49 | /* Define to 1 if you have the header file. */ 50 | #undef HAVE_UNISTD_H 51 | 52 | /* Define to 1 if the system has the type `_Bool'. */ 53 | #undef HAVE__BOOL 54 | 55 | /* Name of package */ 56 | #undef PACKAGE 57 | 58 | /* Define to the address where bug reports for this package should be sent. */ 59 | #undef PACKAGE_BUGREPORT 60 | 61 | /* Define to the full name of this package. */ 62 | #undef PACKAGE_NAME 63 | 64 | /* Define to the full name and version of this package. */ 65 | #undef PACKAGE_STRING 66 | 67 | /* Define to the one symbol short name of this package. */ 68 | #undef PACKAGE_TARNAME 69 | 70 | /* Define to the home page for this package. */ 71 | #undef PACKAGE_URL 72 | 73 | /* Define to the version of this package. */ 74 | #undef PACKAGE_VERSION 75 | 76 | /* Define to 1 if you have the ANSI C header files. */ 77 | #undef STDC_HEADERS 78 | 79 | /* Version number of package */ 80 | #undef VERSION 81 | -------------------------------------------------------------------------------- /include/AhoCorasickPlus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AhoCorasickPlus.h: This is the header file for a sample 3 | * C++ wrapper for Aho-Corasick C library 4 | * 5 | * This file is part of multifast. 6 | * 7 | Copyright 2010-2013 Kamiar Kanani 8 | 9 | multifast is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Lesser General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | multifast is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public License 20 | along with multifast. If not, see . 21 | */ 22 | 23 | #ifndef AHOCORASICKPPW_H_ 24 | #define AHOCORASICKPPW_H_ 25 | 26 | #include 27 | using std::string; 28 | #include 29 | using std::queue; 30 | 31 | #include "actypes.h" 32 | 33 | // forward declaration 34 | struct AC_AUTOMATA; 35 | struct AC_TEXT; 36 | 37 | 38 | class AhoCorasickPlus 39 | { 40 | public: 41 | 42 | enum EnumReturnStatus 43 | { 44 | RETURNSTATUS_SUCCESS = 0, // No error occurred 45 | RETURNSTATUS_DUPLICATE_PATTERN, // Duplicate patterns 46 | RETURNSTATUS_LONG_PATTERN, // Pattern length is bigger than AC_PATTRN_MAX_LENGTH 47 | RETURNSTATUS_ZERO_PATTERN, // Empty pattern (zero length) 48 | RETURNSTATUS_AUTOMATA_CLOSED, // Automata is closed 49 | RETURNSTATUS_FAILED, // General unknown failure 50 | }; 51 | 52 | typedef unsigned int PatternId; 53 | 54 | struct Match 55 | { 56 | unsigned int position; 57 | PatternId id; 58 | AC_PATTERN_t pattern; 59 | }; 60 | 61 | public: 62 | 63 | AhoCorasickPlus(); 64 | ~AhoCorasickPlus(); 65 | 66 | EnumReturnStatus addPattern (const std::string &pattern, PatternId id); 67 | EnumReturnStatus addPattern (const char pattern[], PatternId id); // zero ending string 68 | void finalize (); 69 | 70 | void search (std::string& text, bool keep); 71 | bool findNext (Match& match); 72 | 73 | private: 74 | AC_AUTOMATA *m_automata; 75 | AC_TEXT *m_acText; 76 | std::queue m_matchQueue; // if multiple matches occur in a single position 77 | // we save them here and return one by one 78 | // for simplicity 79 | }; 80 | 81 | #endif /* AHOCORASICKPPW_H_ */ 82 | -------------------------------------------------------------------------------- /include/node.h: -------------------------------------------------------------------------------- 1 | /* 2 | * node.h: automata node header file 3 | * This file is part of multifast. 4 | * 5 | Copyright 2010-2013 Kamiar Kanani 6 | 7 | multifast is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | multifast is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with multifast. If not, see . 19 | */ 20 | 21 | #ifndef _NODE_H_ 22 | #define _NODE_H_ 23 | 24 | #include "actypes.h" 25 | 26 | #ifdef __cplusplus 27 | /*extern "C" {*/ 28 | #endif 29 | 30 | /* Forward Declaration */ 31 | struct edge; 32 | 33 | /* automata node */ 34 | typedef struct AC_NODE 35 | { 36 | int id; /* Node ID : for debugging purpose */ 37 | short int final; /* 0: no ; 1: yes, it is a final node */ 38 | struct AC_NODE * failure_node; /* The failure node of this node */ 39 | unsigned short depth; /* depth: distance between this node and the root */ 40 | 41 | /* Matched patterns */ 42 | AC_PATTERN_t * matched_patterns; /* Array of matched patterns */ 43 | unsigned short matched_patterns_num; /* Number of matched patterns at this node */ 44 | unsigned short matched_patterns_max; /* Max capacity of allocated memory for matched_patterns */ 45 | 46 | /* Outgoing Edges */ 47 | struct edge * outgoing; /* Array of outgoing edges */ 48 | unsigned short outgoing_degree; /* Number of outgoing edges */ 49 | unsigned short outgoing_max; /* Max capacity of allocated memory for outgoing */ 50 | } AC_NODE_t; 51 | 52 | /* The Edge of the Node */ 53 | struct edge 54 | { 55 | AC_ALPHABET_t alpha; /* Edge alpha */ 56 | AC_NODE_t * next; /* Target of the edge */ 57 | }; 58 | 59 | 60 | AC_NODE_t * node_create (void); 61 | AC_NODE_t * node_create_next (AC_NODE_t * thiz, AC_ALPHABET_t alpha); 62 | void node_register_matchstr (AC_NODE_t * thiz, AC_PATTERN_t * str); 63 | void node_register_outgoing (AC_NODE_t * thiz, AC_NODE_t * next, AC_ALPHABET_t alpha); 64 | AC_NODE_t * node_find_next (AC_NODE_t * thiz, AC_ALPHABET_t alpha); 65 | AC_NODE_t * node_findbs_next (AC_NODE_t * thiz, AC_ALPHABET_t alpha); 66 | void node_release (AC_NODE_t * thiz); 67 | void node_assign_id (AC_NODE_t * thiz); 68 | void node_sort_edges (AC_NODE_t * thiz); 69 | 70 | #ifdef __cplusplus 71 | /*}*/ 72 | #endif 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/qdpi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #include "qdpi.h" 21 | #include 22 | 23 | #include "main.h" 24 | 25 | 26 | static void *malloc_wrapper(unsigned long size) 27 | { 28 | nfqFilter::current_ndpi_memory += size; 29 | if(nfqFilter::current_ndpi_memory > nfqFilter::max_ndpi_memory) 30 | nfqFilter::max_ndpi_memory = nfqFilter::current_ndpi_memory; 31 | return calloc(1,size); 32 | } 33 | 34 | static void free_wrapper(void *freeable) 35 | { 36 | free(freeable); 37 | } 38 | 39 | void debug_printf(u_int32_t protocol, void *id_struct, ndpi_log_level_t log_level, const char *format, ...) { 40 | va_list va_ap; 41 | struct tm result; 42 | 43 | char buf[8192], out_buf[8192]; 44 | char theDate[32]; 45 | const char *extra_msg = ""; 46 | time_t theTime = time(NULL); 47 | 48 | va_start (va_ap, format); 49 | 50 | /* 51 | if(log_level == NDPI_LOG_ERROR) 52 | extra_msg = "ERROR: "; 53 | else if(log_level == NDPI_LOG_TRACE) 54 | extra_msg = "TRACE: "; 55 | else. 56 | extra_msg = "DEBUG: "; 57 | */ 58 | 59 | memset(buf, 0, sizeof(buf)); 60 | strftime(theDate, 32, "%d/%b/%Y %H:%M:%S", localtime_r(&theTime, &result) ); 61 | vsnprintf(buf, sizeof(buf)-1, format, va_ap); 62 | 63 | snprintf(out_buf, sizeof(out_buf), "%s %s%s", theDate, extra_msg, buf); 64 | printf("%s", out_buf); 65 | Poco::Util::Application& app = Poco::Util::Application::instance(); 66 | std::string msg(&out_buf[0]); 67 | app.logger().information("nDPI message: %s",msg); 68 | 69 | fflush(stdout); 70 | 71 | va_end(va_ap); 72 | } 73 | 74 | struct ndpi_detection_module_struct* init_ndpi() 75 | { 76 | u_int32_t detection_tick_resolution = 1000; 77 | struct ndpi_detection_module_struct* my_ndpi_struct = ndpi_init_detection_module(detection_tick_resolution, malloc_wrapper, free_wrapper, debug_printf); 78 | 79 | if (my_ndpi_struct == NULL) { 80 | return NULL; 81 | } 82 | 83 | // my_ndpi_struct->http_dont_dissect_response=1; 84 | 85 | NDPI_PROTOCOL_BITMASK all; 86 | 87 | NDPI_BITMASK_ADD(all,NDPI_PROTOCOL_HTTP); 88 | NDPI_BITMASK_ADD(all,NDPI_PROTOCOL_SSL); 89 | 90 | // enable all protocols 91 | // NDPI_BITMASK_SET_ALL(all); 92 | ndpi_set_protocol_detection_bitmask2(my_ndpi_struct, &all); 93 | 94 | return my_ndpi_struct; 95 | } 96 | -------------------------------------------------------------------------------- /include/sendertask.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | #ifndef __SENDER_TASK_H 20 | #define __SENDER_TASK_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "sender.h" 31 | 32 | class RedirectNotification: public Poco::Notification 33 | // The notification sent to worker threads. 34 | { 35 | public: 36 | typedef Poco::AutoPtr Ptr; 37 | 38 | RedirectNotification(int user_port, int dst_port, Poco::Net::IPAddress *user_ip, Poco::Net::IPAddress *dst_ip, uint32_t acknum, uint32_t seqnum, int f_psh, std::string &additional_param, bool is_rst=false): 39 | _user_port(user_port), 40 | _dst_port(dst_port), 41 | _user_ip(*user_ip), 42 | _dst_ip(*dst_ip), 43 | _acknum(acknum), 44 | _seqnum(seqnum), 45 | _f_psh(f_psh), 46 | _additional_param(additional_param), 47 | _is_rst(is_rst) 48 | { 49 | } 50 | int user_port() 51 | { 52 | return _user_port; 53 | } 54 | int dst_port() 55 | { 56 | return _dst_port; 57 | } 58 | Poco::Net::IPAddress &user_ip() 59 | { 60 | return _user_ip; 61 | } 62 | Poco::Net::IPAddress &dst_ip() 63 | { 64 | return _dst_ip; 65 | } 66 | u_int32_t acknum() 67 | { 68 | return _acknum; 69 | } 70 | u_int32_t seqnum() 71 | { 72 | return _seqnum; 73 | } 74 | int f_psh() 75 | { 76 | return _f_psh; 77 | } 78 | std::string &additional_param() 79 | { 80 | return _additional_param; 81 | } 82 | bool is_rst() 83 | { 84 | return _is_rst; 85 | } 86 | private: 87 | int _user_port; 88 | int _dst_port; 89 | Poco::Net::IPAddress _user_ip; 90 | Poco::Net::IPAddress _dst_ip; 91 | uint32_t _acknum; 92 | uint32_t _seqnum; 93 | int _f_psh; 94 | std::string _additional_param; 95 | bool _is_rst; 96 | }; 97 | 98 | /// Данная задача отсылает редирект заданному клиенту 99 | class SenderTask: public Poco::Task 100 | { 101 | public: 102 | SenderTask(struct CSender::params &prm); 103 | ~SenderTask(); 104 | 105 | void runTask(); 106 | 107 | // очередь, куда необходимо писать отправные данные... 108 | static Poco::NotificationQueue queue; 109 | 110 | private: 111 | CSender *sender; 112 | static Poco::FastMutex _mutex; 113 | Poco::Logger& _logger; 114 | }; 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /include/ahocorasick.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ahocorasick.h: the main ahocorasick header file. 3 | * This file is part of multifast. 4 | * 5 | Copyright 2010-2013 Kamiar Kanani 6 | 7 | multifast is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | multifast is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with multifast. If not, see . 19 | */ 20 | 21 | #ifndef _AUTOMATA_H_ 22 | #define _AUTOMATA_H_ 23 | 24 | #include "actypes.h" 25 | 26 | #ifdef __cplusplus 27 | /*extern "C" {*/ 28 | #endif 29 | 30 | struct AC_NODE; 31 | 32 | typedef struct AC_AUTOMATA 33 | { 34 | /* The root of the Aho-Corasick trie */ 35 | struct AC_NODE * root; 36 | 37 | /* maintain all nodes pointers. it will be used to access or release 38 | * all nodes. */ 39 | struct AC_NODE ** all_nodes; 40 | 41 | unsigned int all_nodes_num; /* Number of all nodes in the automata */ 42 | unsigned int all_nodes_max; /* Current max allocated memory for *all_nodes */ 43 | 44 | /* this flag indicates that if automata is finalized by 45 | * ac_automata_finalize() or not. 1 means finalized and 0 46 | * means not finalized (is open). after finalizing automata you can not 47 | * add pattern to automata anymore. */ 48 | unsigned short automata_open; 49 | 50 | /* It is possible to feed a large input to the automata chunk by chunk to 51 | * be searched using ac_automata_search(). in fact by default automata 52 | * thinks that all chunks are related unless you do ac_automata_reset(). 53 | * followings are variables that keep track of searching state. */ 54 | struct AC_NODE * current_node; /* Pointer to current node while searching */ 55 | unsigned long base_position; /* Represents the position of current chunk 56 | * related to whole input text */ 57 | 58 | /* The input text. 59 | * used only when it is working in settext/findnext mode */ 60 | AC_TEXT_t * text; 61 | 62 | /* The lase searched position in the chunk. 63 | * used only when it is working in settext/findnext mode */ 64 | unsigned long position; 65 | 66 | /* Statistic Variables */ 67 | 68 | /* Total patterns in the automata */ 69 | unsigned long total_patterns; 70 | 71 | } AC_AUTOMATA_t; 72 | 73 | 74 | AC_AUTOMATA_t * ac_automata_init (void); 75 | AC_STATUS_t ac_automata_add (AC_AUTOMATA_t * thiz, AC_PATTERN_t * str); 76 | void ac_automata_finalize (AC_AUTOMATA_t * thiz); 77 | int ac_automata_search (AC_AUTOMATA_t * thiz, AC_TEXT_t * text, int keep, AC_MATCH_CALBACK_f callback, void * param); 78 | 79 | void ac_automata_settext (AC_AUTOMATA_t * thiz, AC_TEXT_t * text, int keep); 80 | AC_MATCH_t * ac_automata_findnext (AC_AUTOMATA_t * thiz); 81 | 82 | void ac_automata_release (AC_AUTOMATA_t * thiz); 83 | void ac_automata_display (AC_AUTOMATA_t * thiz, char repcast); 84 | 85 | 86 | #ifdef __cplusplus 87 | /*}*/ 88 | #endif 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /nDPI-1.7-bugfixes.patch: -------------------------------------------------------------------------------- 1 | diff --git a/configure.ac b/configure.ac 2 | index ab32d67..b72ebd7 100644 3 | --- a/configure.ac 4 | +++ b/configure.ac 5 | @@ -1,6 +1,7 @@ 6 | AC_INIT([libndpi], [1.7.0]) 7 | 8 | AC_CONFIG_MACRO_DIR([m4]) 9 | +AC_CONFIG_AUX_DIR([.]) 10 | 11 | AM_INIT_AUTOMAKE([foreign subdir-objects]) 12 | 13 | diff --git a/src/include/ndpi_typedefs.h b/src/include/ndpi_typedefs.h 14 | index d73768f..e9d1402 100644 15 | --- a/src/include/ndpi_typedefs.h 16 | +++ b/src/include/ndpi_typedefs.h 17 | @@ -911,7 +911,7 @@ typedef struct ndpi_flow_struct { 18 | } ntp; 19 | 20 | struct { 21 | - char client_certificate[48], server_certificate[48]; 22 | + char client_certificate[128], server_certificate[128]; 23 | } ssl; 24 | } protos; 25 | 26 | diff --git a/src/lib/protocols/ssl.c b/src/lib/protocols/ssl.c 27 | index b113bf1..f978de6 100644 28 | --- a/src/lib/protocols/ssl.c 29 | +++ b/src/lib/protocols/ssl.c 30 | @@ -101,6 +101,7 @@ static void stripCertificateTrailer(char *buffer, int buffer_len) { 31 | 32 | if((buffer[i] != '.') 33 | && (buffer[i] != '-') 34 | + && (buffer[i] != '_') 35 | && (buffer[i] != '*') 36 | && (!ndpi_isalpha(buffer[i])) 37 | && (!ndpi_isdigit(buffer[i]))) { 38 | @@ -234,7 +235,7 @@ int getSSLcertificate(struct ndpi_detection_module_struct *ndpi_struct, 39 | extensions_len = packet->payload[offset]; 40 | 41 | if((extensions_len+offset) < total_len) { 42 | - u_int16_t extension_offset = 1; /* Move to the first extension */ 43 | + u_int extension_offset = 1; /* Move to the first extension */ 44 | 45 | while(extension_offset < extensions_len) { 46 | u_int16_t extension_id, extension_len; 47 | @@ -251,6 +252,12 @@ int getSSLcertificate(struct ndpi_detection_module_struct *ndpi_struct, 48 | u_int begin = 0,len; 49 | char *server_name = (char*)&packet->payload[offset+extension_offset]; 50 | 51 | + if(packet->payload[offset+extension_offset+2] == 0x00) // host_name 52 | + { 53 | + 54 | + begin =+ 5; 55 | + } 56 | + 57 | while(begin < extension_len) { 58 | if((!ndpi_isprint(server_name[begin])) 59 | || ndpi_ispunct(server_name[begin]) 60 | @@ -263,7 +270,7 @@ int getSSLcertificate(struct ndpi_detection_module_struct *ndpi_struct, 61 | len = (u_int)ndpi_min(extension_len-begin, buffer_len-1); 62 | strncpy(buffer, &server_name[begin], len); 63 | buffer[len] = '\0'; 64 | - stripCertificateTrailer(buffer, buffer_len); 65 | + //stripCertificateTrailer(buffer, buffer_len); 66 | 67 | snprintf(flow->protos.ssl.client_certificate, 68 | sizeof(flow->protos.ssl.client_certificate), "%s", buffer); 69 | @@ -288,8 +295,6 @@ int getSSLcertificate(struct ndpi_detection_module_struct *ndpi_struct, 70 | int sslDetectProtocolFromCertificate(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow) { 71 | struct ndpi_packet_struct *packet = &flow->packet; 72 | 73 | - if(!packet->iph /* IPv4 */) return(-1); 74 | - 75 | if((packet->payload_packet_len > 9) 76 | && (packet->payload[0] == 0x16 /* consider only specific SSL packets (handshake) */)) { 77 | if((packet->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN) 78 | diff --git a/src/lib/protocols/tor.c b/src/lib/protocols/tor.c 79 | index 036162b..693661c 100644 80 | --- a/src/lib/protocols/tor.c 81 | +++ b/src/lib/protocols/tor.c 82 | @@ -24,7 +24,7 @@ int ndpi_is_ssl_tor(struct ndpi_detection_module_struct *ndpi_struct, 83 | 84 | if((certificate == NULL) 85 | || (strlen(certificate) < 6) 86 | - || strncmp(certificate, "www.", 4)) 87 | + || !strncmp(certificate, "www.", 4)) 88 | return(0); 89 | 90 | // printf("***** [SSL] %s(): %s\n", __FUNCTION__, certificate); 91 | -------------------------------------------------------------------------------- /src/AhoCorasickPlus.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AhoCorasickPlus.cpp: This is the implementation file for a sample 3 | * C++ wrapper for Aho-Corasick C library 4 | * 5 | * This file is part of multifast. 6 | * 7 | Copyright 2010-2013 Kamiar Kanani 8 | 9 | multifast is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU Lesser General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | multifast is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public License 20 | along with multifast. If not, see . 21 | */ 22 | 23 | #include "ahocorasick.h" 24 | #include "AhoCorasickPlus.h" 25 | 26 | AhoCorasickPlus::AhoCorasickPlus () 27 | { 28 | m_automata = ac_automata_init (); 29 | m_acText = new AC_TEXT_t; 30 | } 31 | 32 | AhoCorasickPlus::~AhoCorasickPlus () 33 | { 34 | ac_automata_release (m_automata); 35 | delete m_acText; 36 | } 37 | 38 | AhoCorasickPlus::EnumReturnStatus AhoCorasickPlus::addPattern (const std::string &pattern, PatternId id) 39 | { 40 | EnumReturnStatus rv = RETURNSTATUS_FAILED; 41 | 42 | AC_PATTERN_t tmp_patt; 43 | tmp_patt.astring = (AC_ALPHABET_t*) pattern.c_str(); 44 | tmp_patt.length = pattern.size(); 45 | tmp_patt.rep.number = id; 46 | 47 | AC_STATUS_t status = ac_automata_add (m_automata, &tmp_patt); 48 | 49 | switch (status) 50 | { 51 | case ACERR_SUCCESS: rv = RETURNSTATUS_SUCCESS; break; 52 | case ACERR_DUPLICATE_PATTERN: rv = RETURNSTATUS_DUPLICATE_PATTERN; break; 53 | case ACERR_LONG_PATTERN: rv = RETURNSTATUS_LONG_PATTERN; break; 54 | case ACERR_ZERO_PATTERN: rv = RETURNSTATUS_ZERO_PATTERN; break; 55 | case ACERR_AUTOMATA_CLOSED: rv = RETURNSTATUS_AUTOMATA_CLOSED; break; 56 | } 57 | return rv; 58 | } 59 | 60 | AhoCorasickPlus::EnumReturnStatus AhoCorasickPlus::addPattern (const char pattern[], PatternId id) 61 | { 62 | std::string tmpString = pattern; 63 | return addPattern (tmpString, id); 64 | } 65 | 66 | void AhoCorasickPlus::finalize () 67 | { 68 | ac_automata_finalize (m_automata); 69 | } 70 | 71 | void AhoCorasickPlus::search (std::string& text, bool keep) 72 | { 73 | m_acText->astring = text.c_str(); 74 | m_acText->length = text.size(); 75 | ac_automata_settext (m_automata, m_acText, (int)keep); 76 | } 77 | 78 | bool AhoCorasickPlus::findNext (Match& match) 79 | { 80 | if (m_matchQueue.size()>0) 81 | { 82 | match = m_matchQueue.front(); 83 | m_matchQueue.pop(); 84 | return true; 85 | } 86 | 87 | AC_MATCH_t * matchp; 88 | 89 | if ((matchp = ac_automata_findnext (m_automata))) 90 | { 91 | Match singleMatch; 92 | singleMatch.position = matchp->position; 93 | 94 | for (unsigned int j=0; j < matchp->match_num; j++) 95 | { 96 | singleMatch.id = matchp->patterns[j].rep.number; 97 | singleMatch.pattern = matchp->patterns[j]; 98 | // we ignore tmp_patt.astring it may have been invalidated 99 | m_matchQueue.push(singleMatch); 100 | } 101 | } 102 | 103 | if (m_matchQueue.size()>0) 104 | { 105 | match = m_matchQueue.front(); 106 | m_matchQueue.pop(); 107 | return true; 108 | } 109 | 110 | return false; 111 | } 112 | -------------------------------------------------------------------------------- /include/nfqthread.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #ifndef __NFQTHREAD_H 21 | #define __NFQTHREAD_H 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #define T_DATA_SIZE 4096 28 | 29 | #define NFQ_BURST_FACTOR 4 30 | 31 | class PktAnalyzer; 32 | 33 | enum ADD_P_TYPES { A_TYPE_NONE, A_TYPE_ID, A_TYPE_URL }; 34 | 35 | struct nfqConfig 36 | { 37 | int queueNumber; 38 | int max_pending_packets; 39 | int mark_value; 40 | bool send_rst; 41 | bool save_exception_dump; 42 | bool block_undetected_ssl; 43 | bool lower_host; 44 | bool match_url_exactly; 45 | bool url_decode; 46 | int num_threads; 47 | enum ADD_P_TYPES add_p_type; 48 | }; 49 | 50 | struct threadStats 51 | { 52 | uint64_t marked_ssl; 53 | uint64_t redirected_domains; 54 | uint64_t redirected_urls; 55 | uint64_t marked_hosts; 56 | uint64_t sended_rst; 57 | uint64_t ip_packets; 58 | uint64_t total_bytes; 59 | uint64_t matched_ssl; 60 | uint64_t matched_ssl_ip; 61 | uint64_t matched_ip_port; 62 | }; 63 | 64 | class nfqThread: public Poco::Task 65 | { 66 | public: 67 | nfqThread(struct nfqConfig& cfg); 68 | virtual void runTask(); 69 | static int nfqueue_cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data); 70 | void getStats(threadStats &); 71 | 72 | void inc_marked_ssl() 73 | { 74 | Poco::Mutex::ScopedLock lock(_statsMutex); 75 | _stats.marked_ssl++; 76 | } 77 | 78 | void inc_redirected_domains() 79 | { 80 | Poco::Mutex::ScopedLock lock(_statsMutex); 81 | _stats.redirected_domains++; 82 | } 83 | 84 | void inc_redirected_urls() 85 | { 86 | Poco::Mutex::ScopedLock lock(_statsMutex); 87 | _stats.redirected_urls++; 88 | } 89 | 90 | void inc_marked_hosts() 91 | { 92 | Poco::Mutex::ScopedLock lock(_statsMutex); 93 | _stats.marked_hosts++; 94 | } 95 | 96 | void inc_sended_rst() 97 | { 98 | Poco::Mutex::ScopedLock lock(_statsMutex); 99 | _stats.sended_rst++; 100 | } 101 | 102 | void inc_ip_packets() 103 | { 104 | Poco::Mutex::ScopedLock lock(_statsMutex); 105 | _stats.ip_packets++; 106 | } 107 | 108 | void inc_total_bytes(uint32_t bytes) 109 | { 110 | Poco::Mutex::ScopedLock lock(_statsMutex); 111 | _stats.total_bytes += bytes; 112 | } 113 | 114 | void inc_total_bytes_packets(uint32_t bytes) 115 | { 116 | Poco::Mutex::ScopedLock lock(_statsMutex); 117 | _stats.total_bytes += bytes; 118 | _stats.ip_packets++; 119 | } 120 | 121 | void inc_matched_ssl() 122 | { 123 | Poco::Mutex::ScopedLock lock(_statsMutex); 124 | _stats.matched_ssl++; 125 | } 126 | 127 | void inc_matched_ssl_ip() 128 | { 129 | Poco::Mutex::ScopedLock lock(_statsMutex); 130 | _stats.matched_ssl_ip++; 131 | } 132 | 133 | void inc_matched_ip_port() 134 | { 135 | Poco::Mutex::ScopedLock lock(_statsMutex); 136 | _stats.matched_ip_port++; 137 | } 138 | private: 139 | Poco::Logger& _logger; 140 | struct nfq_q_handle *qh; 141 | int _queue_maxlen; 142 | struct nfqConfig _config; 143 | struct threadStats _stats; 144 | Poco::Mutex _statsMutex; 145 | 146 | Poco::NotificationQueue queue; 147 | std::vector _workThreads; 148 | }; 149 | 150 | #endif 151 | -------------------------------------------------------------------------------- /src/reloadtask.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "reloadtask.h" 3 | #include "main.h" 4 | #include "AhoCorasickPlus.h" 5 | 6 | 7 | Poco::Event ReloadTask::_event; 8 | 9 | 10 | ReloadTask::ReloadTask(nfqFilter *parent): 11 | Task("ReloadTask"), 12 | _parent(parent), 13 | _logger(Poco::Logger::get("ReloadTask")) 14 | { 15 | 16 | } 17 | 18 | 19 | ReloadTask::~ReloadTask() 20 | { 21 | } 22 | 23 | void ReloadTask::runTask() 24 | { 25 | _logger.debug("Starting reload task..."); 26 | while (!isCancelled()) 27 | { 28 | if(_event.tryWait(300)) 29 | { 30 | _logger.information("Reloading data from files..."); 31 | 32 | AhoCorasickPlus *atm_new = new AhoCorasickPlus(); 33 | DomainsMatchType *dm_new = new DomainsMatchType; 34 | AhoCorasickPlus *to_del_atm; 35 | DomainsMatchType *to_del_dm; 36 | try 37 | { 38 | _parent->loadDomains(_parent->getSSLFile(),atm_new,dm_new); 39 | atm_new->finalize(); 40 | { 41 | Poco::Mutex::ScopedLock lock(nfqFilter::_sslMutex); 42 | to_del_atm = nfqFilter::atm_ssl; 43 | to_del_dm = nfqFilter::_SSLdomainsMatchType; 44 | nfqFilter::atm_ssl = atm_new; 45 | nfqFilter::_SSLdomainsMatchType = dm_new; 46 | } 47 | delete to_del_atm; 48 | delete to_del_dm; 49 | _logger.information("Reloaded data for ssl hosts list"); 50 | } catch (Poco::Exception &excep) 51 | { 52 | _logger.error("Got exception while reload ssl data: %s", excep.displayText()); 53 | delete atm_new; 54 | delete dm_new; 55 | } 56 | 57 | 58 | atm_new = new AhoCorasickPlus(); 59 | dm_new = new DomainsMatchType; 60 | try 61 | { 62 | _parent->loadDomains(_parent->getDomainsFile(),atm_new,dm_new); 63 | atm_new->finalize(); 64 | { 65 | Poco::Mutex::ScopedLock lock(nfqFilter::_domainMapMutex); 66 | to_del_atm = nfqFilter::atm_domains; 67 | to_del_dm = nfqFilter::_domainsMatchType; 68 | nfqFilter::atm_domains = atm_new; 69 | nfqFilter::_domainsMatchType = dm_new; 70 | } 71 | delete to_del_atm; 72 | delete to_del_dm; 73 | _logger.information("Reloaded data for domains list"); 74 | } catch (Poco::Exception &excep) 75 | { 76 | _logger.error("Got exception while reload domains data: %s", excep.displayText()); 77 | delete atm_new; 78 | delete dm_new; 79 | } 80 | 81 | atm_new = new AhoCorasickPlus(); 82 | try 83 | { 84 | _parent->loadURLs(_parent->getURLsFile(),atm_new); 85 | atm_new->finalize(); 86 | { 87 | Poco::Mutex::ScopedLock lock(nfqFilter::_urlMapMutex); 88 | to_del_atm = nfqFilter::atm; 89 | nfqFilter::atm = atm_new; 90 | } 91 | delete to_del_atm; 92 | _logger.information("Reloaded data for urls list"); 93 | } catch (Poco::Exception &excep) 94 | { 95 | _logger.error("Got exception while reload urls data: %s", excep.displayText()); 96 | delete atm_new; 97 | } 98 | 99 | IPPortMap *ip_port_map = new IPPortMap; 100 | try 101 | { 102 | IPPortMap *old; 103 | _parent->loadHosts(_parent->getHostsFile(),ip_port_map); 104 | { 105 | Poco::ScopedWriteRWLock lock(nfqFilter::_ipportMapMutex); 106 | old = nfqFilter::_ipportMap; 107 | nfqFilter::_ipportMap = ip_port_map; 108 | } 109 | delete old; 110 | _logger.information("Reloaded data for ip port list"); 111 | } catch (Poco::Exception &excep) 112 | { 113 | _logger.error("Got exception while reload ip port data: %s", excep.displayText()); 114 | delete ip_port_map; 115 | } 116 | 117 | Patricia *ssl_ips = new Patricia; 118 | try 119 | { 120 | _parent->loadSSLIP(_parent->getSSLIpsFile(),ssl_ips); 121 | Patricia *ssl_ips_old; 122 | { 123 | Poco::ScopedWriteRWLock lock(nfqFilter::_sslIpsSetMutex); 124 | ssl_ips_old = nfqFilter::_sslIps; 125 | nfqFilter::_sslIps = ssl_ips; 126 | } 127 | delete ssl_ips_old; 128 | _logger.information("Reloaded data for ssl ip list"); 129 | } catch (Poco::Exception &excep) 130 | { 131 | _logger.error("Got exception while reload ip ssl data: %s", excep.displayText()); 132 | delete ssl_ips; 133 | } 134 | } 135 | } 136 | _logger.debug("Stopping reload task..."); 137 | } 138 | -------------------------------------------------------------------------------- /src/patr.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #include "patricia.h" 21 | #include "patr.h" 22 | #include 23 | #include 24 | 25 | 26 | Patricia::Patricia() 27 | { 28 | tree_ipv4 = New_Patricia(32); 29 | tree_ipv6 = New_Patricia(128); 30 | } 31 | 32 | Patricia::~Patricia() 33 | { 34 | Destroy_Patricia(tree_ipv4, nullptr); 35 | Destroy_Patricia(tree_ipv6, nullptr); 36 | } 37 | 38 | 39 | patricia_node_t *Patricia::make_and_lookup(std::string &description) 40 | { 41 | prefix_t prefix; 42 | patricia_node_t *node; 43 | Poco::Net::IPAddress mask; 44 | Poco::Net::IPAddress address; 45 | if(description.empty()) 46 | return nullptr; 47 | std::size_t slash=description.find('/'); 48 | if(slash != std::string::npos) 49 | { 50 | std::string addr=description.substr(0,slash); 51 | std::string msk_t=description.substr(slash+1,description.size()); 52 | if(Poco::Net::IPAddress::tryParse(addr,address)) 53 | { 54 | int msk = std::stoi(msk_t,nullptr); 55 | if(address.family() == Poco::Net::IPAddress::IPv4) 56 | { 57 | if(msk > 32) 58 | { 59 | return nullptr; 60 | } 61 | Poco::Net::IPAddress msk1(msk, Poco::Net::IPAddress::IPv4); 62 | mask=msk1; 63 | } else { 64 | if(msk > 128) 65 | { 66 | return nullptr; 67 | } 68 | mask=Poco::Net::IPAddress(msk,Poco::Net::IPAddress::IPv6); 69 | } 70 | } else 71 | return nullptr; 72 | } else { 73 | if(Poco::Net::IPAddress::tryParse(description,address)) 74 | { 75 | if(address.family() == Poco::Net::IPAddress::IPv4) 76 | mask=Poco::Net::IPAddress(32,Poco::Net::IPAddress::IPv4); 77 | else 78 | mask=Poco::Net::IPAddress(128,Poco::Net::IPAddress::IPv6); 79 | } else 80 | return nullptr; 81 | } 82 | if(!fill_prefix(address.family() == Poco::Net::IPAddress::IPv4 ? AF_INET : AF_INET6,(void *)address.addr(),mask.prefixLength(),prefix)) 83 | return nullptr; 84 | node = patricia_lookup (address.family() == Poco::Net::IPAddress::IPv4 ? tree_ipv4 : tree_ipv6, &prefix); 85 | return (node); 86 | } 87 | 88 | patricia_node_t *Patricia::try_search_exact_ip(Poco::Net::IPAddress &address) 89 | { 90 | prefix_t prefix; 91 | patricia_node_t *node; 92 | if(!fill_prefix(address.family() == Poco::Net::IPAddress::IPv4 ? AF_INET : AF_INET6,(void *)address.addr(),address.family() == Poco::Net::IPAddress::IPv4 ? 32 : 128, prefix)) 93 | return nullptr; 94 | node=patricia_search_exact (address.family() == Poco::Net::IPAddress::IPv4 ? tree_ipv4 : tree_ipv6, &prefix); 95 | return (node); 96 | } 97 | 98 | bool Patricia::fill_prefix(int family, void *dest, int bitlen, prefix_t &prefix) 99 | { 100 | int default_bitlen = sizeof(struct in_addr) * 8; 101 | if(family == AF_INET6) 102 | { 103 | default_bitlen = sizeof(struct in6_addr) * 8; 104 | memcpy (&prefix.add.sin6, dest, sizeof(struct in6_addr)); 105 | } else if (family == AF_INET) 106 | { 107 | memcpy (&prefix.add.sin, dest, sizeof(struct in_addr)); 108 | } else { 109 | return false; 110 | } 111 | prefix.bitlen = (bitlen >= 0)? bitlen: default_bitlen; 112 | prefix.family = family; 113 | prefix.ref_count = 0; 114 | return true; 115 | } 116 | 117 | void Patricia::print_all_nodes() 118 | { 119 | patricia_node_t *node; 120 | std::cout << "IPv4 nodes:" << std::endl; 121 | PATRICIA_WALK(tree_ipv4->head, node) { 122 | std::cout << "node: " << prefix_toa(node->prefix) << "/" << node->prefix->bitlen << std::endl; 123 | } PATRICIA_WALK_END; 124 | std::cout << "IPv6 nodes:" << std::endl; 125 | PATRICIA_WALK(tree_ipv6->head, node) { 126 | std::cout << "node: " << prefix_toa(node->prefix) << "/" << node->prefix->bitlen << std::endl; 127 | } PATRICIA_WALK_END; 128 | } 129 | -------------------------------------------------------------------------------- /include/main.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #ifndef __MAIN_H 21 | #define __MAIN_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include "sender.h" 38 | #include "nfqthread.h" 39 | #include "patr.h" 40 | 41 | /* Max packets processed simultaniously per thread. */ 42 | #define DEFAULT_MAX_PENDING_PACKETS 1024 43 | 44 | // каким значением маркировать пакет, чтобы потом зарезать его файерволом 45 | #define MARK_VALUE 17 46 | 47 | typedef Poco::HashMap DomainsMatchType; 48 | 49 | typedef Poco::HashMap UrlsMap; 50 | 51 | typedef std::map> IPPortMap; 52 | 53 | class AhoCorasickPlus; 54 | 55 | class nfqFilter: public Poco::Util::ServerApplication 56 | { 57 | 58 | public: 59 | nfqFilter(); 60 | ~nfqFilter(); 61 | 62 | static Poco::Mutex _domainMapMutex; 63 | static DomainsMatchType *_domainsMatchType; 64 | 65 | static DomainsMatchType *_SSLdomainsMatchType; 66 | 67 | static Poco::RWLock _ipportMapMutex; 68 | static IPPortMap *_ipportMap; 69 | 70 | static Poco::RWLock _sslIpsSetMutex; 71 | static Patricia *_sslIps; 72 | 73 | static Poco::Mutex _urlMapMutex; 74 | 75 | static struct ndpi_detection_module_struct* my_ndpi_struct; 76 | static u_int32_t ndpi_size_flow_struct; 77 | static u_int32_t ndpi_size_id_struct; 78 | static AhoCorasickPlus *atm; 79 | 80 | static u_int32_t current_ndpi_memory; 81 | static u_int32_t max_ndpi_memory; 82 | 83 | static Poco::Mutex _sslMutex; 84 | static AhoCorasickPlus *atm_ssl; 85 | static AhoCorasickPlus *atm_domains; 86 | 87 | std::string &getSSLFile() 88 | { 89 | return _sslFile; 90 | } 91 | 92 | std::string &getDomainsFile() 93 | { 94 | return _domainsFile; 95 | } 96 | 97 | std::string &getURLsFile() 98 | { 99 | return _urlsFile; 100 | } 101 | 102 | std::string &getHostsFile() 103 | { 104 | return _hostsFile; 105 | } 106 | 107 | std::string &getSSLIpsFile() 108 | { 109 | return _sslIpsFile; 110 | } 111 | 112 | void loadDomains(std::string &fn, AhoCorasickPlus *_dm_atm,DomainsMatchType *_dm_map); 113 | void loadURLs(std::string &fn, AhoCorasickPlus *dm_atm); 114 | void loadHosts(std::string &fn,IPPortMap *ippm); 115 | void loadSSLIP(const std::string &fn, Patricia *patricia); 116 | protected: 117 | class ErrorHandler: public Poco::ErrorHandler 118 | { 119 | public: 120 | ErrorHandler(nfqFilter& app): 121 | _app(app) 122 | { 123 | } 124 | void exception(const Poco::Exception& exc) 125 | { 126 | log(exc.displayText()); 127 | } 128 | void exception(const std::exception& exc) 129 | { 130 | log(exc.what()); 131 | } 132 | void exception() 133 | { 134 | log("unknown exception"); 135 | } 136 | void log(const std::string& message) 137 | { 138 | _app.logger().error("A thread was terminated by an unhandled exception: " + message); 139 | } 140 | private: 141 | nfqFilter& _app; 142 | }; 143 | 144 | void initialize(Application& self); 145 | void uninitialize(); 146 | void defineOptions(Poco::Util::OptionSet& options); 147 | void handleOption(const std::string& name,const std::string& value); 148 | void handleHelp(const std::string& name,const std::string& value); 149 | void displayHelp(); 150 | int main(const ArgVec& args); 151 | 152 | private: 153 | bool _helpRequested; 154 | std::string _configFile; 155 | std::string _domainsFile; 156 | std::string _urlsFile; 157 | std::string _protocolsFile; 158 | std::string _sslFile; 159 | std::string _hostsFile; 160 | std::string _sslIpsFile; 161 | int _statistic_interval; 162 | struct nfqConfig _config; 163 | 164 | int _cmd_queueNum; 165 | int _cmd_threadsNum; 166 | 167 | ErrorHandler _errorHandler; 168 | struct CSender::params _sender_params; 169 | }; 170 | 171 | #endif 172 | -------------------------------------------------------------------------------- /src/nfqstatistictask.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #define __STDC_FORMAT_MACROS 21 | #include 22 | #include "main.h" 23 | #include "nfqstatistictask.h" 24 | #include "nfqthread.h" 25 | 26 | static struct timeval begin_time; 27 | 28 | NFQStatisticTask::NFQStatisticTask(int sec): 29 | Task("NFQStatisticTask"), 30 | _sec(sec) 31 | { 32 | } 33 | 34 | static std::string formatBytes(u_int32_t howMuch) 35 | { 36 | char unit = 'B'; 37 | char buf[32]; 38 | int buf_len=sizeof(buf); 39 | 40 | if(howMuch < 1024) 41 | { 42 | snprintf(buf, buf_len, "%lu %c", (unsigned long)howMuch, unit); 43 | } else if(howMuch < 1048576) 44 | { 45 | snprintf(buf, buf_len, "%.2f K%c", (float)(howMuch)/1024, unit); 46 | } else { 47 | float tmpGB = ((float)howMuch)/1048576; 48 | if(tmpGB < 1024) 49 | { 50 | snprintf(buf, buf_len, "%.2f M%c", tmpGB, unit); 51 | } else { 52 | tmpGB /= 1024; 53 | snprintf(buf, buf_len, "%.2f G%c", tmpGB, unit); 54 | } 55 | } 56 | return std::string(buf); 57 | } 58 | 59 | static std::string formatPackets(float numPkts) 60 | { 61 | char buf[32]; 62 | int buf_len=sizeof(buf); 63 | if(numPkts < 1000) 64 | { 65 | snprintf(buf, buf_len, "%.2f", numPkts); 66 | } else if(numPkts < 1000000) 67 | { 68 | snprintf(buf, buf_len, "%.2f K", numPkts/1000); 69 | } else { 70 | numPkts /= 1000000; 71 | snprintf(buf, buf_len, "%.2f M", numPkts); 72 | } 73 | return std::string(buf); 74 | } 75 | 76 | void NFQStatisticTask::OutStatistic() 77 | { 78 | Poco::Util::Application& app = Poco::Util::Application::instance(); 79 | app.logger().information("nDPI memory (once): %s",formatBytes(sizeof(ndpi_detection_module_struct))); 80 | app.logger().information("nDPI memory per flow: %s",formatBytes(nfqFilter::ndpi_size_flow_struct)); 81 | app.logger().information("nDPI current memory usage: %s",formatBytes(nfqFilter::current_ndpi_memory)); 82 | app.logger().information("nDPI maximum memory usage: %s",formatBytes(nfqFilter::max_ndpi_memory)); 83 | 84 | Poco::TaskManager *pOwner=getOwner(); 85 | if(pOwner) 86 | { 87 | Poco::TaskManager::TaskList tl=pOwner->taskList(); 88 | for(Poco::TaskManager::TaskList::iterator it=tl.begin(); it != tl.end(); it++) 89 | { 90 | std::string threadName=(*it)->name(); 91 | std::size_t found=threadName.find("nfqThread"); 92 | if(found != std::string::npos) 93 | { 94 | // статистика задачи... 95 | struct threadStats stats; 96 | Poco::AutoPtr p=it->cast(); 97 | p->getStats(stats); 98 | unsigned int avg_pkt_size=0; 99 | struct timeval end; 100 | gettimeofday(&end, NULL); 101 | uint64_t tot_usec = end.tv_sec*1000000 + end.tv_usec - (begin_time.tv_sec*1000000 + begin_time.tv_usec); 102 | float t = (float)(stats.ip_packets*1000000)/(float)tot_usec; 103 | if(stats.ip_packets && stats.total_bytes) 104 | avg_pkt_size = (unsigned int)(stats.total_bytes/stats.ip_packets); 105 | 106 | app.logger().information("Total seen packets: %" PRIu64 ", Total seen bytes: %" PRIu64 ", Average packet size: %" PRIu32 " bytes, Traffic throughput: %s pps", stats.ip_packets, stats.total_bytes, avg_pkt_size, formatPackets(t)); 107 | app.logger().information("Total matched by ip/port: %" PRIu64 ", Total matched by ssl: %" PRIu64 ", Total matched by ssl/ip: %" PRIu64, stats.matched_ip_port, stats.matched_ssl, stats.matched_ssl_ip); 108 | app.logger().information("Total redirected domains %" PRIu64 ", Total redirected urls: %" PRIu64 ", Total marked ssl: %" PRIu64 ", Total marked hosts: %" PRIu64 ", Total rst sended: %" PRIu64, stats.redirected_domains,stats.redirected_urls,stats.marked_ssl,stats.marked_hosts,stats.sended_rst); 109 | } 110 | app.logger().debug("State of task %s is %d", (*it)->name(), (int)(*it)->state()); 111 | } 112 | } 113 | } 114 | 115 | void NFQStatisticTask::runTask() 116 | { 117 | Poco::Util::Application& app = Poco::Util::Application::instance(); 118 | app.logger().debug("Starting statistic task..."); 119 | gettimeofday(&begin_time, NULL); 120 | int sleep_sec=_sec; 121 | if(!sleep_sec) 122 | sleep_sec=1; 123 | sleep_sec *= 1000; 124 | while (!isCancelled()) 125 | { 126 | sleep(sleep_sec); 127 | if(_sec) 128 | OutStatistic(); 129 | } 130 | app.logger().debug("Stopping statistic task..."); 131 | } 132 | 133 | -------------------------------------------------------------------------------- /include/patricia.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: patricia.h,v 1.6 2005/12/07 20:53:01 dplonka Exp $ 3 | * Dave Plonka 4 | * 5 | * This product includes software developed by the University of Michigan, 6 | * Merit Network, Inc., and their contributors. 7 | * 8 | * This file had been called "radix.h" in the MRT sources. 9 | * 10 | * I renamed it to "patricia.h" since it's not an implementation of a general 11 | * radix trie. Also, pulled in various requirements from "mrt.h" and added 12 | * some other things it could be used as a standalone API. 13 | */ 14 | 15 | #ifndef _PATRICIA_H 16 | #define _PATRICIA_H 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | #define HAVE_IPV6 23 | 24 | /* typedef unsigned int u_int; */ 25 | typedef void (*void_fn_t)(); 26 | /* { from defs.h */ 27 | #define prefix_touchar(prefix) ((u_char *)&(prefix)->add.sin) 28 | #define MAXLINE 1024 29 | #define BIT_TEST(f, b) ((f) & (b)) 30 | /* } */ 31 | 32 | #define addroute make_and_lookup 33 | 34 | #include /* for u_* definitions (on FreeBSD 5) */ 35 | 36 | #include /* for EAFNOSUPPORT */ 37 | #ifndef EAFNOSUPPORT 38 | # defined EAFNOSUPPORT WSAEAFNOSUPPORT 39 | # include 40 | #else 41 | # include /* for struct in_addr */ 42 | #endif 43 | 44 | #include /* for AF_INET */ 45 | 46 | /* { from mrt.h */ 47 | 48 | typedef struct _prefix4_t { 49 | u_short family; /* AF_INET | AF_INET6 */ 50 | u_short bitlen; /* same as mask? */ 51 | int ref_count; /* reference count */ 52 | struct in_addr sin; 53 | } prefix4_t; 54 | 55 | typedef struct _prefix_t { 56 | u_short family; /* AF_INET | AF_INET6 */ 57 | u_short bitlen; /* same as mask? */ 58 | int ref_count; /* reference count */ 59 | union { 60 | struct in_addr sin; 61 | #ifdef HAVE_IPV6 62 | struct in6_addr sin6; 63 | #endif /* IPV6 */ 64 | } add; 65 | } prefix_t; 66 | 67 | /* } */ 68 | 69 | typedef struct _patricia_node_t { 70 | u_int bit; /* flag if this node used */ 71 | prefix_t *prefix; /* who we are in patricia tree */ 72 | struct _patricia_node_t *l, *r; /* left and right children */ 73 | struct _patricia_node_t *parent;/* may be used */ 74 | void *data; /* pointer to data */ 75 | void *user1; /* pointer to usr data (ex. route flap info) */ 76 | } patricia_node_t; 77 | 78 | typedef struct _patricia_tree_t { 79 | patricia_node_t *head; 80 | u_int maxbits; /* for IP, 32 bit addresses */ 81 | int num_active_node; /* for debug purpose */ 82 | } patricia_tree_t; 83 | 84 | 85 | patricia_node_t *patricia_search_exact (patricia_tree_t *patricia, prefix_t *prefix); 86 | patricia_node_t *patricia_search_best (patricia_tree_t *patricia, prefix_t *prefix); 87 | patricia_node_t * patricia_search_best2 (patricia_tree_t *patricia, prefix_t *prefix, 88 | int inclusive); 89 | patricia_node_t *patricia_lookup (patricia_tree_t *patricia, prefix_t *prefix); 90 | void patricia_remove (patricia_tree_t *patricia, patricia_node_t *node); 91 | patricia_tree_t *New_Patricia (int maxbits); 92 | void Clear_Patricia (patricia_tree_t *patricia, void_fn_t func); 93 | void Destroy_Patricia (patricia_tree_t *patricia, void_fn_t func); 94 | 95 | void patricia_process (patricia_tree_t *patricia, void_fn_t func); 96 | 97 | char *prefix_toa (prefix_t * prefix); 98 | 99 | prefix_t *New_Prefix (int family, void *dest, int bitlen); 100 | void Deref_Prefix (prefix_t * prefix); 101 | size_t patricia_walk_inorder(patricia_node_t *node, void_fn_t func); 102 | 103 | 104 | #define PATRICIA_MAXBITS (sizeof(struct in6_addr) * 8) 105 | #define PATRICIA_NBIT(x) (0x80 >> ((x) & 0x7f)) 106 | #define PATRICIA_NBYTE(x) ((x) >> 3) 107 | 108 | #define PATRICIA_DATA_GET(node, type) (type *)((node)->data) 109 | #define PATRICIA_DATA_SET(node, value) ((node)->data = (void *)(value)) 110 | 111 | #define PATRICIA_WALK(Xhead, Xnode) \ 112 | do { \ 113 | patricia_node_t *Xstack[PATRICIA_MAXBITS+1]; \ 114 | patricia_node_t **Xsp = Xstack; \ 115 | patricia_node_t *Xrn = (Xhead); \ 116 | while ((Xnode = Xrn)) { \ 117 | if (Xnode->prefix) 118 | 119 | #define PATRICIA_WALK_ALL(Xhead, Xnode) \ 120 | do { \ 121 | patricia_node_t *Xstack[PATRICIA_MAXBITS+1]; \ 122 | patricia_node_t **Xsp = Xstack; \ 123 | patricia_node_t *Xrn = (Xhead); \ 124 | while ((Xnode = Xrn)) { \ 125 | if (1) 126 | 127 | #define PATRICIA_WALK_BREAK { \ 128 | if (Xsp != Xstack) { \ 129 | Xrn = *(--Xsp); \ 130 | } else { \ 131 | Xrn = (patricia_node_t *) 0; \ 132 | } \ 133 | continue; } 134 | 135 | #define PATRICIA_WALK_END \ 136 | if (Xrn->l) { \ 137 | if (Xrn->r) { \ 138 | *Xsp++ = Xrn->r; \ 139 | } \ 140 | Xrn = Xrn->l; \ 141 | } else if (Xrn->r) { \ 142 | Xrn = Xrn->r; \ 143 | } else if (Xsp != Xstack) { \ 144 | Xrn = *(--Xsp); \ 145 | } else { \ 146 | Xrn = (patricia_node_t *) 0; \ 147 | } \ 148 | } \ 149 | } while (0) 150 | 151 | #ifdef __cplusplus 152 | } 153 | #endif 154 | 155 | #endif /* _PATRICIA_H */ 156 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ([2.69]) 5 | AC_INIT(nfqfilter, 0.3, max1976@mail.ru) 6 | 7 | NDPI_HOME=./nDPI 8 | NDPI_GIT_VERSION=1.7-stable 9 | 10 | AM_INIT_AUTOMAKE() 11 | 12 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(no)]) 13 | 14 | AC_CONFIG_SRCDIR([src/main.cpp]) 15 | AC_CONFIG_HEADERS([include/config.h]) 16 | 17 | AC_LANG([C++]) 18 | AC_LANG_PUSH([C++]) 19 | 20 | # store current user given compiler flags to avoid default setup via AC_PROG_CXX 21 | OLD_CXXFLAGS=$CXXFLAGS 22 | OLD_CFLAGS=$CFLAGS 23 | 24 | # Checks for programs. 25 | AC_PROG_CXX 26 | AC_PROG_CC 27 | 28 | CXXFLAGS=$OLD_CXXFLAGS 29 | CFLAGS=$OLD_CFLAGS 30 | 31 | CFLAGS="$CFLAGS --pedantic -Wall -O2" 32 | 33 | AC_ARG_ENABLE(debug, 34 | AS_HELP_STRING( 35 | [--enable-debug], 36 | [enable debugging, default: no]), 37 | [case "${enableval}" in 38 | yes) debug=true ;; 39 | no) debug=false ;; 40 | *) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;; 41 | esac], 42 | [debug=false]) 43 | 44 | AC_MSG_CHECKING([for debug enabled]) 45 | 46 | 47 | if test x"$debug" = x"true"; then 48 | CXXFLAGS="$CXXFLAGS -std=c++0x -O0 -g -Wall -pthread" 49 | else 50 | CXXFLAGS="$CXXFLAGS -std=c++0x -O2 -pthread" 51 | fi 52 | 53 | AC_COMPILE_IFELSE([AC_LANG_SOURCE( 54 | [[template 55 | struct check 56 | { 57 | static_assert(sizeof(int) <= sizeof(T), "not big enough"); 58 | }; 59 | 60 | typedef check> right_angle_brackets; 61 | 62 | int a; 63 | decltype(a) b; 64 | 65 | typedef check check_type; 66 | check_type c; 67 | check_type&& cr = static_cast(c);]])],, 68 | AC_MSG_FAILURE(['$CXX $CXXFLAGS' does not accept ISO C++11])) 69 | 70 | 71 | 72 | 73 | # Checks for libraries. 74 | 75 | # Checks for header files. 76 | AC_CHECK_HEADERS([netinet/in.h stdint.h]) 77 | 78 | # Checks for typedefs, structures, and compiler characteristics. 79 | AC_CHECK_HEADER_STDBOOL 80 | 81 | # Checks for library functions. 82 | AC_FUNC_ERROR_AT_LINE 83 | AC_CHECK_FUNCS([strerror]) 84 | 85 | # Check for methods in library and check for header files 86 | AC_CHECK_HEADERS([Poco/Foundation.h Poco/Net/HTTPCookie.h Poco/Util/Timer.h], 87 | [], 88 | AC_MSG_ERROR([Poco include files not found.]) 89 | ) 90 | 91 | AC_CHECK_LIB([PocoFoundation],[main],[HAVE_POCOFOUNDATION=1],AC_MSG_ERROR([PocoFoundation library not found.])) 92 | if test "$HAVE_POCOFOUNDATION"; then 93 | save_libs="${LIBS}" 94 | LIBS="-lPocoFoundation" 95 | AC_LINK_IFELSE( 96 | [AC_LANG_PROGRAM( 97 | [#include "Poco/UnicodeConverter.h"], 98 | [std::wstring wstr; Poco::UnicodeConverter::toUTF16("hello", wstr);] 99 | )], 100 | [LIBS="$LIBS $save_libs"], 101 | [AC_MSG_ERROR([linking with PocoFoundation failed.])] 102 | ) 103 | fi 104 | 105 | AC_CHECK_LIB([PocoUtil],[main],[HAVE_POCOUTIL=1],AC_MSG_ERROR([PocoUtil library not found.])) 106 | if test "$HAVE_POCOUTIL"; then 107 | save_libs="${LIBS}" 108 | LIBS="-lPocoUtil" 109 | AC_LINK_IFELSE( 110 | [AC_LANG_PROGRAM( 111 | [#include "Poco/Util/Option.h"], 112 | [Poco::Util::Option();] 113 | )], 114 | [LIBS="$LIBS $save_libs"], 115 | [AC_MSG_ERROR([linking with PocoUtil failed.])] 116 | ) 117 | fi 118 | 119 | AC_CHECK_LIB([PocoNet],[main],[HAVE_POCONET=1],AC_MSG_ERROR([PocoNet library not found.])) 120 | if test "$HAVE_POCONET"; then 121 | save_libs="${LIBS}" 122 | LIBS="-lPocoNet" 123 | AC_LINK_IFELSE( 124 | [AC_LANG_PROGRAM( 125 | [#include "Poco/Net/HTTPClientSession.h"], 126 | [Poco::Net::HTTPClientSession();] 127 | )], 128 | [LIBS="$LIBS $save_libs"], 129 | [AC_MSG_ERROR([linking with PocoNET failed.])] 130 | ) 131 | fi 132 | 133 | AC_CHECK_LIB([netfilter_queue], [nfq_open],,AC_MSG_ERROR([Netfilter_queue library not found!])) 134 | AC_CHECK_LIB([nfnetlink], [nfnl_fd],,AC_MSG_ERROR([Nfnetlink library not found!])) 135 | 136 | dnl nDPI checks... 137 | AC_MSG_CHECKING(for nDPI $NDPI_HOME) 138 | if test -d "$NDPI_HOME" ; then : 139 | AC_MSG_RESULT(found in $NDPI_HOME) 140 | else 141 | AC_MSG_RESULT(not found) 142 | AC_MSG_NOTICE(Getting nDPI from git) 143 | git clone -b $NDPI_GIT_VERSION https://github.com/ntop/nDPI.git $NDPI_HOME; cd $NDPI_HOME; patch -p1 < ../nDPI-1.7-bugfixes.patch ; cd - 144 | AC_MSG_CHECKING(for nDPI $NDPI_HOME) 145 | if test -d "$NDPI_HOME" ; then : 146 | AC_MSG_RESULT(found in $NDPI_HOME) 147 | else 148 | AC_MSG_ERROR(Install nDPI 1.7-stable: git clone -b $NDPI_GIT_VERSION https://github.com/ntop/nDPI.git $NDPI_HOME; cd $NDPI_HOME; patch -p1 < ../nDPI-1.7-bugfixes.patch ; ./autogen.sh; make; cd - ) 149 | fi 150 | fi 151 | 152 | NDPI_LIB=$NDPI_HOME/src/lib/.libs/libndpi.a 153 | AC_MSG_CHECKING(for $NDPI_LIB) 154 | if test -f "$NDPI_LIB" ; then : 155 | 156 | else 157 | AC_MSG_RESULT([not found, compiling...]) 158 | cd $NDPI_HOME; ./autogen.sh; make; cd - 159 | fi 160 | AC_MSG_RESULT(yes) 161 | 162 | save_flags="${CXXFLAGS}" 163 | CXXFLAGS="${CXXFLAGS} -I${NDPI_HOME}/src/include" 164 | AC_MSG_CHECKING(for compiling with nDPI) 165 | save_libs="${LIBS}" 166 | LIBS="$NDPI_LIB" 167 | AC_LINK_IFELSE( 168 | [AC_LANG_PROGRAM( 169 | [#include "ndpi_api.h"], 170 | [ndpi_revision();] 171 | )], 172 | [LIBS="$save_libs"], 173 | [AC_MSG_ERROR([Linking with nDPI failed!])] 174 | ) 175 | AC_MSG_RESULT(yes) 176 | CXXFLAGS="$save_flags" 177 | 178 | AC_OUTPUT(Makefile src/Makefile include/Makefile) 179 | -------------------------------------------------------------------------------- /include/actypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * actypes.h: Includes basic data types of ahocorasick library 3 | * This file is part of multifast. 4 | * 5 | Copyright 2010-2013 Kamiar Kanani 6 | 7 | multifast is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | multifast is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with multifast. If not, see . 19 | */ 20 | 21 | #ifndef _AC_TYPES_H_ 22 | #define _AC_TYPES_H_ 23 | 24 | #ifdef __cplusplus 25 | /*extern "C" {*/ 26 | #endif 27 | 28 | /* AC_ALPHABET_t: 29 | * defines the alphabet type. 30 | * Actually defining AC_ALPHABET_t as a char work as well, but sometimes we deal 31 | * with streams of other basic types e.g. integers or enumerators. 32 | * Although they consists of string of bytes (chars), but using their specific 33 | * types as AC_ALPHABET_t will lead to a better performance. so instead of 34 | * working with strings of chars, we assume that we are working with strings of 35 | * AC_ALPHABET_t and leave it optional for other users to define their 36 | * own alphabets. 37 | **/ 38 | typedef char AC_ALPHABET_t; 39 | 40 | /* AC_REP_t: 41 | * Provides a more readable representative for a pattern. 42 | * because patterns themselves are not always suitable for displaying 43 | * (e.g. hex patterns), we offer this type to improve intelligibility 44 | * of output. Sometimes it can be also useful, when you are 45 | * retrieving patterns from a database, to maintain their identifiers in the 46 | * automata for further reference. we provisioned two possible types as a 47 | * union. you can add your desired type in it. 48 | **/ 49 | typedef union AC_REP 50 | { 51 | const char * stringy; /* null-terminated string */ 52 | unsigned long number; 53 | } AC_REP_t; 54 | 55 | /* AC_PATTERN_t: 56 | * This is the pattern type that must be fed into AC automata. 57 | * the 'astring' field is not null-terminated, because it can contain zero 58 | * value bytes. the 'length' field determines the number of AC_ALPHABET_t it 59 | * carries. the 'rep' field is described in AC_REP_t. despite 60 | * 'astring', 'rep' can have duplicate values for different given 61 | * AC_PATTERN_t. it is an optional field and you can just fill it with 0. 62 | * CAUTION: 63 | * Not always the 'astring' points to the correct position in memory. 64 | * it is the responsibility of your program to maintain a permanent allocation 65 | * for astring field. 66 | **/ 67 | typedef struct AC_PATTERN 68 | { 69 | const AC_ALPHABET_t * astring; /* String of alphabets */ 70 | unsigned int length; /* Length of pattern */ 71 | AC_REP_t rep; /* Representative string (optional) */ 72 | } AC_PATTERN_t; 73 | 74 | /* AC_TEXT_t: 75 | * The input text type that is fed to ac_automata_search() to be searched. 76 | * it is similar to AC_PATTERN_t. actually we could use AC_PATTERN_t as input 77 | * text, but for the purpose of being more readable, we defined this new type. 78 | **/ 79 | typedef struct AC_TEXT 80 | { 81 | const AC_ALPHABET_t * astring; /* String of alphabets */ 82 | unsigned int length; /* Length of string */ 83 | } AC_TEXT_t; 84 | 85 | /* AC_MATCH_t: 86 | * Provides the structure for reporting a match in the text. 87 | * a match occurs when the automata reaches a final node. any final 88 | * node can match one or more pattern at a position in a text. the 89 | * 'patterns' field holds these matched patterns. obviously these 90 | * matched patterns have same end-position in the text. there is a relationship 91 | * between matched patterns: the shorter one is a factor (tail) of the longer 92 | * one. the 'position' maintains the end position of matched patterns. the 93 | * start position of patterns could be found by knowing their 'length' in 94 | * AC_PATTERN_t. e.g. suppose "recent" and "cent" are matched at 95 | * position 40 in the text, then the start position of them are 34 and 36 96 | * respectively. finally the field 'match_num' maintains the number of 97 | * matched patterns. 98 | **/ 99 | typedef struct AC_MATCH 100 | { 101 | AC_PATTERN_t * patterns; /* Array of matched pattern */ 102 | long position; /* The end position of matching pattern(s) in the text */ 103 | unsigned int match_num; /* Number of matched patterns */ 104 | } AC_MATCH_t; 105 | 106 | /* AC_STATUS_t: 107 | * Return status of an AC function 108 | **/ 109 | typedef enum AC_STATUS 110 | { 111 | ACERR_SUCCESS = 0, /* No error occurred */ 112 | ACERR_DUPLICATE_PATTERN, /* Duplicate patterns */ 113 | ACERR_LONG_PATTERN, /* Pattern length is longer than AC_PATTRN_MAX_LENGTH */ 114 | ACERR_ZERO_PATTERN, /* Empty pattern (zero length) */ 115 | ACERR_AUTOMATA_CLOSED /* Automata is closed. after calling 116 | * ac_automata_finalize() you can not add new 117 | * patterns to the automata. */ 118 | } AC_STATUS_t; 119 | 120 | /* AC_MATCH_CALBACK_t: 121 | * This is the call-back function to report match back to the caller. 122 | * when a match is find, the automata will reach you using this function and sends 123 | * you a pointer to AC_MATCH_t. using that pointer you can handle 124 | * matches. you can send parameters to the call-back function when you call 125 | * ac_automata_search(). at call-back, the automata will sent you those 126 | * parameters as the second parameter (void *) of AC_MATCH_CALBACK_t. inside 127 | * the call-back function you can cast it to whatever you want. 128 | * If you return 0 from AC_MATCH_CALBACK_t function to the automata, it will 129 | * continue searching, otherwise it will return from ac_automata_search() 130 | * to your calling function. 131 | **/ 132 | typedef int (*AC_MATCH_CALBACK_f)(AC_MATCH_t *, void *); 133 | 134 | /* AC_PATTRN_MAX_LENGTH: 135 | * Maximum acceptable pattern length in AC_PATTERN_t.length 136 | **/ 137 | #define AC_PATTRN_MAX_LENGTH 1024 138 | 139 | #ifdef __cplusplus 140 | /*}*/ 141 | #endif 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /src/nfqthread.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "nfqthread.h" 27 | #include "main.h" 28 | 29 | #include "pktanalyzer.h" 30 | 31 | nfqThread::nfqThread(struct nfqConfig& cfg): 32 | Task("nfqThread"), 33 | _logger(Poco::Logger::get("nfqThread")), 34 | _queue_maxlen(cfg.max_pending_packets*NFQ_BURST_FACTOR), 35 | _config(cfg) 36 | { 37 | _stats={0}; 38 | } 39 | 40 | void nfqThread::getStats(threadStats &st) 41 | { 42 | Poco::Mutex::ScopedLock lock(_statsMutex); 43 | st=_stats; 44 | } 45 | 46 | void nfqThread::runTask() 47 | { 48 | struct nfq_handle *h; 49 | struct nfnl_handle *nh; 50 | int fd,rv; 51 | struct timeval tv; 52 | int opt; 53 | 54 | char *buf; 55 | buf=(char *)calloc(1,T_DATA_SIZE); 56 | if(buf == NULL) 57 | { 58 | _logger.error("Unable to get memory for buffer"); 59 | return ; 60 | } 61 | 62 | _logger.debug("Trying to open nfq library"); 63 | if(!(h = nfq_open())) 64 | { 65 | _logger.fatal("Error during nfq_open"); 66 | return ; 67 | } 68 | 69 | if(nfq_unbind_pf(h,AF_INET6) < 0) 70 | { 71 | _logger.fatal("Error during nfq_unbind_pf()"); 72 | return ; 73 | } 74 | 75 | if(nfq_bind_pf(h,AF_INET6) < 0) 76 | { 77 | _logger.fatal("Error during nfq_bind_pf"); 78 | return ; 79 | } 80 | 81 | _logger.information("NFQ: Binding to queue %d",_config.queueNumber); 82 | 83 | qh = nfq_create_queue(h,_config.queueNumber,&nfqueue_cb,this); 84 | 85 | if(!qh) 86 | { 87 | _logger.fatal("Error during nfq_create_queue"); 88 | return ; 89 | } 90 | 91 | if(nfq_set_mode(qh,NFQNL_COPY_PACKET,0xffff) < 0) 92 | { 93 | _logger.fatal("Can't set packet copy mode"); 94 | return ; 95 | } 96 | 97 | nh = nfq_nfnlh(h); // netlink handle 98 | 99 | fd = nfnl_fd(nh); 100 | 101 | _logger.information("Setting queue length to %d", _queue_maxlen); 102 | /* non-fatal if it fails */ 103 | if (nfq_set_queue_maxlen(qh, _queue_maxlen) < 0) 104 | { 105 | _logger.warning("can't set queue maxlen: your kernel probably doesn't support setting the queue length"); 106 | } 107 | 108 | /* set netlink buffer size to a decent value */ 109 | nfnl_rcvbufsiz(nh, _queue_maxlen * 1500); 110 | _logger.information("Setting nfnl bufsize to %d",_queue_maxlen * 1500); 111 | opt=1; 112 | 113 | if(setsockopt(fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &opt, sizeof(int)) == -1) 114 | { 115 | _logger.warning("Can't set netlink enobufs: %s", strerror(errno)); 116 | } 117 | /* Set some netlink specific option on the socket to increase performance */ 118 | opt = 1; 119 | if (setsockopt(fd, SOL_NETLINK, NETLINK_BROADCAST_SEND_ERROR, &opt, sizeof(int)) == -1) 120 | { 121 | _logger.warning("Can't set netlink broadcast error: %s",strerror(errno)); 122 | } 123 | /* set a timeout to the socket so we can check for a signal 124 | * in case we don't get packets for a longer period. */ 125 | tv.tv_sec = 1; 126 | tv.tv_usec = 0; 127 | 128 | if(::setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) 129 | { 130 | _logger.warning("can't set socket timeout: %s",strerror(errno)); 131 | } 132 | 133 | Poco::ThreadPool threadpool("PktAnalyzerPool"); 134 | for(int i=1; i <= _config.num_threads; i++) 135 | { 136 | PktAnalyzer *thread=new PktAnalyzer("PktAnalyzer "+std::to_string(i),queue,_config,this); 137 | _workThreads.push_back(thread); 138 | threadpool.start(*thread); 139 | } 140 | 141 | while(!isCancelled()) 142 | { 143 | if((rv=::recv(fd,buf,T_DATA_SIZE,0)) >= 0) 144 | { 145 | // посылаем пакет в наш callback 146 | try 147 | { 148 | nfq_handle_packet(h,buf,rv); 149 | } catch (Poco::Exception &excep) 150 | { 151 | time_t ttm=time(NULL); 152 | std::string s=std::to_string(ttm); 153 | Poco::FileOutputStream pdump("/tmp/packet_dump"+s,std::ios::binary); 154 | for(int i=0; i < rv; i++) 155 | { 156 | pdump << (unsigned char) buf[i]; 157 | } 158 | pdump.close(); 159 | _logger.error("Got exception: %s:%s",excep.message(),excep.what()); 160 | } catch (...) 161 | { 162 | _logger.error("Unknown exception!"); 163 | } 164 | continue; 165 | } else if(rv == -1) { 166 | if(errno == EWOULDBLOCK) 167 | { 168 | // всего лишь таймаут, ждем снова... 169 | continue; 170 | } 171 | // выходим из цикла... 172 | if(errno == EINTR) 173 | { 174 | break; 175 | } 176 | switch(errno) 177 | { 178 | case EAGAIN: 179 | _logger.error("ERROR: EAGAIN"); break; 180 | case EBADF: 181 | _logger.error("ERROR: EBADF: Bad file descriptor"); break; 182 | case ECONNRESET: 183 | _logger.error("ERROR: ECONNRESET: NFQ socket connection reset"); break; 184 | case ETIMEDOUT: 185 | _logger.error("ERROR: ETIMEDOUT: NFQ socket connection timedout"); break; 186 | case ENOBUFS: 187 | _logger.error("ERROR: ENOBUFS: Application is not fast enough. Increase socket buffer size by nfnl_rcvbufsize()"); break; 188 | default: 189 | _logger.error("Unknown error code %d",errno); break; 190 | } 191 | } 192 | } 193 | while(!queue.empty()) Poco::Thread::sleep(100); 194 | for(auto it=_workThreads.begin(); it != _workThreads.end(); it++) 195 | { 196 | (*it)->stop(); 197 | } 198 | queue.wakeUpAll(); 199 | threadpool.joinAll(); 200 | nfq_destroy_queue(qh); 201 | nfq_close(h); 202 | if(buf) 203 | free(buf); 204 | _logger.debug("Destroing queue %d",_config.queueNumber); 205 | } 206 | 207 | 208 | int nfqThread::nfqueue_cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) 209 | { 210 | // указатель на наш объект 211 | nfqThread *self=(nfqThread *)data; 212 | struct nfqnl_msg_packet_hdr *ph; 213 | ph = nfq_get_msg_packet_hdr(nfa); 214 | self->_logger.debug("Got the packet from queue"); 215 | // if(ph && ph->hook == NF_INET_LOCAL_OUT) 216 | if(ph && ph->hook == NF_INET_PRE_ROUTING) 217 | { 218 | 219 | self->queue.enqueueNotification(new PktNotification(qh,ntohl(ph->packet_id),nfa)); 220 | return 0; 221 | } else { 222 | if(!ph) 223 | { 224 | self->_logger.error("NFQ: Can't get message packet header"); 225 | return 0; 226 | } else { 227 | self->_logger.warning("NFQ: Packet not for our callback"); 228 | } 229 | } 230 | nfq_set_verdict(qh,ntohl(ph->packet_id),NF_ACCEPT,0,NULL); 231 | return 0; 232 | } 233 | 234 | 235 | -------------------------------------------------------------------------------- /src/sender.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #include "sender.h" 21 | #include 22 | #include 23 | #include 24 | 25 | struct pseudo_header 26 | { 27 | u_int32_t source_address; 28 | u_int32_t dest_address; 29 | u_int8_t placeholder; 30 | u_int8_t protocol; 31 | u_int16_t tcp_length; 32 | }; 33 | 34 | struct ipv6_pseudo_hdr 35 | { 36 | struct in6_addr source_address; 37 | struct in6_addr dest_address; 38 | u_int32_t tcp_length; 39 | u_int32_t zero: 24, 40 | nexthdr: 8; 41 | }; 42 | 43 | CSender::CSender(struct params &prm) : _logger(Poco::Logger::get("CSender")), _parameters(prm) 44 | { 45 | this->s = ::socket( PF_INET, SOCK_RAW, IPPROTO_RAW ); 46 | if( s == -1 ) { 47 | _logger.error("Failed to create IPv4 socket!"); 48 | return; 49 | } 50 | this->s6 = ::socket( PF_INET6, SOCK_RAW, IPPROTO_RAW ); 51 | if( s6 == -1 ) { 52 | _logger.error("Failed to create IPv6 socket!"); 53 | return; 54 | } 55 | 56 | int one = 1; 57 | const int *val = &one; 58 | if( ::setsockopt(this->s, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0 ) 59 | { 60 | _logger.error("Error setting IP_HDRINCL for IPv4 socket"); 61 | return; 62 | } 63 | 64 | this->rHeader = "HTTP/1.1 "+_parameters.code+"\r\nLocation: " + _parameters.redirect_url + "\r\nConnection: close\r\n"; 65 | _logger.debug("Default header is %s", rHeader); 66 | } 67 | 68 | CSender::~CSender() 69 | { 70 | ::close(s); 71 | ::close(s6); 72 | } 73 | 74 | void CSender::sendPacket(Poco::Net::IPAddress &ip_from, Poco::Net::IPAddress &ip_to, int port_from, int port_to, uint32_t acknum, uint32_t seqnum, std::string &dt, int f_reset, int f_psh) 75 | { 76 | char datagram[4096], *data, *pseudogram=NULL; 77 | 78 | // zero out the packet buffer 79 | memset(datagram, 0, sizeof(datagram)); 80 | 81 | // IP header 82 | struct iphdr *iph = (struct iphdr *) datagram; 83 | struct ip6_hdr *iph6 = (struct ip6_hdr *) datagram; 84 | 85 | // TCP header 86 | struct tcphdr *tcph = (struct tcphdr *) (datagram + (ip_from.family() == Poco::Net::IPAddress::IPv4 ? sizeof(struct iphdr) : sizeof(struct ip6_hdr))); 87 | 88 | struct sockaddr_in sin; 89 | struct sockaddr_in6 sin6; 90 | int payloadlen=dt.size(); 91 | 92 | // Data part 93 | data = (char *)tcph + sizeof(struct tcphdr); 94 | memcpy(data,dt.c_str(),payloadlen); 95 | 96 | _logger.debug("Trying to send packet to %s port %d", ip_to.toString(), port_to); 97 | 98 | if(ip_from.family() == Poco::Net::IPAddress::IPv4) 99 | { 100 | sin.sin_family = AF_INET; 101 | sin.sin_port = htons(port_to); 102 | sin.sin_addr.s_addr=((in_addr *)ip_to.addr())->s_addr; 103 | // Fill the IPv4 header 104 | iph->ihl = 5; 105 | iph->version = 4; 106 | iph->tos=0; 107 | iph->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr) + payloadlen; 108 | iph->id = htons(random()); 109 | iph->frag_off = 0; 110 | iph->ttl = 250; 111 | iph->protocol = IPPROTO_TCP; 112 | iph->check = 0; 113 | iph->saddr = ((in_addr *)ip_from.addr())->s_addr; 114 | iph->daddr = sin.sin_addr.s_addr; 115 | // IP checksum 116 | iph->check = 0; // done by kernel //this->csum((unsigned short *) datagram, iph->tot_len); 117 | } else { 118 | sin6.sin6_family = AF_INET6; 119 | sin6.sin6_port = 0; // not filled in ipv6 120 | memcpy(&sin6.sin6_addr,ip_to.addr(),sizeof(sin6.sin6_addr)); 121 | 122 | // IPv6 version (4 bits), Traffic class (8 bits), Flow label (20 bits) 123 | iph6->ip6_flow = htonl ((6 << 28) | (0 << 20) | 0); 124 | // Payload length (16 bits): TCP header + TCP data 125 | iph6->ip6_plen = htons (sizeof(struct tcphdr) + payloadlen); 126 | // Next header (8 bits): 6 for TCP 127 | iph6->ip6_nxt = IPPROTO_TCP; 128 | // Hop limit (8 bits): default to maximum value 129 | iph6->ip6_hops = 250; 130 | memcpy(&iph6->ip6_src,ip_from.addr(),sizeof(in6_addr)); 131 | memcpy(&iph6->ip6_dst,ip_to.addr(),sizeof(in6_addr)); 132 | } 133 | 134 | // TCP Header 135 | tcph->source = htons(port_from); 136 | tcph->dest = htons(port_to); 137 | tcph->seq = acknum; 138 | tcph->doff = 5; 139 | tcph->syn = 0; 140 | tcph->rst = f_reset; 141 | tcph->psh = f_psh; 142 | if(f_reset) 143 | { 144 | tcph->ack = 0; 145 | tcph->ack_seq = 0; 146 | tcph->fin = 0; 147 | } else { 148 | tcph->ack_seq = seqnum; 149 | tcph->ack = 1; 150 | tcph->fin = 1; 151 | } 152 | tcph->urg = 0; 153 | tcph->window = htons(5840); 154 | tcph->check = 0; 155 | tcph->urg_ptr = 0; 156 | 157 | 158 | 159 | if(ip_from.family() == Poco::Net::IPAddress::IPv4) 160 | { 161 | struct pseudo_header psh; 162 | psh.source_address = ((in_addr *)ip_from.addr())->s_addr; 163 | psh.dest_address = sin.sin_addr.s_addr; 164 | psh.placeholder = 0; 165 | psh.protocol = IPPROTO_TCP; 166 | psh.tcp_length = htons(sizeof(struct tcphdr) + dt.size() ); 167 | 168 | int psize = sizeof(struct pseudo_header) + sizeof(struct tcphdr) + dt.size(); 169 | pseudogram = (char*)calloc(1,psize); 170 | 171 | memcpy( pseudogram, (char*) &psh, sizeof(struct pseudo_header)); 172 | memcpy( pseudogram + sizeof(struct pseudo_header), tcph, sizeof(struct tcphdr) + dt.size()); 173 | 174 | tcph->check = csum( (unsigned short*) pseudogram, psize); 175 | 176 | // Send the packet 177 | if( ::sendto( this->s, datagram, iph->tot_len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0 ) 178 | { 179 | _logger.error("sendto() failed to %s:%d errno: %d",ip_to.toString(), port_to, errno); 180 | } 181 | } else { 182 | struct ipv6_pseudo_hdr psh; 183 | // filling pseudoheader... 184 | memcpy(&psh.source_address,&iph6->ip6_src,sizeof(iph6->ip6_src)); 185 | memcpy(&psh.dest_address,&iph6->ip6_dst,sizeof(iph6->ip6_dst)); 186 | psh.tcp_length = htonl(sizeof(tcphdr) + payloadlen); 187 | psh.zero = 0; 188 | psh.nexthdr = iph6->ip6_nxt; 189 | int psize = sizeof(ipv6_pseudo_hdr) + sizeof(struct tcphdr) + payloadlen; 190 | 191 | pseudogram = (char*)calloc(1,psize); 192 | memcpy( pseudogram, (char*) &psh, sizeof(struct ipv6_pseudo_hdr)); 193 | memcpy( pseudogram + sizeof(struct ipv6_pseudo_hdr), tcph, sizeof(struct tcphdr) + dt.size()); 194 | 195 | tcph->check = csum( (unsigned short*) pseudogram, psize); 196 | 197 | // Send the packet 198 | if( ::sendto( this->s6, datagram, (sizeof(struct ip6_hdr) + sizeof(struct tcphdr) + payloadlen), 0, (struct sockaddr *)&sin6, sizeof(sin6)) < 0 ) 199 | { 200 | _logger.error("sendto() failed to [%s]:%d errno: %d",ip_to.toString(), port_to, errno); 201 | } 202 | } 203 | if(pseudogram) 204 | free(pseudogram); 205 | 206 | return; 207 | } 208 | 209 | //void CSender::sendPacket(char *ip_from, char *ip_to, int port_from, int port_to, uint32_t acknum, uint32_t seqnum) 210 | void CSender::Redirect(int user_port, int dst_port, Poco::Net::IPAddress &user_ip, Poco::Net::IPAddress &dst_ip, uint32_t acknum, uint32_t seqnum, int f_psh, std::string &additional_param ) 211 | { 212 | // формируем дополнительные параметры 213 | std::string tstr=rHeader; 214 | if(!additional_param.empty()) 215 | { 216 | tstr = "HTTP/1.1 "+_parameters.code+"\r\nLocation: " + _parameters.redirect_url + additional_param + "\r\nConnection: close\r\n"; 217 | } else { 218 | tstr=rHeader; 219 | } 220 | this->sendPacket( dst_ip, user_ip, dst_port, user_port, acknum, seqnum, tstr, 0, 0); 221 | // And reset session with client 222 | // this->sendPacket( dst_ip, user_ip, dst_port, user_port, acknum, seqnum, redirectHeader, 1, 0); 223 | 224 | // And reset session with server 225 | std::string empty_str; 226 | this->sendPacket( user_ip, dst_ip, user_port, dst_port, seqnum, acknum, empty_str, 1, f_psh ); 227 | 228 | return; 229 | } 230 | 231 | void CSender::SendRST(int user_port, int dst_port, Poco::Net::IPAddress &user_ip, Poco::Net::IPAddress &dst_ip, uint32_t acknum, uint32_t seqnum, int f_psh) 232 | { 233 | std::string empty_str; 234 | // send rst to the client 235 | this->sendPacket( dst_ip, user_ip, dst_port, user_port, acknum, seqnum, empty_str, 1, 0); 236 | // send rst to the server 237 | this->sendPacket( user_ip, dst_ip, user_port, dst_port, seqnum, acknum, empty_str, 1, 0 ); 238 | } 239 | 240 | unsigned short CSender::csum( unsigned short *ptr, int nbytes ) 241 | { 242 | register long sum; 243 | unsigned short oddbyte; 244 | register short answer; 245 | 246 | sum = 0; 247 | while( nbytes > 1 ) { 248 | sum+=*ptr++; 249 | nbytes-=2; 250 | } 251 | if( nbytes==1 ) { 252 | oddbyte=0; 253 | *((u_char*)&oddbyte)=*(u_char*)ptr; 254 | sum+=oddbyte; 255 | } 256 | 257 | sum = (sum>>16)+(sum & 0xffff); 258 | sum = sum+(sum>>16); 259 | answer=(short)~sum; 260 | 261 | return( answer ); 262 | } 263 | -------------------------------------------------------------------------------- /src/node.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * node.c: implementation of automata node 3 | * This file is part of multifast. 4 | * 5 | Copyright 2010-2013 Kamiar Kanani 6 | 7 | multifast is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | multifast is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with multifast. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include "node.h" 25 | 26 | /* reallocation step for AC_NODE_t.matched_patterns */ 27 | #define REALLOC_CHUNK_MATCHSTR 1 28 | 29 | /* reallocation step for AC_NODE_t.outgoing array */ 30 | #define REALLOC_CHUNK_OUTGOING 1 31 | /* For different node depth, number of outgoing edges differs considerably 32 | * if you care about preprocessing speed, you can set a higher value for 33 | * reallocation step size to prevent multiple reallocations. 34 | */ 35 | 36 | /* Private function prototype */ 37 | static void node_init (AC_NODE_t * thiz); 38 | static int node_edge_compare (const void * l, const void * r); 39 | static int node_has_matchstr (AC_NODE_t * thiz, AC_PATTERN_t * newstr); 40 | 41 | 42 | /****************************************************************************** 43 | * FUNCTION: node_create 44 | * Create the node 45 | ******************************************************************************/ 46 | struct AC_NODE * node_create(void) 47 | { 48 | AC_NODE_t * thiz; 49 | thiz = (AC_NODE_t *) malloc (sizeof(AC_NODE_t)); 50 | node_init(thiz); 51 | node_assign_id(thiz); 52 | return thiz; 53 | } 54 | 55 | /****************************************************************************** 56 | * FUNCTION: node_init 57 | * Initialize node 58 | ******************************************************************************/ 59 | void node_init(AC_NODE_t * thiz) 60 | { 61 | memset(thiz, 0, sizeof(AC_NODE_t)); 62 | 63 | thiz->outgoing_max = REALLOC_CHUNK_OUTGOING; 64 | thiz->outgoing = (struct edge *) malloc 65 | (thiz->outgoing_max*sizeof(struct edge)); 66 | 67 | thiz->matched_patterns_max = REALLOC_CHUNK_MATCHSTR; 68 | thiz->matched_patterns = (AC_PATTERN_t *) malloc 69 | (thiz->matched_patterns_max*sizeof(AC_PATTERN_t)); 70 | } 71 | 72 | /****************************************************************************** 73 | * FUNCTION: node_release 74 | * Release node 75 | ******************************************************************************/ 76 | void node_release(AC_NODE_t * thiz) 77 | { 78 | free(thiz->matched_patterns); 79 | free(thiz->outgoing); 80 | free(thiz); 81 | } 82 | 83 | /****************************************************************************** 84 | * FUNCTION: node_find_next 85 | * Find out the next node for a given Alpha to move. this function is used in 86 | * the pre-processing stage in which edge array is not sorted. so it uses 87 | * linear search. 88 | ******************************************************************************/ 89 | AC_NODE_t * node_find_next(AC_NODE_t * thiz, AC_ALPHABET_t alpha) 90 | { 91 | int i; 92 | 93 | for (i=0; i < thiz->outgoing_degree; i++) 94 | { 95 | if(thiz->outgoing[i].alpha == alpha) 96 | return (thiz->outgoing[i].next); 97 | } 98 | return NULL; 99 | } 100 | 101 | /****************************************************************************** 102 | * FUNCTION: node_findbs_next 103 | * Find out the next node for a given Alpha. this function is used after the 104 | * pre-processing stage in which we sort edges. so it uses Binary Search. 105 | ******************************************************************************/ 106 | AC_NODE_t * node_findbs_next (AC_NODE_t * thiz, AC_ALPHABET_t alpha) 107 | { 108 | int min, max, mid; 109 | AC_ALPHABET_t amid; 110 | 111 | min = 0; 112 | max = thiz->outgoing_degree - 1; 113 | 114 | while (min <= max) 115 | { 116 | mid = (min+max) >> 1; 117 | amid = thiz->outgoing[mid].alpha; 118 | if (alpha > amid) 119 | min = mid + 1; 120 | else if (alpha < amid) 121 | max = mid - 1; 122 | else 123 | return (thiz->outgoing[mid].next); 124 | } 125 | return NULL; 126 | } 127 | 128 | /****************************************************************************** 129 | * FUNCTION: node_has_matchstr 130 | * Determine if a final node contains a pattern in its accepted pattern list 131 | * or not. return values: 1 = it has, 0 = it hasn't 132 | ******************************************************************************/ 133 | int node_has_matchstr (AC_NODE_t * thiz, AC_PATTERN_t * newstr) 134 | { 135 | int i, j; 136 | AC_PATTERN_t * str; 137 | 138 | for (i=0; i < thiz->matched_patterns_num; i++) 139 | { 140 | str = &thiz->matched_patterns[i]; 141 | 142 | if (str->length != newstr->length) 143 | continue; 144 | 145 | for (j=0; jlength; j++) 146 | if(str->astring[j] != newstr->astring[j]) 147 | continue; 148 | 149 | if (j == str->length) 150 | return 1; 151 | } 152 | return 0; 153 | } 154 | 155 | /****************************************************************************** 156 | * FUNCTION: node_create_next 157 | * Create the next node for the given alpha. 158 | ******************************************************************************/ 159 | AC_NODE_t * node_create_next (AC_NODE_t * thiz, AC_ALPHABET_t alpha) 160 | { 161 | AC_NODE_t * next; 162 | next = node_find_next (thiz, alpha); 163 | if (next) 164 | /* The edge already exists */ 165 | return NULL; 166 | /* Otherwise register new edge */ 167 | next = node_create (); 168 | node_register_outgoing(thiz, next, alpha); 169 | 170 | return next; 171 | } 172 | 173 | /****************************************************************************** 174 | * FUNCTION: node_register_matchstr 175 | * Adds the pattern to the list of accepted pattern. 176 | ******************************************************************************/ 177 | void node_register_matchstr (AC_NODE_t * thiz, AC_PATTERN_t * str) 178 | { 179 | /* Check if the new pattern already exists in the node list */ 180 | if (node_has_matchstr(thiz, str)) 181 | return; 182 | 183 | /* Manage memory */ 184 | if (thiz->matched_patterns_num >= thiz->matched_patterns_max) 185 | { 186 | thiz->matched_patterns_max += REALLOC_CHUNK_MATCHSTR; 187 | thiz->matched_patterns = (AC_PATTERN_t *) realloc 188 | (thiz->matched_patterns, thiz->matched_patterns_max*sizeof(AC_PATTERN_t)); 189 | } 190 | 191 | thiz->matched_patterns[thiz->matched_patterns_num].astring = str->astring; 192 | thiz->matched_patterns[thiz->matched_patterns_num].length = str->length; 193 | thiz->matched_patterns[thiz->matched_patterns_num].rep = str->rep; 194 | thiz->matched_patterns_num++; 195 | } 196 | 197 | /****************************************************************************** 198 | * FUNCTION: node_register_outgoing 199 | * Establish an edge between two nodes 200 | ******************************************************************************/ 201 | void node_register_outgoing 202 | (AC_NODE_t * thiz, AC_NODE_t * next, AC_ALPHABET_t alpha) 203 | { 204 | if(thiz->outgoing_degree >= thiz->outgoing_max) 205 | { 206 | thiz->outgoing_max += REALLOC_CHUNK_OUTGOING; 207 | thiz->outgoing = (struct edge *) realloc 208 | (thiz->outgoing, thiz->outgoing_max*sizeof(struct edge)); 209 | } 210 | 211 | thiz->outgoing[thiz->outgoing_degree].alpha = alpha; 212 | thiz->outgoing[thiz->outgoing_degree++].next = next; 213 | } 214 | 215 | /****************************************************************************** 216 | * FUNCTION: node_assign_id 217 | * assign a unique ID to the node (used for debugging purpose). 218 | ******************************************************************************/ 219 | void node_assign_id (AC_NODE_t * thiz) 220 | { 221 | static int unique_id = 1; 222 | thiz->id = unique_id ++; 223 | } 224 | 225 | /****************************************************************************** 226 | * FUNCTION: node_edge_compare 227 | * Comparison function for qsort. see man qsort. 228 | ******************************************************************************/ 229 | int node_edge_compare (const void * l, const void * r) 230 | { 231 | /* According to man page: 232 | * The comparison function must return an integer less than, equal to, or 233 | * greater than zero if the first argument is considered to be 234 | * respectively less than, equal to, or greater than the second. if two 235 | * members compare as equal, their order in the sorted array is undefined. 236 | * 237 | * NOTE: Because edge alphabets are unique in every node we ignore 238 | * equivalence case. 239 | **/ 240 | if ( ((struct edge *)l)->alpha >= ((struct edge *)r)->alpha ) 241 | return 1; 242 | else 243 | return -1; 244 | } 245 | 246 | /****************************************************************************** 247 | * FUNCTION: node_sort_edges 248 | * sorts edges alphabets. 249 | ******************************************************************************/ 250 | void node_sort_edges (AC_NODE_t * thiz) 251 | { 252 | qsort ((void *)thiz->outgoing, thiz->outgoing_degree, sizeof(struct edge), 253 | node_edge_compare); 254 | } 255 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include "main.h" 24 | #include "nfqstatistictask.h" 25 | #include "qdpi.h" 26 | #include "AhoCorasickPlus.h" 27 | #include "sendertask.h" 28 | #include "nfqthread.h" 29 | #include "reloadtask.h" 30 | #include 31 | #include 32 | 33 | Poco::Mutex nfqFilter::_domainMapMutex; 34 | DomainsMatchType *nfqFilter::_domainsMatchType = new DomainsMatchType; 35 | DomainsMatchType *nfqFilter::_SSLdomainsMatchType = new DomainsMatchType; 36 | 37 | Poco::RWLock nfqFilter::_ipportMapMutex; 38 | IPPortMap *nfqFilter::_ipportMap = new IPPortMap; 39 | Poco::RWLock nfqFilter::_sslIpsSetMutex; 40 | Patricia *nfqFilter::_sslIps = new Patricia; 41 | 42 | Poco::Mutex nfqFilter::_urlMapMutex; 43 | 44 | Poco::Mutex nfqFilter::_sslMutex; 45 | 46 | struct ndpi_detection_module_struct* nfqFilter::my_ndpi_struct = NULL; 47 | u_int32_t nfqFilter::ndpi_size_flow_struct = 0; 48 | u_int32_t nfqFilter::ndpi_size_id_struct = 0; 49 | 50 | u_int32_t nfqFilter::current_ndpi_memory = 0; 51 | u_int32_t nfqFilter::max_ndpi_memory = 0; 52 | 53 | AhoCorasickPlus *nfqFilter::atm=NULL; 54 | 55 | AhoCorasickPlus *nfqFilter::atm_ssl=NULL; 56 | 57 | AhoCorasickPlus *nfqFilter::atm_domains=NULL; 58 | 59 | std::map add_type_s; 60 | 61 | nfqFilter::nfqFilter(): 62 | _helpRequested(false), 63 | _errorHandler(*this), 64 | _cmd_queueNum(-1), 65 | _cmd_threadsNum(-1) 66 | { 67 | Poco::ErrorHandler::set(&_errorHandler); 68 | } 69 | 70 | nfqFilter::~nfqFilter() 71 | { 72 | } 73 | 74 | void nfqFilter::initialize(Application& self) 75 | { 76 | loadConfiguration(); 77 | 78 | ServerApplication::initialize(self); 79 | 80 | if(_cmd_queueNum > 0) 81 | { 82 | _config.queueNumber=_cmd_queueNum; 83 | } else { 84 | _config.queueNumber=config().getInt("queue",0); 85 | } 86 | _config.max_pending_packets=config().getInt("max_pending_packets",DEFAULT_MAX_PENDING_PACKETS); 87 | _config.send_rst=config().getBool("send_rst", false); 88 | _config.mark_value=config().getInt("mark_value",MARK_VALUE); 89 | _config.block_undetected_ssl=config().getBool("block_undetected_ssl",false); 90 | _config.save_exception_dump=config().getBool("save_bad_packets",false); 91 | _config.lower_host=config().getBool("lower_host",false); 92 | _config.match_url_exactly=config().getBool("match_url_exactly",false); 93 | _config.url_decode=config().getBool("url_decode",false); 94 | unsigned concurentThreadsSupported = std::thread::hardware_concurrency(); 95 | 96 | if(_cmd_threadsNum > 0 && _cmd_threadsNum <= 16) 97 | { 98 | _config.num_threads=_cmd_threadsNum; 99 | } else { 100 | if (concurentThreadsSupported>0) { 101 | if (concurentThreadsSupported > 16) { 102 | concurentThreadsSupported=16; 103 | } 104 | _config.num_threads=config().getInt("num_threads",concurentThreadsSupported); 105 | } else { 106 | _config.num_threads=config().getInt("num_threads",2); 107 | } 108 | if(_config.num_threads > 16) 109 | _config.num_threads=16; 110 | } 111 | 112 | std::string add_p_type=config().getString("url_additional_info","none"); 113 | std::transform(add_p_type.begin(), add_p_type.end(), add_p_type.begin(), ::tolower); 114 | 115 | add_type_s["none"]=A_TYPE_NONE; 116 | add_type_s["line"]=A_TYPE_ID; 117 | add_type_s["url"]=A_TYPE_URL; 118 | 119 | std::map::iterator it=add_type_s.find(add_p_type); 120 | if(it == add_type_s.end()) 121 | { 122 | throw Poco::Exception("Unknown url_additional_info type '" + add_p_type + "'",404); 123 | } 124 | _config.add_p_type=it->second; 125 | logger().debug("URL additional info set to %s", add_p_type); 126 | 127 | std::string http_code=config().getString("http_code",""); 128 | if(!http_code.empty()) 129 | { 130 | http_code.erase(std::remove(http_code.begin(), http_code.end(), '"'), http_code.end()); 131 | _sender_params.code=http_code; 132 | logger().debug("HTTP code set to %s", http_code); 133 | } 134 | 135 | _domainsFile=config().getString("domainlist",""); 136 | _urlsFile=config().getString("urllist",""); 137 | _sender_params.redirect_url=config().getString("redirect_url",""); 138 | _protocolsFile=config().getString("protocols",""); 139 | 140 | _sslFile=config().getString("ssllist",""); 141 | _statistic_interval=config().getInt("statistic_interval",0); 142 | 143 | _sslIpsFile=config().getString("sslips",""); 144 | 145 | _hostsFile=config().getString("hostlist",""); 146 | 147 | logger().information("Starting up on queue: %d",_config.queueNumber); 148 | 149 | atm_domains=new AhoCorasickPlus(); 150 | loadDomains(_domainsFile,atm_domains,_domainsMatchType); 151 | atm_domains->finalize(); 152 | 153 | 154 | atm_ssl=new AhoCorasickPlus(); 155 | if(!_sslFile.empty()) 156 | loadDomains(_sslFile,atm_ssl,_SSLdomainsMatchType); 157 | atm_ssl->finalize(); 158 | 159 | atm=new AhoCorasickPlus(); 160 | loadURLs(_urlsFile,atm); 161 | atm->finalize(); 162 | 163 | 164 | 165 | if(!_hostsFile.empty()) 166 | loadHosts(_hostsFile,_ipportMap); 167 | 168 | if(!_sslIpsFile.empty()) 169 | loadSSLIP(_sslIpsFile,_sslIps); 170 | 171 | my_ndpi_struct = init_ndpi(); 172 | 173 | if (my_ndpi_struct == NULL) { 174 | logger().error("Can't load nDPI!"); 175 | } 176 | if(!_protocolsFile.empty()) 177 | { 178 | ndpi_load_protocols_file(my_ndpi_struct, (char *)_protocolsFile.c_str()); 179 | } 180 | // Load sizes of main parsing structures 181 | ndpi_size_id_struct = ndpi_detection_get_sizeof_ndpi_id_struct(); 182 | ndpi_size_flow_struct = ndpi_detection_get_sizeof_ndpi_flow_struct(); 183 | } 184 | 185 | void nfqFilter::uninitialize() 186 | { 187 | logger().debug("Shutting down"); 188 | ServerApplication::uninitialize(); 189 | } 190 | 191 | void nfqFilter::defineOptions(Poco::Util::OptionSet& options) 192 | { 193 | ServerApplication::defineOptions(options); 194 | options.addOption( 195 | Poco::Util::Option("help","h","display help on command line arguments") 196 | .required(false) 197 | .repeatable(false) 198 | .callback(Poco::Util::OptionCallback(this,&nfqFilter::handleHelp))); 199 | options.addOption( 200 | Poco::Util::Option("config_file","c","specify config file to read") 201 | .required(true) 202 | .repeatable(false) 203 | .argument("file")); 204 | options.addOption( 205 | Poco::Util::Option("queue","q","specify queue number") 206 | .required(false) 207 | .repeatable(false) 208 | .argument("queue_num")); 209 | 210 | options.addOption( 211 | Poco::Util::Option("threads","t","specify number of running threads") 212 | .required(false) 213 | .repeatable(false) 214 | .argument("threads_num")); 215 | 216 | } 217 | 218 | void nfqFilter::handleOption(const std::string& name,const std::string& value) 219 | { 220 | ServerApplication::handleOption(name, value); 221 | if(name == "config_file") 222 | { 223 | loadConfiguration(value); 224 | } 225 | if(name == "queue") 226 | { 227 | _cmd_queueNum = Poco::NumberParser::parse(value); 228 | } 229 | if(name == "threads") 230 | { 231 | _cmd_threadsNum = Poco::NumberParser::parse(value); 232 | } 233 | } 234 | 235 | void nfqFilter::handleHelp(const std::string& name,const std::string& value) 236 | { 237 | _helpRequested=true; 238 | displayHelp(); 239 | stopOptionsProcessing(); 240 | } 241 | 242 | void nfqFilter::displayHelp() 243 | { 244 | Poco::Util::HelpFormatter helpFormatter(options()); 245 | helpFormatter.setCommand(commandName()); 246 | helpFormatter.setUsage("<-c config file> [options]"); 247 | helpFormatter.setHeader("NfQueue filter"); 248 | helpFormatter.format(std::cout); 249 | } 250 | 251 | namespace 252 | { 253 | static void handleSignal(int sig) 254 | { 255 | Poco::Util::Application& app = Poco::Util::Application::instance(); 256 | app.logger().information("Got HUP signal - reload data"); 257 | ReloadTask::_event.set(); 258 | } 259 | } 260 | 261 | int nfqFilter::main(const ArgVec& args) 262 | { 263 | if(!_helpRequested) 264 | { 265 | struct sigaction handler; 266 | handler.sa_handler = handleSignal; 267 | handler.sa_flags = 0; 268 | sigemptyset(&handler.sa_mask); 269 | sigaction(SIGHUP, &handler, NULL); 270 | Poco::TaskManager tm; 271 | tm.start(new NFQStatisticTask(_statistic_interval)); 272 | tm.start(new nfqThread(_config)); 273 | tm.start(new SenderTask(_sender_params)); 274 | tm.start(new ReloadTask(this)); 275 | waitForTerminationRequest(); 276 | tm.cancelAll(); 277 | SenderTask::queue.wakeUpAll(); 278 | tm.joinAll(); 279 | } 280 | return Poco::Util::Application::EXIT_OK; 281 | } 282 | 283 | void nfqFilter::loadDomains(std::string &fn, AhoCorasickPlus *dm_atm,DomainsMatchType *dm_map) 284 | { 285 | Poco::FileInputStream df(fn); 286 | if(df.good()) 287 | { 288 | int lineno=1; 289 | while(!df.eof()) 290 | { 291 | std::string str; 292 | getline(df,str); 293 | if(!str.empty()) 294 | { 295 | if(str[0] == '#' || str[0] == ';') 296 | continue; 297 | AhoCorasickPlus::EnumReturnStatus status; 298 | AhoCorasickPlus::PatternId patId = lineno; 299 | std::size_t pos = str.find("*."); 300 | bool exact_match=true; 301 | std::string insert=str; 302 | if(pos != std::string::npos) 303 | { 304 | exact_match=false; 305 | insert=str.substr(pos+2,str.length()-2); 306 | } 307 | status = dm_atm->addPattern(insert, patId); 308 | if (status!=AhoCorasickPlus::RETURNSTATUS_SUCCESS) 309 | { 310 | if(status == AhoCorasickPlus::RETURNSTATUS_DUPLICATE_PATTERN) 311 | { 312 | logger().warning("Pattern '%s' already present database from file %s",insert,fn); 313 | } else { 314 | logger().error("Failed to add '%s' from line %d from file %s",insert,lineno,fn); 315 | } 316 | } else { 317 | std::pair res=dm_map->insert(DomainsMatchType::ValueType(lineno,exact_match)); 318 | if(res.second) 319 | { 320 | logger().debug("Inserted domain: '%s' from line %d from file %s",str,lineno,fn); 321 | } else { 322 | logger().debug("Updated domain: '%s' from line %d from file %s",str,lineno,fn); 323 | } 324 | } 325 | } 326 | lineno++; 327 | } 328 | } else 329 | throw Poco::OpenFileException(fn); 330 | df.close(); 331 | } 332 | 333 | void nfqFilter::loadURLs(std::string &fn, AhoCorasickPlus *dm_atm) 334 | { 335 | Poco::FileInputStream uf(fn); 336 | if(uf.good()) 337 | { 338 | int lineno=1; 339 | while(!uf.eof()) 340 | { 341 | std::string str; 342 | getline(uf,str); 343 | if(!str.empty()) 344 | { 345 | if(str[0] == '#' || str[0] == ';') 346 | continue; 347 | AhoCorasickPlus::EnumReturnStatus status; 348 | AhoCorasickPlus::PatternId patId = lineno; 349 | std::string url = str; 350 | std::size_t http_pos = url.find("http://"); 351 | if(http_pos == std::string::npos || http_pos > 0) 352 | { 353 | url.insert(0,"http://"); 354 | } 355 | status = dm_atm->addPattern(url, patId); 356 | if (status!=AhoCorasickPlus::RETURNSTATUS_SUCCESS) 357 | { 358 | if(status == AhoCorasickPlus::RETURNSTATUS_DUPLICATE_PATTERN) 359 | { 360 | logger().warning("Pattern '%s' already present in the URL database from file %s",str,fn); 361 | } else { 362 | logger().error("Failed to add '%s' from line %d from file %s",str,lineno,fn); 363 | } 364 | } 365 | } 366 | lineno++; 367 | } 368 | } else 369 | throw Poco::OpenFileException(fn); 370 | uf.close(); 371 | } 372 | 373 | void nfqFilter::loadHosts(std::string &fn,IPPortMap *ippm) 374 | { 375 | Poco::FileInputStream hf(fn); 376 | if(hf.good()) 377 | { 378 | int lineno=1; 379 | while(!hf.eof()) 380 | { 381 | std::string str; 382 | getline(hf,str); 383 | if(!str.empty()) 384 | { 385 | if(str[0] == '#' || str[0] == ';') 386 | continue; 387 | std::size_t found=str.find(":"); 388 | std::string ip=str.substr(0, found); 389 | std::string port; 390 | unsigned short porti=0; 391 | if(found != std::string::npos) 392 | { 393 | port=str.substr(found+1,str.length()); 394 | logger().debug("IP is %s port %s",ip,port); 395 | porti=atoi(port.c_str()); 396 | } else { 397 | logger().debug("IP %s without port", ip); 398 | } 399 | Poco::Net::IPAddress ip_addr(ip); 400 | IPPortMap::iterator it=ippm->find(ip_addr); 401 | if(it == ippm->end()) 402 | { 403 | std::set ports; 404 | if(porti) 405 | { 406 | logger().debug("Adding port %s to ip %s", port, ip); 407 | ports.insert(porti); 408 | } 409 | ippm->insert(std::make_pair(ip_addr,ports)); 410 | logger().debug("Inserted ip: %s from line %d", ip, lineno); 411 | } else { 412 | logger().debug("Adding port %s from line %d to ip %s", port,lineno,ip); 413 | it->second.insert(porti); 414 | } 415 | 416 | } 417 | lineno++; 418 | } 419 | } else 420 | throw Poco::OpenFileException(fn); 421 | hf.close(); 422 | } 423 | 424 | void nfqFilter::loadSSLIP(const std::string &fn, Patricia *patricia) 425 | { 426 | Poco::FileInputStream hf(fn); 427 | if(hf.good()) 428 | { 429 | int lineno=1; 430 | while(!hf.eof()) 431 | { 432 | std::string str; 433 | getline(hf,str); 434 | if(!str.empty()) 435 | { 436 | if(str[0] == '#' || str[0] == ';') 437 | continue; 438 | if(!patricia->make_and_lookup(str)) 439 | { 440 | logger().information("Unable to add IP address %s from line %d to the SSL IPs list", str, lineno); 441 | } 442 | } 443 | lineno++; 444 | } 445 | } else 446 | throw Poco::OpenFileException(fn); 447 | hf.close(); 448 | 449 | } 450 | 451 | POCO_SERVER_MAIN(nfqFilter) 452 | -------------------------------------------------------------------------------- /src/pktanalyzer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) Max 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 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "main.h" 30 | #include "AhoCorasickPlus.h" 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include "sendertask.h" 37 | #include "ndpiwrapper.h" 38 | #include "pktanalyzer.h" 39 | 40 | #define iphdr(x) ((struct iphdr *)(x)) 41 | #define tcphdr(x) ((struct tcphdr *)(x)) 42 | 43 | #define __USE_POCO_URI_DECODE 44 | 45 | PktAnalyzer::PktAnalyzer(const std::string& name, Poco::NotificationQueue& queue, struct nfqConfig& cfg, nfqThread *parent): 46 | _name(name), 47 | _queue(queue), 48 | _logger(Poco::Logger::get(name)), 49 | _config(cfg), 50 | _parent(parent), 51 | _idleTime(200), 52 | _stopped(false) 53 | { 54 | 55 | } 56 | 57 | 58 | char PktAnalyzer::from_hex(char ch) 59 | { 60 | return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; 61 | } 62 | 63 | std::string PktAnalyzer::url_decode(std::string text) 64 | { 65 | char h; 66 | std::string escaped; 67 | escaped.reserve(text.length()); 68 | for (auto i = text.begin(), n = text.end(); i != n; ++i) 69 | { 70 | std::string::value_type c = (*i); 71 | if (c == '%') 72 | { 73 | if (i[1] && i[2]) 74 | { 75 | h = from_hex(i[1]) << 4 | from_hex(i[2]); 76 | if((h >= '0' && h <= '9') || ( h >= 'a' && h <= 'z') || ( h >= 'A' && h <= 'Z')) 77 | { 78 | escaped += h; 79 | i += 2; 80 | } else { 81 | escaped += c; 82 | } 83 | } 84 | } else { 85 | escaped += c; 86 | } 87 | } 88 | return escaped; 89 | } 90 | 91 | void PktAnalyzer::dump_file(unsigned char *buf,uint32_t rv, int pkt_id) 92 | { 93 | std::string s=std::to_string(pkt_id); 94 | Poco::FileOutputStream pdump("/tmp/packet_dump"+s,std::ios::binary); 95 | for(uint32_t i=0; i < rv; i++) 96 | { 97 | pdump << (unsigned char) buf[i]; 98 | } 99 | pdump.close(); 100 | 101 | } 102 | 103 | void PktAnalyzer::analyzer(Packet &pkt) 104 | { 105 | Poco::Stopwatch sw; 106 | _logger.debug("Got packet from queue"); 107 | 108 | unsigned char *full_packet=pkt.get_payload(); 109 | int id = pkt.get_id(); 110 | uint32_t size = pkt.get_size(); 111 | struct nfq_q_handle *qh=pkt.get_qh(); 112 | struct ip *iph = (struct ip *)full_packet; 113 | struct ip6_hdr *iph6 = (struct ip6_hdr *)full_packet; 114 | 115 | // определяем версию протокола 116 | int ip_version=0; 117 | if(iph->ip_v == 6) 118 | ip_version = 6; 119 | else if (iph->ip_v == 4) 120 | ip_version = 4; 121 | if(!ip_version) 122 | { 123 | _logger.error("Unsupported IP protocol version %d for packet id %d",(int) iph->ip_v, id); 124 | nfq_set_verdict(qh,id,NF_ACCEPT,0,NULL); 125 | dump_file(full_packet,size,id); 126 | return ; 127 | } 128 | 129 | unsigned char *pkt_data_ptr = NULL; 130 | struct tcphdr* tcph; 131 | 132 | pkt_data_ptr = full_packet + (ip_version == 4 ? sizeof(struct ip) : sizeof(struct ip6_hdr)); 133 | 134 | tcph = (struct tcphdr *) pkt_data_ptr; 135 | 136 | // длина ip заголовка 137 | int iphlen = iphdr(full_packet)->ihl*4; // ipv4 138 | if(ip_version == 6) 139 | iphlen = sizeof(struct ip6_hdr); 140 | 141 | // длина tcp заголовка 142 | int tcphlen = tcphdr(full_packet+iphlen)->doff*4; 143 | 144 | // общая длина всех заголовков 145 | uint32_t hlen = iphlen + tcphlen; 146 | 147 | _parent->inc_total_bytes_packets(size); 148 | 149 | // пропускаем пакет без данных 150 | if(hlen == size) 151 | { 152 | nfq_set_verdict(qh,id,NF_ACCEPT,0,NULL); 153 | return ; 154 | } 155 | 156 | int tcp_src_port=ntohs(tcph->source); 157 | int tcp_dst_port=ntohs(tcph->dest); 158 | std::unique_ptr src_ip; 159 | std::unique_ptr dst_ip; 160 | if(ip_version == 4) 161 | { 162 | src_ip.reset(new Poco::Net::IPAddress(&iph->ip_src,sizeof(in_addr))); 163 | dst_ip.reset(new Poco::Net::IPAddress(&iph->ip_dst,sizeof(in_addr))); 164 | } else { 165 | src_ip.reset(new Poco::Net::IPAddress(&iph6->ip6_src,sizeof(in6_addr))); 166 | dst_ip.reset(new Poco::Net::IPAddress(&iph6->ip6_dst,sizeof(in6_addr))); 167 | } 168 | 169 | uint8_t ip_protocol=(ip_version == 4 ? iph->ip_p : iph6->ip6_ctlun.ip6_un1.ip6_un1_nxt); 170 | 171 | 172 | { 173 | Poco::ScopedReadRWLock lock(nfqFilter::_ipportMapMutex); 174 | IPPortMap::iterator it_ip=nfqFilter::_ipportMap->find(*dst_ip.get()); 175 | if(it_ip != nfqFilter::_ipportMap->end()) 176 | { 177 | unsigned short port=tcp_dst_port; 178 | if (it_ip->second.size() == 0 || it_ip->second.find(port) != it_ip->second.end()) 179 | { 180 | _parent->inc_matched_ip_port(); 181 | if(_config.send_rst) 182 | { 183 | _logger.debug("HostList: Send RST to the client (%s) and server (%s) (packet no %d)",src_ip->toString(),dst_ip->toString(),id); 184 | std::string empty_str; 185 | SenderTask::queue.enqueueNotification(new RedirectNotification(tcp_src_port, tcp_dst_port,src_ip.get(), dst_ip.get(),/*acknum*/ tcph->ack_seq, /*seqnum*/ tcph->seq,/* flag psh */ (tcph->psh ? 1 : 0 ),empty_str,true)); 186 | _parent->inc_sended_rst(); 187 | nfq_set_verdict(qh,id,NF_DROP,0,NULL); 188 | } else { 189 | _logger.debug("HostList: Set mark %d to packet no %d port %hu",_config.mark_value,id,port); 190 | _parent->inc_marked_hosts(); 191 | nfq_set_verdict2(qh,id,NF_ACCEPT,_config.mark_value,0,NULL); 192 | } 193 | return ; 194 | } 195 | } 196 | } 197 | 198 | 199 | // nDPI usage 200 | sw.reset(); 201 | sw.start(); 202 | nDPIWrapper nw; 203 | 204 | struct ndpi_flow_struct *flow=nw.get_flow(); 205 | 206 | uint32_t current_tickt = 0; 207 | ndpi_protocol protocol = ndpi_detection_process_packet(nfqFilter::my_ndpi_struct, flow, full_packet, size, current_tickt, nw.get_src(), nw.get_dst()); 208 | 209 | if(protocol.protocol == NDPI_PROTOCOL_UNKNOWN) 210 | { 211 | _logger.debug("Guessing protocol..."); 212 | protocol = ndpi_guess_undetected_protocol(nfqFilter::my_ndpi_struct, 213 | ip_protocol, 214 | 0,//ip 215 | tcp_src_port, // sport 216 | 0, 217 | tcp_dst_port); // dport 218 | } 219 | _logger.debug("Protocol is %hu/%hu ",protocol.master_protocol,protocol.protocol); 220 | sw.stop(); 221 | _logger.debug("nDPI protocol detection occupied %ld us",sw.elapsed()); 222 | if(protocol.master_protocol == NDPI_PROTOCOL_SSL || protocol.protocol == NDPI_PROTOCOL_SSL || protocol.protocol == NDPI_PROTOCOL_TOR) 223 | { 224 | if(flow->l4.tcp.ssl_seen_client_cert == 1) 225 | { 226 | std::string ssl_client; 227 | _logger.debug("Analysing SSL protocol"); 228 | if(flow->protos.ssl.client_certificate[0] != '\0') 229 | { 230 | ssl_client=flow->protos.ssl.client_certificate; 231 | _logger.debug("SSL client is: %s",ssl_client); 232 | } 233 | if(!ssl_client.empty()) 234 | { 235 | sw.reset(); 236 | sw.start(); 237 | if(_config.lower_host) 238 | std::transform(ssl_client.begin(), ssl_client.end(), ssl_client.begin(), ::tolower); 239 | AhoCorasickPlus::Match match; 240 | std::size_t host_len=ssl_client.length(); 241 | bool found=false; 242 | { 243 | Poco::Mutex::ScopedLock lock(nfqFilter::_sslMutex); 244 | nfqFilter::atm_ssl->search(ssl_client,false); 245 | while(nfqFilter::atm_ssl->findNext(match) && !found) 246 | { 247 | if(match.pattern.length != host_len) 248 | { 249 | DomainsMatchType::Iterator it=nfqFilter::_SSLdomainsMatchType->find(match.id); 250 | bool exact_match=false; 251 | if(it != nfqFilter::_SSLdomainsMatchType->end()) 252 | exact_match = it->second; 253 | if(exact_match) 254 | continue; 255 | if(ssl_client[host_len-match.pattern.length-1] != '.') 256 | continue; 257 | } 258 | found=true; 259 | } 260 | } 261 | sw.stop(); 262 | _logger.debug("SSL Host seek occupied %ld us, host: %s",sw.elapsed(),ssl_client); 263 | if(found) 264 | { 265 | _parent->inc_matched_ssl(); 266 | if(_config.send_rst) 267 | { 268 | _logger.debug("SSLHostList: Send RST to the client (%s) and server (%s) (packet no %d)",src_ip->toString(),dst_ip->toString(),id); 269 | std::string empty_str; 270 | SenderTask::queue.enqueueNotification(new RedirectNotification(tcp_src_port, tcp_dst_port,src_ip.get(), dst_ip.get(),/*acknum*/ tcph->ack_seq, /*seqnum*/ tcph->seq,/* flag psh */ (tcph->psh ? 1 : 0 ),empty_str,true)); 271 | _parent->inc_sended_rst(); 272 | nfq_set_verdict(qh,id,NF_DROP,0,NULL); 273 | } else { 274 | _logger.debug("SSLHostList: Set mark %d to packet no %d, ssl host name: %s",_config.mark_value,id,ssl_client); 275 | _parent->inc_marked_ssl(); 276 | nfq_set_verdict2(qh,id,NF_ACCEPT,_config.mark_value,0,NULL); 277 | } 278 | return ; 279 | } else { 280 | nfq_set_verdict(qh,id,NF_ACCEPT,0,NULL); 281 | return ; 282 | } 283 | } else { 284 | if(_config.block_undetected_ssl) 285 | { 286 | Poco::ScopedReadRWLock lock(nfqFilter::_sslIpsSetMutex); 287 | if(nfqFilter::_sslIps->try_search_exact_ip(*dst_ip.get())) 288 | { 289 | _parent->inc_matched_ssl_ip(); 290 | _logger.debug("Blocking/Marking SSL client hello packet from %s:%d to %s:%d", src_ip->toString(),tcp_src_port,dst_ip->toString(),tcp_dst_port); 291 | if(_config.send_rst) 292 | { 293 | _logger.debug("SSLClientHello: Send RST to the client (%s) and server (%s) (packet no %d)",src_ip->toString(),dst_ip->toString(),id); 294 | std::string empty_str; 295 | SenderTask::queue.enqueueNotification(new RedirectNotification(tcp_src_port, tcp_dst_port,src_ip.get(), dst_ip.get(),/*acknum*/ tcph->ack_seq, /*seqnum*/ tcph->seq,/* flag psh */ (tcph->psh ? 1 : 0 ),empty_str,true)); 296 | _parent->inc_sended_rst(); 297 | nfq_set_verdict(qh,id,NF_DROP,0,NULL); 298 | } else { 299 | _logger.debug("SSLClientHello: Set mark %d to packet no %d",_config.mark_value,id); 300 | _parent->inc_marked_ssl(); 301 | nfq_set_verdict2(qh,id,NF_ACCEPT,_config.mark_value,0,NULL); 302 | } 303 | return ; 304 | } 305 | } 306 | _logger.debug("No ssl client certificate found! Accept packet from %s:%d to %s:%d.",src_ip->toString(),tcp_src_port,dst_ip->toString(),tcp_dst_port); 307 | } 308 | } 309 | nfq_set_verdict(qh,id,NF_ACCEPT,0,NULL); 310 | return ; 311 | } 312 | if(protocol.master_protocol != NDPI_PROTOCOL_HTTP && protocol.protocol != NDPI_PROTOCOL_HTTP && protocol.protocol != NDPI_PROTOCOL_DIRECT_DOWNLOAD_LINK) 313 | { 314 | _logger.debug("Not http protocol. Protocol is %hu/%hu from %s:%d to %s:%d",protocol.master_protocol,protocol.protocol,src_ip->toString(),tcp_src_port,dst_ip->toString(),tcp_dst_port); 315 | nfq_set_verdict(qh,id,NF_ACCEPT,0,NULL); 316 | return ; 317 | } 318 | 319 | _logger.debug("Got HTTP protocol"); 320 | 321 | std::string host((char *)&flow->host_server_name[0]); 322 | if((flow->http.method == HTTP_METHOD_GET || flow->http.method == HTTP_METHOD_POST || flow->http.method == HTTP_METHOD_HEAD) && !host.empty()) 323 | { 324 | int dot_del=0; 325 | if(host[host.length()-1] == '.') 326 | { 327 | dot_del=host.length()-1; 328 | host.erase(dot_del,1); 329 | } 330 | if(_config.lower_host) 331 | std::transform(host.begin(), host.end(), host.begin(), ::tolower); 332 | sw.reset(); 333 | sw.start(); 334 | 335 | AhoCorasickPlus::Match match; 336 | bool found=false; 337 | { 338 | Poco::Mutex::ScopedLock lock(nfqFilter::_domainMapMutex); 339 | nfqFilter::atm_domains->search(host,false); 340 | std::size_t host_len=host.length(); 341 | while(nfqFilter::atm_domains->findNext(match) && !found) 342 | { 343 | if(match.pattern.length != host_len) 344 | { 345 | DomainsMatchType::Iterator it=nfqFilter::_domainsMatchType->find(match.id); 346 | bool exact_match=false; 347 | if(it != nfqFilter::_domainsMatchType->end()) 348 | exact_match = it->second; 349 | if(exact_match) 350 | continue; 351 | if(host[host_len-match.pattern.length-1] != '.') 352 | continue; 353 | } 354 | found=true; 355 | } 356 | } 357 | sw.stop(); 358 | _logger.debug("Host seek occupied %ld us",sw.elapsed()); 359 | if(found) 360 | { 361 | _logger.debug("Host %s present in domain (file line %d) list from ip %s", host, match.id, src_ip->toString()); 362 | std::string add_param; 363 | switch (_config.add_p_type) 364 | { 365 | case A_TYPE_ID: add_param="id="+std::to_string(match.id); 366 | break; 367 | case A_TYPE_URL: add_param="url="+host; 368 | break; 369 | default: break; 370 | } 371 | SenderTask::queue.enqueueNotification(new RedirectNotification(tcp_src_port, tcp_dst_port, src_ip.get(), dst_ip.get(),/*acknum*/ tcph->ack_seq, /*seqnum*/ tcph->seq,/* flag psh */ (tcph->psh ? 1 : 0 ),add_param)); 372 | _parent->inc_redirected_domains(); 373 | nfq_set_verdict(qh,id,NF_DROP,0,NULL); 374 | return ; 375 | } 376 | sw.reset(); 377 | sw.start(); 378 | found=false; 379 | std::string uri_o(flow->http.url ? flow->http.url : ""); 380 | if(flow->http.url) 381 | { 382 | std::string uri; 383 | if(dot_del) 384 | uri_o.erase(dot_del+7,1); 385 | try 386 | { 387 | Poco::URI uri_p(uri_o); 388 | uri_p.normalize(); 389 | uri.assign(uri_p.toString()); 390 | if(_config.url_decode) 391 | { 392 | #ifdef __USE_POCO_URI_DECODE 393 | Poco::URI::decode(uri_p.toString(),uri); 394 | #else 395 | uri=url_decode(uri); 396 | #endif 397 | } 398 | } catch (Poco::SyntaxException &ex) 399 | { 400 | _logger.debug("An SyntaxException occured: '%s' on URI: '%s'",ex.displayText(), uri_o); 401 | uri.assign(flow->http.url); 402 | } 403 | 404 | { 405 | Poco::Mutex::ScopedLock lock(nfqFilter::_urlMapMutex); 406 | nfqFilter::atm->search(uri,false); 407 | while(nfqFilter::atm->findNext(match) && !found) 408 | { 409 | if(_config.match_url_exactly && uri.length() != match.pattern.length) 410 | continue; 411 | found=true; 412 | } 413 | } 414 | sw.stop(); 415 | _logger.debug("URL seek occupied %ld us for uri %s",sw.elapsed(),uri); 416 | if(found) 417 | { 418 | _logger.debug("URL %s present in url (file pos %u) list from ip %s",uri,match.id,src_ip->toString()); 419 | std::string add_param; 420 | switch (_config.add_p_type) 421 | { 422 | case A_TYPE_ID: add_param="id="+std::to_string(match.id); 423 | break; 424 | case A_TYPE_URL: add_param="url="+uri; 425 | break; 426 | default: break; 427 | } 428 | SenderTask::queue.enqueueNotification(new RedirectNotification(tcp_src_port, tcp_dst_port,src_ip.get(),dst_ip.get(),/*acknum*/ tcph->ack_seq, /*seqnum*/ tcph->seq,/* flag psh */ (tcph->psh ? 1 : 0 ),add_param)); 429 | _parent->inc_redirected_urls(); 430 | nfq_set_verdict(qh,id,NF_DROP,0,NULL); 431 | return ; 432 | } 433 | } 434 | } 435 | nfq_set_verdict(qh,id,NF_ACCEPT,0,NULL); 436 | } 437 | 438 | void PktAnalyzer::run() 439 | { 440 | _logger.debug("Starting thread..."); 441 | for (;;) 442 | { 443 | Poco::Notification::Ptr pNf(_queue.waitDequeueNotification(_idleTime)); 444 | if (pNf) 445 | { 446 | PktNotification::Ptr pPktNf = pNf.cast(); 447 | if (pPktNf) 448 | { 449 | analyzer(pPktNf->pkt()); 450 | } 451 | } 452 | Poco::FastMutex::ScopedLock lock(_mutex); 453 | if(_stopped) 454 | { 455 | break; 456 | } 457 | } 458 | _logger.debug("Stopping thread..."); 459 | } 460 | 461 | void PktAnalyzer::stop() 462 | { 463 | _stopped=true; 464 | } 465 | 466 | -------------------------------------------------------------------------------- /src/ahocorasick.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ahocorasick.cpp: implementation of ahocorasick library's functions 3 | * This file is part of multifast. 4 | * 5 | Copyright 2010-2013 Kamiar Kanani 6 | 7 | multifast is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | multifast is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with multifast. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "node.h" 27 | #include "ahocorasick.h" 28 | 29 | /* Allocation step for automata.all_nodes */ 30 | #define REALLOC_CHUNK_ALLNODES 200 31 | 32 | /* Private function prototype */ 33 | static void ac_automata_register_nodeptr 34 | (AC_AUTOMATA_t * thiz, AC_NODE_t * node); 35 | static void ac_automata_union_matchstrs 36 | (AC_NODE_t * node); 37 | static void ac_automata_set_failure 38 | (AC_AUTOMATA_t * thiz, AC_NODE_t * node, AC_ALPHABET_t * alphas); 39 | static void ac_automata_traverse_setfailure 40 | (AC_AUTOMATA_t * thiz, AC_NODE_t * node, AC_ALPHABET_t * alphas); 41 | static void ac_automata_reset (AC_AUTOMATA_t * thiz); 42 | 43 | 44 | /****************************************************************************** 45 | * FUNCTION: ac_automata_init 46 | * Initialize automata; allocate memories and set initial values 47 | * PARAMS: 48 | ******************************************************************************/ 49 | AC_AUTOMATA_t * ac_automata_init () 50 | { 51 | AC_AUTOMATA_t * thiz = (AC_AUTOMATA_t *)malloc(sizeof(AC_AUTOMATA_t)); 52 | memset (thiz, 0, sizeof(AC_AUTOMATA_t)); 53 | thiz->root = node_create (); 54 | thiz->all_nodes_max = REALLOC_CHUNK_ALLNODES; 55 | thiz->all_nodes = (AC_NODE_t **) malloc (thiz->all_nodes_max*sizeof(AC_NODE_t *)); 56 | ac_automata_register_nodeptr (thiz, thiz->root); 57 | ac_automata_reset (thiz); 58 | thiz->total_patterns = 0; 59 | thiz->automata_open = 1; 60 | return thiz; 61 | } 62 | 63 | /****************************************************************************** 64 | * FUNCTION: ac_automata_add 65 | * Adds pattern to the automata. 66 | * PARAMS: 67 | * AC_AUTOMATA_t * thiz: the pointer to the automata 68 | * AC_PATTERN_t * patt: the pointer to added pattern 69 | * RETUERN VALUE: AC_ERROR_t 70 | * the return value indicates the success or failure of adding action 71 | ******************************************************************************/ 72 | AC_STATUS_t ac_automata_add (AC_AUTOMATA_t * thiz, AC_PATTERN_t * patt) 73 | { 74 | unsigned int i; 75 | AC_NODE_t * n = thiz->root; 76 | AC_NODE_t * next; 77 | AC_ALPHABET_t alpha; 78 | 79 | if(!thiz->automata_open) 80 | return ACERR_AUTOMATA_CLOSED; 81 | 82 | if (!patt->length) 83 | return ACERR_ZERO_PATTERN; 84 | 85 | if (patt->length > AC_PATTRN_MAX_LENGTH) 86 | return ACERR_LONG_PATTERN; 87 | 88 | for (i=0; ilength; i++) 89 | { 90 | alpha = patt->astring[i]; 91 | if ((next = node_find_next(n, alpha))) 92 | { 93 | n = next; 94 | continue; 95 | } 96 | else 97 | { 98 | next = node_create_next(n, alpha); 99 | next->depth = n->depth + 1; 100 | n = next; 101 | ac_automata_register_nodeptr(thiz, n); 102 | } 103 | } 104 | 105 | if(n->final) 106 | return ACERR_DUPLICATE_PATTERN; 107 | 108 | n->final = 1; 109 | node_register_matchstr(n, patt); 110 | thiz->total_patterns++; 111 | 112 | return ACERR_SUCCESS; 113 | } 114 | 115 | /****************************************************************************** 116 | * FUNCTION: ac_automata_finalize 117 | * Locate the failure node for all nodes and collect all matched pattern for 118 | * every node. it also sorts outgoing edges of node, so binary search could be 119 | * performed on them. after calling this function the automate literally will 120 | * be finalized and you can not add new patterns to the automate. 121 | * PARAMS: 122 | * AC_AUTOMATA_t * thiz: the pointer to the automata 123 | ******************************************************************************/ 124 | void ac_automata_finalize (AC_AUTOMATA_t * thiz) 125 | { 126 | unsigned int i; 127 | AC_ALPHABET_t alphas[AC_PATTRN_MAX_LENGTH]; 128 | AC_NODE_t * node; 129 | 130 | ac_automata_traverse_setfailure (thiz, thiz->root, alphas); 131 | 132 | for (i=0; i < thiz->all_nodes_num; i++) 133 | { 134 | node = thiz->all_nodes[i]; 135 | ac_automata_union_matchstrs (node); 136 | node_sort_edges (node); 137 | } 138 | thiz->automata_open = 0; /* do not accept patterns any more */ 139 | } 140 | 141 | /****************************************************************************** 142 | * FUNCTION: ac_automata_search 143 | * Search in the input text using the given automata. on match event it will 144 | * call the call-back function. and the call-back function in turn after doing 145 | * its job, will return an integer value to ac_automata_search(). 0 value means 146 | * continue search, and non-0 value means stop search and return to the caller. 147 | * PARAMS: 148 | * AC_AUTOMATA_t * thiz: the pointer to the automata 149 | * AC_TEXT_t * txt: the input text that must be searched 150 | * int keep: is the input text the successive chunk of the previous given text 151 | * void * param: this parameter will be send to call-back function. it is 152 | * useful for sending parameter to call-back function from caller function. 153 | * RETURN VALUE: 154 | * -1: failed; automata is not finalized 155 | * 0: success; input text was searched to the end 156 | * 1: success; input text was searched partially. (callback broke the loop) 157 | ******************************************************************************/ 158 | int ac_automata_search (AC_AUTOMATA_t * thiz, AC_TEXT_t * text, int keep, 159 | AC_MATCH_CALBACK_f callback, void * param) 160 | { 161 | unsigned long position; 162 | AC_NODE_t * current; 163 | AC_NODE_t * next; 164 | AC_MATCH_t match; 165 | 166 | if (thiz->automata_open) 167 | /* you must call ac_automata_locate_failure() first */ 168 | return -1; 169 | 170 | thiz->text = 0; 171 | 172 | if (!keep) 173 | ac_automata_reset(thiz); 174 | 175 | position = 0; 176 | current = thiz->current_node; 177 | 178 | /* This is the main search loop. 179 | * it must be as lightweight as possible. */ 180 | while (position < text->length) 181 | { 182 | if (!(next = node_findbs_next(current, text->astring[position]))) 183 | { 184 | if(current->failure_node /* we are not in the root node */) 185 | current = current->failure_node; 186 | else 187 | position++; 188 | } 189 | else 190 | { 191 | current = next; 192 | position++; 193 | } 194 | 195 | if (current->final && next) 196 | /* We check 'next' to find out if we came here after a alphabet 197 | * transition or due to a fail. in second case we should not report 198 | * matching because it was reported in previous node */ 199 | { 200 | match.position = position + thiz->base_position; 201 | match.match_num = current->matched_patterns_num; 202 | match.patterns = current->matched_patterns; 203 | /* we found a match! do call-back */ 204 | if (callback(&match, param)) 205 | return 1; 206 | } 207 | } 208 | 209 | /* save status variables */ 210 | thiz->current_node = current; 211 | thiz->base_position += position; 212 | return 0; 213 | } 214 | 215 | /****************************************************************************** 216 | * FUNCTION: ac_automata_settext 217 | ******************************************************************************/ 218 | void ac_automata_settext (AC_AUTOMATA_t * thiz, AC_TEXT_t * text, int keep) 219 | { 220 | thiz->text = text; 221 | if (!keep) 222 | ac_automata_reset(thiz); 223 | thiz->position = 0; 224 | } 225 | 226 | /****************************************************************************** 227 | * FUNCTION: ac_automata_findnext 228 | ******************************************************************************/ 229 | AC_MATCH_t * ac_automata_findnext (AC_AUTOMATA_t * thiz) 230 | { 231 | unsigned long position; 232 | AC_NODE_t * current; 233 | AC_NODE_t * next; 234 | static AC_MATCH_t match; 235 | 236 | if (thiz->automata_open) 237 | return 0; 238 | 239 | if (!thiz->text) 240 | return 0; 241 | 242 | position = thiz->position; 243 | current = thiz->current_node; 244 | match.match_num = 0; 245 | 246 | /* This is the main search loop. 247 | * it must be as lightweight as possible. */ 248 | while (position < thiz->text->length) 249 | { 250 | if (!(next = node_findbs_next(current, thiz->text->astring[position]))) 251 | { 252 | if (current->failure_node /* we are not in the root node */) 253 | current = current->failure_node; 254 | else 255 | position++; 256 | } 257 | else 258 | { 259 | current = next; 260 | position++; 261 | } 262 | 263 | if (current->final && next) 264 | /* We check 'next' to find out if we came here after a alphabet 265 | * transition or due to a fail. in second case we should not report 266 | * matching because it was reported in previous node */ 267 | { 268 | match.position = position + thiz->base_position; 269 | match.match_num = current->matched_patterns_num; 270 | match.patterns = current->matched_patterns; 271 | break; 272 | } 273 | } 274 | 275 | /* save status variables */ 276 | thiz->current_node = current; 277 | thiz->position = position; 278 | 279 | if (!match.match_num) 280 | /* if we came here due to reaching to the end of input text 281 | * not a loop break 282 | */ 283 | thiz->base_position += position; 284 | 285 | return match.match_num?&match:0; 286 | } 287 | 288 | /****************************************************************************** 289 | * FUNCTION: ac_automata_reset 290 | * reset the automata and make it ready for doing new search on a new text. 291 | * when you finished with the input text, you must reset automata state for 292 | * new input, otherwise it will not work. 293 | * PARAMS: 294 | * AC_AUTOMATA_t * thiz: the pointer to the automata 295 | ******************************************************************************/ 296 | void ac_automata_reset (AC_AUTOMATA_t * thiz) 297 | { 298 | thiz->current_node = thiz->root; 299 | thiz->base_position = 0; 300 | } 301 | 302 | /****************************************************************************** 303 | * FUNCTION: ac_automata_release 304 | * Release all allocated memories to the automata 305 | * PARAMS: 306 | * AC_AUTOMATA_t * thiz: the pointer to the automata 307 | ******************************************************************************/ 308 | void ac_automata_release (AC_AUTOMATA_t * thiz) 309 | { 310 | unsigned int i; 311 | AC_NODE_t * n; 312 | 313 | for (i=0; i < thiz->all_nodes_num; i++) 314 | { 315 | n = thiz->all_nodes[i]; 316 | node_release(n); 317 | } 318 | free(thiz->all_nodes); 319 | free(thiz); 320 | } 321 | 322 | /****************************************************************************** 323 | * FUNCTION: ac_automata_display 324 | * Prints the automata to output in human readable form. it is useful for 325 | * debugging purpose. 326 | * PARAMS: 327 | * AC_AUTOMATA_t * thiz: the pointer to the automata 328 | * char repcast: 'n': print AC_REP_t as number, 's': print AC_REP_t as string 329 | ******************************************************************************/ 330 | void ac_automata_display (AC_AUTOMATA_t * thiz, char repcast) 331 | { 332 | unsigned int i, j; 333 | AC_NODE_t * n; 334 | struct edge * e; 335 | AC_PATTERN_t sid; 336 | 337 | printf("---------------------------------\n"); 338 | 339 | for (i=0; iall_nodes_num; i++) 340 | { 341 | n = thiz->all_nodes[i]; 342 | printf("NODE(%3d)/----fail----> NODE(%3d)\n", 343 | n->id, (n->failure_node)?n->failure_node->id:1); 344 | for (j=0; joutgoing_degree; j++) 345 | { 346 | e = &n->outgoing[j]; 347 | printf(" |----("); 348 | if(isgraph(e->alpha)) 349 | printf("%c)---", e->alpha); 350 | else 351 | printf("0x%x)", e->alpha); 352 | printf("--> NODE(%3d)\n", e->next->id); 353 | } 354 | if (n->matched_patterns_num) { 355 | printf("Accepted patterns: {"); 356 | for (j=0; jmatched_patterns_num; j++) 357 | { 358 | sid = n->matched_patterns[j]; 359 | if(j) printf(", "); 360 | switch (repcast) 361 | { 362 | case 'n': 363 | printf("%ld", sid.rep.number); 364 | break; 365 | case 's': 366 | printf("%s", sid.rep.stringy); 367 | break; 368 | } 369 | } 370 | printf("}\n"); 371 | } 372 | printf("---------------------------------\n"); 373 | } 374 | } 375 | 376 | /****************************************************************************** 377 | * FUNCTION: ac_automata_register_nodeptr 378 | * Adds the node pointer to all_nodes. 379 | ******************************************************************************/ 380 | static void ac_automata_register_nodeptr (AC_AUTOMATA_t * thiz, AC_NODE_t * node) 381 | { 382 | if(thiz->all_nodes_num >= thiz->all_nodes_max) 383 | { 384 | thiz->all_nodes_max += REALLOC_CHUNK_ALLNODES; 385 | thiz->all_nodes = (AC_NODE **)realloc 386 | (thiz->all_nodes, thiz->all_nodes_max*sizeof(AC_NODE_t *)); 387 | } 388 | thiz->all_nodes[thiz->all_nodes_num++] = node; 389 | } 390 | 391 | /****************************************************************************** 392 | * FUNCTION: ac_automata_union_matchstrs 393 | * Collect accepted patterns of the node. the accepted patterns consist of the 394 | * node's own accepted pattern plus accepted patterns of its failure node. 395 | ******************************************************************************/ 396 | static void ac_automata_union_matchstrs (AC_NODE_t * node) 397 | { 398 | unsigned int i; 399 | AC_NODE_t * m = node; 400 | 401 | while ((m = m->failure_node)) 402 | { 403 | for (i=0; i < m->matched_patterns_num; i++) 404 | node_register_matchstr(node, &(m->matched_patterns[i])); 405 | 406 | if (m->final) 407 | node->final = 1; 408 | } 409 | /* TODO : sort matched_patterns? is that necessary? I don't think so. */ 410 | } 411 | 412 | /****************************************************************************** 413 | * FUNCTION: ac_automata_set_failure 414 | * find failure node for the given node. 415 | ******************************************************************************/ 416 | static void ac_automata_set_failure 417 | (AC_AUTOMATA_t * thiz, AC_NODE_t * node, AC_ALPHABET_t * alphas) 418 | { 419 | unsigned int i, j; 420 | AC_NODE_t * m; 421 | 422 | for (i=1; i < node->depth; i++) 423 | { 424 | m = thiz->root; 425 | for (j=i; j < node->depth && m; j++) 426 | m = node_find_next (m, alphas[j]); 427 | if (m) 428 | { 429 | node->failure_node = m; 430 | break; 431 | } 432 | } 433 | if (!node->failure_node) 434 | node->failure_node = thiz->root; 435 | } 436 | 437 | /****************************************************************************** 438 | * FUNCTION: ac_automata_traverse_setfailure 439 | * Traverse all automata nodes using DFS (Depth First Search), meanwhile it set 440 | * the failure node for every node it passes through. this function must be 441 | * called after adding last pattern to automata. i.e. after calling this you 442 | * can not add further pattern to automata. 443 | ******************************************************************************/ 444 | static void ac_automata_traverse_setfailure 445 | (AC_AUTOMATA_t * thiz, AC_NODE_t * node, AC_ALPHABET_t * alphas) 446 | { 447 | unsigned int i; 448 | AC_NODE_t * next; 449 | 450 | for (i=0; i < node->outgoing_degree; i++) 451 | { 452 | alphas[node->depth] = node->outgoing[i].alpha; 453 | next = node->outgoing[i].next; 454 | 455 | /* At every node look for its failure node */ 456 | ac_automata_set_failure (thiz, next, alphas); 457 | 458 | /* Recursively call itself to traverse all nodes */ 459 | ac_automata_traverse_setfailure (thiz, next, alphas); 460 | } 461 | } 462 | -------------------------------------------------------------------------------- /src/patricia.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: patricia.c,v 1.7 2005/12/07 20:46:41 dplonka Exp $ 3 | * Dave Plonka 4 | * 5 | * This product includes software developed by the University of Michigan, 6 | * Merit Network, Inc., and their contributors. 7 | * 8 | * This file had been called "radix.c" in the MRT sources. 9 | * 10 | * I renamed it to "patricia.c" since it's not an implementation of a general 11 | * radix trie. Also I pulled in various requirements from "prefix.c" and 12 | * "demo.c" so that it could be used as a standalone API. 13 | */ 14 | 15 | 16 | static char copyright[] = 17 | "This product includes software developed by the University of Michigan, Merit" 18 | "Network, Inc., and their contributors."; 19 | 20 | #include /* assert */ 21 | #include /* isdigit */ 22 | #include /* errno */ 23 | #include /* sin */ 24 | #include /* NULL */ 25 | #include /* sprintf, fprintf, stderr */ 26 | #include /* free, atol, calloc */ 27 | #include /* memcpy, strchr, strlen */ 28 | #include /* BSD: for inet_addr */ 29 | #include /* BSD, Linux: for inet_addr */ 30 | #include /* BSD, Linux: for inet_addr */ 31 | #include /* BSD, Linux, Solaris: for inet_addr */ 32 | 33 | #include "patricia.h" 34 | 35 | #define Delete free 36 | 37 | /* { from prefix.c */ 38 | 39 | /* prefix_tochar 40 | * convert prefix information to bytes 41 | */ 42 | u_char * 43 | prefix_tochar (prefix_t * prefix) 44 | { 45 | if (prefix == NULL) 46 | return (NULL); 47 | 48 | return ((u_char *) & prefix->add.sin); 49 | } 50 | 51 | int 52 | comp_with_mask (void *addr, void *dest, u_int mask) 53 | { 54 | 55 | if ( /* mask/8 == 0 || */ memcmp (addr, dest, mask / 8) == 0) { 56 | int n = mask / 8; 57 | int m = ((-1) << (8 - (mask % 8))); 58 | 59 | if (mask % 8 == 0 || (((u_char *)addr)[n] & m) == (((u_char *)dest)[n] & m)) 60 | return (1); 61 | } 62 | return (0); 63 | } 64 | 65 | 66 | #define PATRICIA_MAX_THREADS 16 67 | 68 | /* 69 | * convert prefix information to ascii string with length 70 | * thread safe and (almost) re-entrant implementation 71 | */ 72 | char * 73 | prefix_toa2x (prefix_t *prefix, char *buff, int with_len) 74 | { 75 | if (prefix == NULL) 76 | return ("(Null)"); 77 | assert (prefix->ref_count >= 0); 78 | if (buff == NULL) { 79 | 80 | struct buffer { 81 | char buffs[PATRICIA_MAX_THREADS][48+5]; 82 | u_int i; 83 | } *buffp; 84 | 85 | # if 0 86 | THREAD_SPECIFIC_DATA (struct buffer, buffp, 1); 87 | # else 88 | { /* for scope only */ 89 | static struct buffer local_buff; 90 | buffp = &local_buff; 91 | } 92 | # endif 93 | if (buffp == NULL) { 94 | /* XXX should we report an error? */ 95 | return (NULL); 96 | } 97 | 98 | buff = buffp->buffs[buffp->i++%PATRICIA_MAX_THREADS]; 99 | } 100 | if (prefix->family == AF_INET) { 101 | u_char *a; 102 | assert (prefix->bitlen <= sizeof(struct in_addr) * 8); 103 | a = prefix_touchar (prefix); 104 | if (with_len) { 105 | sprintf (buff, "%d.%d.%d.%d/%d", a[0], a[1], a[2], a[3], 106 | prefix->bitlen); 107 | } 108 | else { 109 | sprintf (buff, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]); 110 | } 111 | return (buff); 112 | } 113 | #ifdef HAVE_IPV6 114 | else if (prefix->family == AF_INET6) { 115 | char *r; 116 | r = (char *) inet_ntop (AF_INET6, &prefix->add.sin6, buff, 48 /* a guess value */ ); 117 | if (r && with_len) { 118 | assert (prefix->bitlen <= sizeof(struct in6_addr) * 8); 119 | sprintf (buff + strlen (buff), "/%d", prefix->bitlen); 120 | } 121 | return (buff); 122 | } 123 | #endif /* HAVE_IPV6 */ 124 | else 125 | return (NULL); 126 | } 127 | 128 | /* prefix_toa2 129 | * convert prefix information to ascii string 130 | */ 131 | char * 132 | prefix_toa2 (prefix_t *prefix, char *buff) 133 | { 134 | return (prefix_toa2x (prefix, buff, 0)); 135 | } 136 | 137 | /* prefix_toa 138 | */ 139 | char * 140 | prefix_toa (prefix_t * prefix) 141 | { 142 | return (prefix_toa2 (prefix, (char *) NULL)); 143 | } 144 | 145 | prefix_t * 146 | New_Prefix2 (int family, void *dest, int bitlen, prefix_t *prefix) 147 | { 148 | int dynamic_allocated = 0; 149 | int default_bitlen = sizeof(struct in_addr) * 8; 150 | 151 | #ifdef HAVE_IPV6 152 | if (family == AF_INET6) { 153 | default_bitlen = sizeof(struct in6_addr) * 8; 154 | if (prefix == NULL) { 155 | prefix = (prefix_t *)calloc(1, sizeof (prefix_t)); 156 | dynamic_allocated++; 157 | } 158 | memcpy (&prefix->add.sin6, dest, sizeof(struct in6_addr)); 159 | } 160 | else 161 | #endif /* HAVE_IPV6 */ 162 | if (family == AF_INET) { 163 | if (prefix == NULL) { 164 | #ifndef NT 165 | prefix = (prefix_t *)calloc(1, sizeof (prefix4_t)); 166 | #else 167 | //for some reason, compiler is getting 168 | //prefix4_t size incorrect on NT 169 | prefix = (prefix_t *)calloc(1, sizeof (prefix_t)); 170 | #endif /* NT */ 171 | 172 | dynamic_allocated++; 173 | } 174 | memcpy (&prefix->add.sin, dest, sizeof(struct in_addr)); 175 | } 176 | else { 177 | return (NULL); 178 | } 179 | 180 | prefix->bitlen = (bitlen >= 0)? bitlen: default_bitlen; 181 | prefix->family = family; 182 | prefix->ref_count = 0; 183 | if (dynamic_allocated) { 184 | prefix->ref_count++; 185 | } 186 | /* fprintf(stderr, "[C %s, %d]\n", prefix_toa (prefix), prefix->ref_count); */ 187 | return (prefix); 188 | } 189 | 190 | prefix_t * 191 | New_Prefix (int family, void *dest, int bitlen) 192 | { 193 | return (New_Prefix2 (family, dest, bitlen, NULL)); 194 | } 195 | 196 | 197 | prefix_t * 198 | Ref_Prefix (prefix_t * prefix) 199 | { 200 | if (prefix == NULL) 201 | return (NULL); 202 | if (prefix->ref_count == 0) { 203 | /* make a copy in case of a static prefix */ 204 | return (New_Prefix2 (prefix->family, &prefix->add, prefix->bitlen, NULL)); 205 | } 206 | prefix->ref_count++; 207 | /* fprintf(stderr, "[A %s, %d]\n", prefix_toa (prefix), prefix->ref_count); */ 208 | return (prefix); 209 | } 210 | 211 | void 212 | Deref_Prefix (prefix_t * prefix) 213 | { 214 | if (prefix == NULL) 215 | return; 216 | /* for secure programming, raise an assert. no static prefix can call this */ 217 | assert (prefix->ref_count > 0); 218 | 219 | prefix->ref_count--; 220 | assert (prefix->ref_count >= 0); 221 | if (prefix->ref_count <= 0) { 222 | Delete (prefix); 223 | return; 224 | } 225 | } 226 | 227 | /* } */ 228 | 229 | /* #define PATRICIA_DEBUG 1 */ 230 | 231 | static int num_active_patricia = 0; 232 | 233 | /* these routines support continuous mask only */ 234 | 235 | patricia_tree_t * 236 | New_Patricia (int maxbits) 237 | { 238 | patricia_tree_t *patricia = (patricia_tree_t *)calloc(1, sizeof *patricia); 239 | 240 | patricia->maxbits = maxbits; 241 | patricia->head = NULL; 242 | patricia->num_active_node = 0; 243 | assert (maxbits <= PATRICIA_MAXBITS); /* XXX */ 244 | num_active_patricia++; 245 | return (patricia); 246 | } 247 | 248 | 249 | /* 250 | * if func is supplied, it will be called as func(node->data) 251 | * before deleting the node 252 | */ 253 | 254 | void 255 | Clear_Patricia (patricia_tree_t *patricia, void_fn_t func) 256 | { 257 | assert (patricia); 258 | if (patricia->head) { 259 | 260 | patricia_node_t *Xstack[PATRICIA_MAXBITS+1]; 261 | patricia_node_t **Xsp = Xstack; 262 | patricia_node_t *Xrn = patricia->head; 263 | 264 | while (Xrn) { 265 | patricia_node_t *l = Xrn->l; 266 | patricia_node_t *r = Xrn->r; 267 | 268 | if (Xrn->prefix) { 269 | Deref_Prefix (Xrn->prefix); 270 | if (Xrn->data && func) 271 | func (Xrn->data); 272 | } 273 | else { 274 | assert (Xrn->data == NULL); 275 | } 276 | Delete (Xrn); 277 | patricia->num_active_node--; 278 | 279 | if (l) { 280 | if (r) { 281 | *Xsp++ = r; 282 | } 283 | Xrn = l; 284 | } else if (r) { 285 | Xrn = r; 286 | } else if (Xsp != Xstack) { 287 | Xrn = *(--Xsp); 288 | } else { 289 | Xrn = NULL; 290 | } 291 | } 292 | } 293 | assert (patricia->num_active_node == 0); 294 | /* Delete (patricia); */ 295 | } 296 | 297 | 298 | void 299 | Destroy_Patricia (patricia_tree_t *patricia, void_fn_t func) 300 | { 301 | Clear_Patricia (patricia, func); 302 | Delete (patricia); 303 | num_active_patricia--; 304 | } 305 | 306 | 307 | /* 308 | * if func is supplied, it will be called as func(node->prefix, node->data) 309 | */ 310 | 311 | void 312 | patricia_process (patricia_tree_t *patricia, void_fn_t func) 313 | { 314 | patricia_node_t *node; 315 | assert (func); 316 | 317 | PATRICIA_WALK (patricia->head, node) { 318 | func (node->prefix, node->data); 319 | } PATRICIA_WALK_END; 320 | } 321 | 322 | size_t 323 | patricia_walk_inorder(patricia_node_t *node, void_fn_t func) 324 | { 325 | size_t n = 0; 326 | assert(func); 327 | 328 | if (node->l) { 329 | n += patricia_walk_inorder(node->l, func); 330 | } 331 | 332 | if (node->prefix) { 333 | func(node->prefix, node->data); 334 | n++; 335 | } 336 | 337 | if (node->r) { 338 | n += patricia_walk_inorder(node->r, func); 339 | } 340 | 341 | return n; 342 | } 343 | 344 | 345 | patricia_node_t * 346 | patricia_search_exact (patricia_tree_t *patricia, prefix_t *prefix) 347 | { 348 | patricia_node_t *node; 349 | u_char *addr; 350 | u_int bitlen; 351 | 352 | assert (patricia); 353 | assert (prefix); 354 | assert (prefix->bitlen <= patricia->maxbits); 355 | 356 | if (patricia->head == NULL) 357 | return (NULL); 358 | 359 | node = patricia->head; 360 | addr = prefix_touchar (prefix); 361 | bitlen = prefix->bitlen; 362 | 363 | while (node->bit < bitlen) { 364 | 365 | if (BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { 366 | #ifdef PATRICIA_DEBUG 367 | if (node->prefix) 368 | fprintf (stderr, "patricia_search_exact: take right %s/%d\n", 369 | prefix_toa (node->prefix), node->prefix->bitlen); 370 | else 371 | fprintf (stderr, "patricia_search_exact: take right at %u\n", 372 | node->bit); 373 | #endif /* PATRICIA_DEBUG */ 374 | node = node->r; 375 | } 376 | else { 377 | #ifdef PATRICIA_DEBUG 378 | if (node->prefix) 379 | fprintf (stderr, "patricia_search_exact: take left %s/%d\n", 380 | prefix_toa (node->prefix), node->prefix->bitlen); 381 | else 382 | fprintf (stderr, "patricia_search_exact: take left at %u\n", 383 | node->bit); 384 | #endif /* PATRICIA_DEBUG */ 385 | node = node->l; 386 | } 387 | 388 | if (node == NULL) 389 | return (NULL); 390 | } 391 | 392 | #ifdef PATRICIA_DEBUG 393 | if (node->prefix) 394 | fprintf (stderr, "patricia_search_exact: stop at %s/%d\n", 395 | prefix_toa (node->prefix), node->prefix->bitlen); 396 | else 397 | fprintf (stderr, "patricia_search_exact: stop at %u\n", node->bit); 398 | #endif /* PATRICIA_DEBUG */ 399 | if (node->bit > bitlen || node->prefix == NULL) 400 | return (NULL); 401 | assert (node->bit == bitlen); 402 | assert (node->bit == node->prefix->bitlen); 403 | if (comp_with_mask (prefix_tochar (node->prefix), prefix_tochar (prefix), 404 | bitlen)) { 405 | #ifdef PATRICIA_DEBUG 406 | fprintf (stderr, "patricia_search_exact: found %s/%d\n", 407 | prefix_toa (node->prefix), node->prefix->bitlen); 408 | #endif /* PATRICIA_DEBUG */ 409 | return (node); 410 | } 411 | return (NULL); 412 | } 413 | 414 | 415 | /* if inclusive != 0, "best" may be the given prefix itself */ 416 | patricia_node_t * 417 | patricia_search_best2 (patricia_tree_t *patricia, prefix_t *prefix, int inclusive) 418 | { 419 | patricia_node_t *node; 420 | patricia_node_t *stack[PATRICIA_MAXBITS + 1]; 421 | u_char *addr; 422 | u_int bitlen; 423 | int cnt = 0; 424 | 425 | assert (patricia); 426 | assert (prefix); 427 | assert (prefix->bitlen <= patricia->maxbits); 428 | 429 | if (patricia->head == NULL) 430 | return (NULL); 431 | 432 | node = patricia->head; 433 | addr = prefix_touchar (prefix); 434 | bitlen = prefix->bitlen; 435 | 436 | while (node->bit < bitlen) { 437 | 438 | if (node->prefix) { 439 | #ifdef PATRICIA_DEBUG 440 | fprintf (stderr, "patricia_search_best: push %s/%d\n", 441 | prefix_toa (node->prefix), node->prefix->bitlen); 442 | #endif /* PATRICIA_DEBUG */ 443 | stack[cnt++] = node; 444 | } 445 | 446 | if (BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { 447 | #ifdef PATRICIA_DEBUG 448 | if (node->prefix) 449 | fprintf (stderr, "patricia_search_best: take right %s/%d\n", 450 | prefix_toa (node->prefix), node->prefix->bitlen); 451 | else 452 | fprintf (stderr, "patricia_search_best: take right at %u\n", 453 | node->bit); 454 | #endif /* PATRICIA_DEBUG */ 455 | node = node->r; 456 | } 457 | else { 458 | #ifdef PATRICIA_DEBUG 459 | if (node->prefix) 460 | fprintf (stderr, "patricia_search_best: take left %s/%d\n", 461 | prefix_toa (node->prefix), node->prefix->bitlen); 462 | else 463 | fprintf (stderr, "patricia_search_best: take left at %u\n", 464 | node->bit); 465 | #endif /* PATRICIA_DEBUG */ 466 | node = node->l; 467 | } 468 | 469 | if (node == NULL) 470 | break; 471 | } 472 | 473 | if (inclusive && node && node->prefix) 474 | stack[cnt++] = node; 475 | 476 | #ifdef PATRICIA_DEBUG 477 | if (node == NULL) 478 | fprintf (stderr, "patricia_search_best: stop at null\n"); 479 | else if (node->prefix) 480 | fprintf (stderr, "patricia_search_best: stop at %s/%d\n", 481 | prefix_toa (node->prefix), node->prefix->bitlen); 482 | else 483 | fprintf (stderr, "patricia_search_best: stop at %u\n", node->bit); 484 | #endif /* PATRICIA_DEBUG */ 485 | 486 | if (cnt <= 0) 487 | return (NULL); 488 | 489 | while (--cnt >= 0) { 490 | node = stack[cnt]; 491 | #ifdef PATRICIA_DEBUG 492 | fprintf (stderr, "patricia_search_best: pop %s/%d\n", 493 | prefix_toa (node->prefix), node->prefix->bitlen); 494 | #endif /* PATRICIA_DEBUG */ 495 | if (comp_with_mask (prefix_tochar (node->prefix), 496 | prefix_tochar (prefix), 497 | node->prefix->bitlen) && node->prefix->bitlen <= bitlen) { 498 | #ifdef PATRICIA_DEBUG 499 | fprintf (stderr, "patricia_search_best: found %s/%d\n", 500 | prefix_toa (node->prefix), node->prefix->bitlen); 501 | #endif /* PATRICIA_DEBUG */ 502 | return (node); 503 | } 504 | } 505 | return (NULL); 506 | } 507 | 508 | 509 | patricia_node_t * 510 | patricia_search_best (patricia_tree_t *patricia, prefix_t *prefix) 511 | { 512 | return (patricia_search_best2 (patricia, prefix, 1)); 513 | } 514 | 515 | 516 | patricia_node_t * 517 | patricia_lookup (patricia_tree_t *patricia, prefix_t *prefix) 518 | { 519 | patricia_node_t *node, *new_node, *parent, *glue; 520 | u_char *addr, *test_addr; 521 | u_int bitlen, check_bit, differ_bit; 522 | int i, j, r; 523 | 524 | assert (patricia); 525 | assert (prefix); 526 | assert (prefix->bitlen <= patricia->maxbits); 527 | 528 | if (patricia->head == NULL) { 529 | node = (patricia_node_t *)calloc(1, sizeof *node); 530 | node->bit = prefix->bitlen; 531 | node->prefix = Ref_Prefix (prefix); 532 | node->parent = NULL; 533 | node->l = node->r = NULL; 534 | node->data = NULL; 535 | patricia->head = node; 536 | #ifdef PATRICIA_DEBUG 537 | fprintf (stderr, "patricia_lookup: new_node #0 %s/%d (head)\n", 538 | prefix_toa (prefix), prefix->bitlen); 539 | #endif /* PATRICIA_DEBUG */ 540 | patricia->num_active_node++; 541 | return (node); 542 | } 543 | 544 | addr = prefix_touchar (prefix); 545 | bitlen = prefix->bitlen; 546 | node = patricia->head; 547 | 548 | while (node->bit < bitlen || node->prefix == NULL) { 549 | 550 | if (node->bit < patricia->maxbits && 551 | BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { 552 | if (node->r == NULL) 553 | break; 554 | #ifdef PATRICIA_DEBUG 555 | if (node->prefix) 556 | fprintf (stderr, "patricia_lookup: take right %s/%d\n", 557 | prefix_toa (node->prefix), node->prefix->bitlen); 558 | else 559 | fprintf (stderr, "patricia_lookup: take right at %u\n", node->bit); 560 | #endif /* PATRICIA_DEBUG */ 561 | node = node->r; 562 | } 563 | else { 564 | if (node->l == NULL) 565 | break; 566 | #ifdef PATRICIA_DEBUG 567 | if (node->prefix) 568 | fprintf (stderr, "patricia_lookup: take left %s/%d\n", 569 | prefix_toa (node->prefix), node->prefix->bitlen); 570 | else 571 | fprintf (stderr, "patricia_lookup: take left at %u\n", node->bit); 572 | #endif /* PATRICIA_DEBUG */ 573 | node = node->l; 574 | } 575 | 576 | assert (node); 577 | } 578 | 579 | assert (node->prefix); 580 | #ifdef PATRICIA_DEBUG 581 | fprintf (stderr, "patricia_lookup: stop at %s/%d\n", 582 | prefix_toa (node->prefix), node->prefix->bitlen); 583 | #endif /* PATRICIA_DEBUG */ 584 | 585 | test_addr = prefix_touchar (node->prefix); 586 | /* find the first bit different */ 587 | check_bit = (node->bit < bitlen)? node->bit: bitlen; 588 | differ_bit = 0; 589 | for (i = 0; i*8 < check_bit; i++) { 590 | if ((r = (addr[i] ^ test_addr[i])) == 0) { 591 | differ_bit = (i + 1) * 8; 592 | continue; 593 | } 594 | /* I know the better way, but for now */ 595 | for (j = 0; j < 8; j++) { 596 | if (BIT_TEST (r, (0x80 >> j))) 597 | break; 598 | } 599 | /* must be found */ 600 | assert (j < 8); 601 | differ_bit = i * 8 + j; 602 | break; 603 | } 604 | if (differ_bit > check_bit) 605 | differ_bit = check_bit; 606 | #ifdef PATRICIA_DEBUG 607 | fprintf (stderr, "patricia_lookup: differ_bit %d\n", differ_bit); 608 | #endif /* PATRICIA_DEBUG */ 609 | 610 | parent = node->parent; 611 | while (parent && parent->bit >= differ_bit) { 612 | node = parent; 613 | parent = node->parent; 614 | #ifdef PATRICIA_DEBUG 615 | if (node->prefix) 616 | fprintf (stderr, "patricia_lookup: up to %s/%d\n", 617 | prefix_toa (node->prefix), node->prefix->bitlen); 618 | else 619 | fprintf (stderr, "patricia_lookup: up to %u\n", node->bit); 620 | #endif /* PATRICIA_DEBUG */ 621 | } 622 | 623 | if (differ_bit == bitlen && node->bit == bitlen) { 624 | if (node->prefix) { 625 | #ifdef PATRICIA_DEBUG 626 | fprintf (stderr, "patricia_lookup: found %s/%d\n", 627 | prefix_toa (node->prefix), node->prefix->bitlen); 628 | #endif /* PATRICIA_DEBUG */ 629 | return (node); 630 | } 631 | node->prefix = Ref_Prefix (prefix); 632 | #ifdef PATRICIA_DEBUG 633 | fprintf (stderr, "patricia_lookup: new node #1 %s/%d (glue mod)\n", 634 | prefix_toa (prefix), prefix->bitlen); 635 | #endif /* PATRICIA_DEBUG */ 636 | assert (node->data == NULL); 637 | return (node); 638 | } 639 | 640 | new_node = (patricia_node_t *) calloc(1, sizeof *new_node); 641 | new_node->bit = prefix->bitlen; 642 | new_node->prefix = Ref_Prefix (prefix); 643 | new_node->parent = NULL; 644 | new_node->l = new_node->r = NULL; 645 | new_node->data = NULL; 646 | patricia->num_active_node++; 647 | 648 | if (node->bit == differ_bit) { 649 | new_node->parent = node; 650 | if (node->bit < patricia->maxbits && 651 | BIT_TEST (addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { 652 | assert (node->r == NULL); 653 | node->r = new_node; 654 | } 655 | else { 656 | assert (node->l == NULL); 657 | node->l = new_node; 658 | } 659 | #ifdef PATRICIA_DEBUG 660 | fprintf (stderr, "patricia_lookup: new_node #2 %s/%d (child)\n", 661 | prefix_toa (prefix), prefix->bitlen); 662 | #endif /* PATRICIA_DEBUG */ 663 | return (new_node); 664 | } 665 | 666 | if (bitlen == differ_bit) { 667 | if (bitlen < patricia->maxbits && 668 | BIT_TEST (test_addr[bitlen >> 3], 0x80 >> (bitlen & 0x07))) { 669 | new_node->r = node; 670 | } 671 | else { 672 | new_node->l = node; 673 | } 674 | new_node->parent = node->parent; 675 | if (node->parent == NULL) { 676 | assert (patricia->head == node); 677 | patricia->head = new_node; 678 | } 679 | else if (node->parent->r == node) { 680 | node->parent->r = new_node; 681 | } 682 | else { 683 | node->parent->l = new_node; 684 | } 685 | node->parent = new_node; 686 | #ifdef PATRICIA_DEBUG 687 | fprintf (stderr, "patricia_lookup: new_node #3 %s/%d (parent)\n", 688 | prefix_toa (prefix), prefix->bitlen); 689 | #endif /* PATRICIA_DEBUG */ 690 | } 691 | else { 692 | glue = (patricia_node_t *) calloc(1, sizeof *glue); 693 | glue->bit = differ_bit; 694 | glue->prefix = NULL; 695 | glue->parent = node->parent; 696 | glue->data = NULL; 697 | patricia->num_active_node++; 698 | if (differ_bit < patricia->maxbits && 699 | BIT_TEST (addr[differ_bit >> 3], 0x80 >> (differ_bit & 0x07))) { 700 | glue->r = new_node; 701 | glue->l = node; 702 | } 703 | else { 704 | glue->r = node; 705 | glue->l = new_node; 706 | } 707 | new_node->parent = glue; 708 | 709 | if (node->parent == NULL) { 710 | assert (patricia->head == node); 711 | patricia->head = glue; 712 | } 713 | else if (node->parent->r == node) { 714 | node->parent->r = glue; 715 | } 716 | else { 717 | node->parent->l = glue; 718 | } 719 | node->parent = glue; 720 | #ifdef PATRICIA_DEBUG 721 | fprintf (stderr, "patricia_lookup: new_node #4 %s/%d (glue+node)\n", 722 | prefix_toa (prefix), prefix->bitlen); 723 | #endif /* PATRICIA_DEBUG */ 724 | } 725 | return (new_node); 726 | } 727 | 728 | 729 | void 730 | patricia_remove (patricia_tree_t *patricia, patricia_node_t *node) 731 | { 732 | patricia_node_t *parent, *child; 733 | 734 | assert (patricia); 735 | assert (node); 736 | 737 | if (node->r && node->l) { 738 | #ifdef PATRICIA_DEBUG 739 | fprintf (stderr, "patricia_remove: #0 %s/%d (r & l)\n", 740 | prefix_toa (node->prefix), node->prefix->bitlen); 741 | #endif /* PATRICIA_DEBUG */ 742 | 743 | /* this might be a placeholder node -- have to check and make sure 744 | * there is a prefix aossciated with it ! */ 745 | if (node->prefix != NULL) 746 | Deref_Prefix (node->prefix); 747 | node->prefix = NULL; 748 | /* Also I needed to clear data pointer -- masaki */ 749 | node->data = NULL; 750 | return; 751 | } 752 | 753 | if (node->r == NULL && node->l == NULL) { 754 | #ifdef PATRICIA_DEBUG 755 | fprintf (stderr, "patricia_remove: #1 %s/%d (!r & !l)\n", 756 | prefix_toa (node->prefix), node->prefix->bitlen); 757 | #endif /* PATRICIA_DEBUG */ 758 | parent = node->parent; 759 | Deref_Prefix (node->prefix); 760 | Delete (node); 761 | patricia->num_active_node--; 762 | 763 | if (parent == NULL) { 764 | assert (patricia->head == node); 765 | patricia->head = NULL; 766 | return; 767 | } 768 | 769 | if (parent->r == node) { 770 | parent->r = NULL; 771 | child = parent->l; 772 | } 773 | else { 774 | assert (parent->l == node); 775 | parent->l = NULL; 776 | child = parent->r; 777 | } 778 | 779 | if (parent->prefix) 780 | return; 781 | 782 | /* we need to remove parent too */ 783 | 784 | if (parent->parent == NULL) { 785 | assert (patricia->head == parent); 786 | patricia->head = child; 787 | } 788 | else if (parent->parent->r == parent) { 789 | parent->parent->r = child; 790 | } 791 | else { 792 | assert (parent->parent->l == parent); 793 | parent->parent->l = child; 794 | } 795 | child->parent = parent->parent; 796 | Delete (parent); 797 | patricia->num_active_node--; 798 | return; 799 | } 800 | 801 | #ifdef PATRICIA_DEBUG 802 | fprintf (stderr, "patricia_remove: #2 %s/%d (r ^ l)\n", 803 | prefix_toa (node->prefix), node->prefix->bitlen); 804 | #endif /* PATRICIA_DEBUG */ 805 | if (node->r) { 806 | child = node->r; 807 | } 808 | else { 809 | assert (node->l); 810 | child = node->l; 811 | } 812 | parent = node->parent; 813 | child->parent = parent; 814 | 815 | Deref_Prefix (node->prefix); 816 | Delete (node); 817 | patricia->num_active_node--; 818 | 819 | if (parent == NULL) { 820 | assert (patricia->head == node); 821 | patricia->head = child; 822 | return; 823 | } 824 | 825 | if (parent->r == node) { 826 | parent->r = child; 827 | } 828 | else { 829 | assert (parent->l == node); 830 | parent->l = child; 831 | } 832 | } 833 | 834 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------