Codebase list dns-flood-detector / upstream/1.10
make daniel happy Jan Wagner 17 years ago
11 changed file(s) with 1272 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 GNU GENERAL PUBLIC LICENSE
1 Version 2, June 1991
2
3 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
4 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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 licenses for most software are designed to take away your
11 freedom to share and change it. By contrast, the GNU General Public
12 License is intended to guarantee your freedom to share and change free
13 software--to make sure the software is free for all its users. This
14 General Public License applies to most of the Free Software
15 Foundation's software and to any other program whose authors commit to
16 using it. (Some other Free Software Foundation software is covered by
17 the GNU Library General Public License instead.) You can apply it to
18 your programs, too.
19
20 When we speak of free software, we are referring to freedom, not
21 price. Our General Public Licenses are designed to make sure that you
22 have the freedom to distribute copies of free software (and charge for
23 this service if you wish), that you receive source code or can get it
24 if you want it, that you can change the software or use pieces of it
25 in new free programs; and that you know you can do these things.
26
27 To protect your rights, we need to make restrictions that forbid
28 anyone to deny you these rights or to ask you to surrender the rights.
29 These restrictions translate to certain responsibilities for you if you
30 distribute copies of the software, or if you modify it.
31
32 For example, if you distribute copies of such a program, whether
33 gratis or for a fee, you must give the recipients all the rights that
34 you have. You must make sure that they, too, receive or can get the
35 source code. And you must show them these terms so they know their
36 rights.
37
38 We protect your rights with two steps: (1) copyright the software, and
39 (2) offer you this license which gives you legal permission to copy,
40 distribute and/or modify the software.
41
42 Also, for each author's protection and ours, we want to make certain
43 that everyone understands that there is no warranty for this free
44 software. If the software is modified by someone else and passed on, we
45 want its recipients to know that what they have is not the original, so
46 that any problems introduced by others will not reflect on the original
47 authors' reputations.
48
49 Finally, any free program is threatened constantly by software
50 patents. We wish to avoid the danger that redistributors of a free
51 program will individually obtain patent licenses, in effect making the
52 program proprietary. To prevent this, we have made it clear that any
53 patent must be licensed for everyone's free use or not licensed at all.
54
55 The precise terms and conditions for copying, distribution and
56 modification follow.
57
58 GNU GENERAL PUBLIC LICENSE
59 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
60
61 0. This License applies to any program or other work which contains
62 a notice placed by the copyright holder saying it may be distributed
63 under the terms of this General Public License. The "Program", below,
64 refers to any such program or work, and a "work based on the Program"
65 means either the Program or any derivative work under copyright law:
66 that is to say, a work containing the Program or a portion of it,
67 either verbatim or with modifications and/or translated into another
68 language. (Hereinafter, translation is included without limitation in
69 the term "modification".) Each licensee is addressed as "you".
70
71 Activities other than copying, distribution and modification are not
72 covered by this License; they are outside its scope. The act of
73 running the Program is not restricted, and the output from the Program
74 is covered only if its contents constitute a work based on the
75 Program (independent of having been made by running the Program).
76 Whether that is true depends on what the Program does.
77
78 1. You may copy and distribute verbatim copies of the Program's
79 source code as you receive it, in any medium, provided that you
80 conspicuously and appropriately publish on each copy an appropriate
81 copyright notice and disclaimer of warranty; keep intact all the
82 notices that refer to this License and to the absence of any warranty;
83 and give any other recipients of the Program a copy of this License
84 along with the Program.
85
86 You may charge a fee for the physical act of transferring a copy, and
87 you may at your option offer warranty protection in exchange for a fee.
88
89 2. You may modify your copy or copies of the Program or any portion
90 of it, thus forming a work based on the Program, and copy and
91 distribute such modifications or work under the terms of Section 1
92 above, provided that you also meet all of these conditions:
93
94 a) You must cause the modified files to carry prominent notices
95 stating that you changed the files and the date of any change.
96
97 b) You must cause any work that you distribute or publish, that in
98 whole or in part contains or is derived from the Program or any
99 part thereof, to be licensed as a whole at no charge to all third
100 parties under the terms of this License.
101
102 c) If the modified program normally reads commands interactively
103 when run, you must cause it, when started running for such
104 interactive use in the most ordinary way, to print or display an
105 announcement including an appropriate copyright notice and a
106 notice that there is no warranty (or else, saying that you provide
107 a warranty) and that users may redistribute the program under
108 these conditions, and telling the user how to view a copy of this
109 License. (Exception: if the Program itself is interactive but
110 does not normally print such an announcement, your work based on
111 the Program is not required to print an announcement.)
112
113 These requirements apply to the modified work as a whole. If
114 identifiable sections of that work are not derived from the Program,
115 and can be reasonably considered independent and separate works in
116 themselves, then this License, and its terms, do not apply to those
117 sections when you distribute them as separate works. But when you
118 distribute the same sections as part of a whole which is a work based
119 on the Program, the distribution of the whole must be on the terms of
120 this License, whose permissions for other licensees extend to the
121 entire whole, and thus to each and every part regardless of who wrote it.
122
123 Thus, it is not the intent of this section to claim rights or contest
124 your rights to work written entirely by you; rather, the intent is to
125 exercise the right to control the distribution of derivative or
126 collective works based on the Program.
127
128 In addition, mere aggregation of another work not based on the Program
129 with the Program (or with a work based on the Program) on a volume of
130 a storage or distribution medium does not bring the other work under
131 the scope of this License.
132
133 3. You may copy and distribute the Program (or a work based on it,
134 under Section 2) in object code or executable form under the terms of
135 Sections 1 and 2 above provided that you also do one of the following:
136
137 a) Accompany it with the complete corresponding machine-readable
138 source code, which must be distributed under the terms of Sections
139 1 and 2 above on a medium customarily used for software interchange; or,
140
141 b) Accompany it with a written offer, valid for at least three
142 years, to give any third party, for a charge no more than your
143 cost of physically performing source distribution, a complete
144 machine-readable copy of the corresponding source code, to be
145 distributed under the terms of Sections 1 and 2 above on a medium
146 customarily used for software interchange; or,
147
148 c) Accompany it with the information you received as to the offer
149 to distribute corresponding source code. (This alternative is
150 allowed only for noncommercial distribution and only if you
151 received the program in object code or executable form with such
152 an offer, in accord with Subsection b above.)
153
154 The source code for a work means the preferred form of the work for
155 making modifications to it. For an executable work, complete source
156 code means all the source code for all modules it contains, plus any
157 associated interface definition files, plus the scripts used to
158 control compilation and installation of the executable. However, as a
159 special exception, the source code distributed need not include
160 anything that is normally distributed (in either source or binary
161 form) with the major components (compiler, kernel, and so on) of the
162 operating system on which the executable runs, unless that component
163 itself accompanies the executable.
164
165 If distribution of executable or object code is made by offering
166 access to copy from a designated place, then offering equivalent
167 access to copy the source code from the same place counts as
168 distribution of the source code, even though third parties are not
169 compelled to copy the source along with the object code.
170
171 4. You may not copy, modify, sublicense, or distribute the Program
172 except as expressly provided under this License. Any attempt
173 otherwise to copy, modify, sublicense or distribute the Program is
174 void, and will automatically terminate your rights under this License.
175 However, parties who have received copies, or rights, from you under
176 this License will not have their licenses terminated so long as such
177 parties remain in full compliance.
178
179 5. You are not required to accept this License, since you have not
180 signed it. However, nothing else grants you permission to modify or
181 distribute the Program or its derivative works. These actions are
182 prohibited by law if you do not accept this License. Therefore, by
183 modifying or distributing the Program (or any work based on the
184 Program), you indicate your acceptance of this License to do so, and
185 all its terms and conditions for copying, distributing or modifying
186 the Program or works based on it.
187
188 6. Each time you redistribute the Program (or any work based on the
189 Program), the recipient automatically receives a license from the
190 original licensor to copy, distribute or modify the Program subject to
191 these terms and conditions. You may not impose any further
192 restrictions on the recipients' exercise of the rights granted herein.
193 You are not responsible for enforcing compliance by third parties to
194 this License.
195
196 7. If, as a consequence of a court judgment or allegation of patent
197 infringement or for any other reason (not limited to patent issues),
198 conditions are imposed on you (whether by court order, agreement or
199 otherwise) that contradict the conditions of this License, they do not
200 excuse you from the conditions of this License. If you cannot
201 distribute so as to satisfy simultaneously your obligations under this
202 License and any other pertinent obligations, then as a consequence you
203 may not distribute the Program at all. For example, if a patent
204 license would not permit royalty-free redistribution of the Program by
205 all those who receive copies directly or indirectly through you, then
206 the only way you could satisfy both it and this License would be to
207 refrain entirely from distribution of the Program.
208
209 If any portion of this section is held invalid or unenforceable under
210 any particular circumstance, the balance of the section is intended to
211 apply and the section as a whole is intended to apply in other
212 circumstances.
213
214 It is not the purpose of this section to induce you to infringe any
215 patents or other property right claims or to contest validity of any
216 such claims; this section has the sole purpose of protecting the
217 integrity of the free software distribution system, which is
218 implemented by public license practices. Many people have made
219 generous contributions to the wide range of software distributed
220 through that system in reliance on consistent application of that
221 system; it is up to the author/donor to decide if he or she is willing
222 to distribute software through any other system and a licensee cannot
223 impose that choice.
224
225 This section is intended to make thoroughly clear what is believed to
226 be a consequence of the rest of this License.
227
228 8. If the distribution and/or use of the Program is restricted in
229 certain countries either by patents or by copyrighted interfaces, the
230 original copyright holder who places the Program under this License
231 may add an explicit geographical distribution limitation excluding
232 those countries, so that distribution is permitted only in or among
233 countries not thus excluded. In such case, this License incorporates
234 the limitation as if written in the body of this License.
235
236 9. The Free Software Foundation may publish revised and/or new versions
237 of the General Public License from time to time. Such new versions will
238 be similar in spirit to the present version, but may differ in detail to
239 address new problems or concerns.
240
241 Each version is given a distinguishing version number. If the Program
242 specifies a version number of this License which applies to it and "any
243 later version", you have the option of following the terms and conditions
244 either of that version or of any later version published by the Free
245 Software Foundation. If the Program does not specify a version number of
246 this License, you may choose any version ever published by the Free Software
247 Foundation.
248
249 10. If you wish to incorporate parts of the Program into other free
250 programs whose distribution conditions are different, write to the author
251 to ask for permission. For software which is copyrighted by the Free
252 Software Foundation, write to the Free Software Foundation; we sometimes
253 make exceptions for this. Our decision will be guided by the two goals
254 of preserving the free status of all derivatives of our free software and
255 of promoting the sharing and reuse of software generally.
256
257 NO WARRANTY
258
259 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
260 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
261 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
262 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
263 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
264 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
265 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
266 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
267 REPAIR OR CORRECTION.
268
269 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
270 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
271 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
272 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
273 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
274 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
275 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
276 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
277 POSSIBILITY OF SUCH DAMAGES.
278
279 END OF TERMS AND CONDITIONS
280
0 DNS FLood Detector 1.10
1 Dennis Opacki
2 dopacki@adotout.com
3
4
5 What is DNS Flood Detector?
6
7 DNS Flood Detector was developed to detect abusive usage levels on high
8 traffic nameservers and to enable quick response to the use of one's
9 nameserver to facilitate spam. DNS Flood Detector is distributed under the
10 Gnu Public License (see included LICENSE file for details).
11
12 How does it work?
13
14 DNS Flood Detector uses libpcap (in non-promiscuous mode) to monitor
15 incoming dns queries to a nameserver. The tool may be run in one of two
16 modes, either daemon mode or "bindsnap" mode. In daemon mode, the tool
17 will alarm via syslog. In bindsnap mode, the user is able to get
18 near-real-time stats on usage to aid in more detailed troubleshooting.
19
20 How do I build it?
21
22 Execute ./configure.pl to select the appropriate make target. Then simply
23 type "make".
24
25 Why was it written?
26
27 I wrote DNS Flood Detector because the fifty or so public recursive
28 nameservers I am responsible for were being abused by both customers and
29 non-customers. DNS Flood Detector allows for prompt action when anomalous
30 conditions are detected.
31
32 What do I need to use it?
33
34 You need libpcap and a little bit of patience.
35
36 What platforms does it work on?
37
38 Linux, BSDI, FreeBSD, Mac OSX, Solaris
39
40 Will it run under Windows {95,98,NT,2000,XP}?
41
42 Maybe. I haven't tried. If it doesn't, feel free to submit a fix.
43
44 What does it look like?
45
46 Usage: ./dns_flood_detector [OPTION]
47
48 -i IFNAME specify interface to listen on
49 -t N alarm at >N queries per second
50 -a N reset alarm after N seconds
51 -w N calculate stats every N seconds
52 -x N create N buckets
53 -m N mark total query rate every N seconds
54 -b run in foreground in bindsnap mode
55 -d run in background in daemon mode
56 -v verbose output - use again for more verbosity
57 -h display this usage information
58
59 Sample Output:
60
61 dopacki:~$ sudo ./dns_flood_detector -v -v -b -t10
62 [15:14:56] source [192.168.1.45] - 0 qps tcp : 24 qps udp [8 qps A] [16
63 qps PTR]
64 [15:14:56] source [10.0.24.2] - 0 qps tcp : 15 qps udp [15 qps A]
65 [15:15:06] source [192.168.1.45] - 0 qps tcp : 24 qps udp [8 qps A] [16
66 qps PTR]
67 [15:15:06] source [10.0.24.2] - 0 qps tcp : 15 qps udp [14 qps A]
68 [15:15:16] source [192.168.1.45] - 0 qps tcp : 23 qps udp [7 qps A] [15
69 qps PTR]
70
71 What if I have questions?
72
73 You can e-mail me at dopacki@adotout.com
0 #!/usr/bin/perl
1
2 use strict;
3
4 my $os = shift;
5
6 # get target listings
7 opendir(MAKE_TARGETS,'./makefiles');
8 my @targets = grep { /Makefile/ && -f './makefiles/'.$_ && s/^Makefile-// } readdir(MAKE_TARGETS);
9 closedir(MAKE_TARGETS);
10
11 # display usage
12 unless ($os && grep{/$os/}@targets) {print<<EOF;exit(0)}
13 usage: $0 {@targets}
14 EOF
15
16 # link appropriate target
17 symlink './makefiles/Makefile-'.$os, 'Makefile';
18 print "type make.\n";
0 /********************************************************************************
1
2 Program: dns_flood_detector.c
3 Author: Dennis Opacki <dopacki@adotout.com>
4 Date: Tue Mar 18 16:46:53 EST 2003
5 Purpose: Monitor DNS servers for abusive usage levels
6 and alarm to syslog
7
8 compile with:
9 gcc -o dns_flood_detector -lpcap -lpthread -lm dns_flood_detector.c
10
11 command-line options:
12
13 -i ifname specify interface to listen on (default lets pcap pick)
14 -t n alarm when more than n queries per second are observed
15 (default 40)
16 -a n wait for n seconds before alarming again on same source
17 (default 90)
18 -w n calculate statistics every n seconds
19 (default 10)
20 -x n use n buckets
21 (default 50)
22 -m n mark overall query rate every n seconds
23 (default disabled)
24 -b run in foreground in "bindsnap" mode
25 -d run in background in "daemon" mode
26 -v detailed information (use twice for more detail)
27 -h usage info
28
29 Copyright (C) 2003 Dennis Opacki
30
31 This program is free software; you can redistribute it and/or modify
32 it under the terms of the GNU General Public License as published by
33 the Free Software Foundation; either version 2 of the License, or
34 (at your option) any later version.
35
36 This program is distributed in the hope that it will be useful,
37 but WITHOUT ANY WARRANTY; without even the implied warranty of
38 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 GNU General Public License for more details.
40
41 You should have received a copy of the GNU General Public License
42 along with this program; if not, write to the Free Software
43 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
44
45 --- new in v1.05 ---
46 8/18/2003 - FreeBSD target - Jim Westfall <jwestfall@surrealistic.net>
47 8/18/2003 - Moved to getopt(3) for compatibility <dopacki@adotout.com>
48 8/19/2003 - Added OSX/BSDI make targets - <dopacki@adotout.com>
49 Added ability to specify inteface - <dopacki@adotout.com>
50
51 --- new in v1.06 ---
52 8/20/2003 - Added Solaris9 make target - <dopacki@adotout.com>
53 8/26/2003 - Fixed tcp qdcount bug - <dopacki@adotout.com>
54
55 --- new in v1.07 ---
56 8/27/2003 - Fixed alarm reset bug - <dopacki@adotout.com>
57 8/28/2003 - Added malloc_fail function - <dopacki@adotout.com>
58 8/28/2003 - Added mutex thread locking - <dopacki@adotout.com>
59 8/30/2003 - Fixed wierd qtype segfault - <jwestfall@surrealistic.net>
60 <dopacki@adotout.com>
61
62 --- new in v1.08 ---
63 9/02/2003 - Added -v -v output in daemon mode - <dopacki@adotout.com>
64
65 --- new in v1.09 ---
66 10/19/2003 - Added stdout flushing to bindsnap mode - <dopacki@adotout.com>
67 10/19/2003 - Changed logging priority to LOG_NOTICE - <dopacki@adotout.com>
68 10/19/2003 - Fixed low traffic verbose logging bugs - <dopacki@adotout.com>
69
70 --- new in v1.10 ---
71 10/22/2003 - Added 'mark status' option via '-m' - <dopacki@adotout.com>
72 10/23/2003 - Code cleanup in verbose syslogging - <dopacki@adotout.com>
73
74 ********************************************************************************/
75
76 #include <pcap.h>
77 #include <stdio.h>
78 #include <stdlib.h>
79 #include <fcntl.h>
80 #include <errno.h>
81 #include <netinet/in_systm.h>
82 #include <netinet/in.h>
83 #include <netinet/ip.h>
84 #include <netinet/tcp.h>
85 #include <netinet/udp.h>
86 #ifdef __bsdi__
87 #include <net/if_ethernet.h>
88 #else
89 #ifdef __sun__
90 #include <sys/ethernet.h>
91 #else
92 #include <net/ethernet.h>
93 #endif
94 #endif
95 #include <pthread.h>
96 #include <unistd.h>
97 #include <time.h>
98 #include <math.h>
99 #include <signal.h>
100 #include <syslog.h>
101 #include "dns_flood_detector.h"
102
103 // global variables and their defaults
104 pthread_mutex_t stats_lock;
105 struct bucket **bb;
106 int option_t = 60;
107 int option_a = 90;
108 int option_w = 10;
109 int option_x = 50;
110 int option_m = 0;
111 int option_b = 0;
112 int option_d = 0;
113 int option_v = 0;
114 int option_h = 0;
115 int totals = 0;
116 char VERSION[] = "1.10";
117
118 // this is our statistics thread
119 void *run_stats () {
120 while (1) {
121
122 // check statistical stuff
123 pthread_mutex_lock(&stats_lock);
124 calculate_averages();
125 pthread_mutex_unlock(&stats_lock);
126
127 sleep (option_w);
128 }
129 }
130
131 // calculate the running average within each bucket
132 int calculate_averages() {
133 u_int i,j,delta,cursize,newsize,qps;
134 char st_time[10];
135 time_t now = time(0);
136 u_int types[] = {1,2,5,6,12,15,252,0};
137 u_char *type;
138 u_char *target;
139 char *names[] = {"A","NS","CNAME","SOA","PTR","MX","AXFR",""};
140 struct tm *raw_time = localtime(&now);
141 snprintf(st_time, 9, "%02d:%02d:%02d",raw_time->tm_hour,raw_time->tm_min,raw_time->tm_sec);
142
143 for (i=0; i<option_x; i++) {
144
145 // only process valid buckets
146 if ( bb[i]->ip_addr != NULL ) {
147 delta = now - bb[i]->first_packet;
148
149 // let's try to avoid a divide-by-zero, shall we?
150 if (delta > 1 ) {
151
152 // round our average and save it in the bucket
153 bb[i]->qps = (int)ceil( (float)((((float)bb[i]->tcp_count) + bb[i]->udp_count) / delta));
154
155 // handle threshold crossing
156 if ( bb[i]->qps > option_t ) {
157
158
159 // display detail to either syslog or stdout
160 if ( option_b ) {
161 if ( ! option_v ) {
162 printf("[%s] source [%s] - %d qps\n",st_time,bb[i]->ip_addr,bb[i]->qps);
163 fflush(stdout);
164 }
165 else {
166 printf("[%s] source [%s] - %d qps tcp : %d qps udp ",st_time,bb[i]->ip_addr,
167 (int)ceil( (float)(bb[i]->tcp_count/delta)),
168 (int)ceil( (float)(bb[i]->udp_count/delta))
169 );
170 if ( option_v >1 ) {
171 for (j=0;types[j];j++) {
172 if ((int)ceil((float)(bb[i]->qstats[types[j]]/delta))){
173 printf("[%d qps %s] ",(int)ceil((float)(bb[i]->qstats[types[j]]/delta)),names[j]);
174 }
175 }
176 }
177 printf("\n");
178 fflush(stdout);
179 }
180 }
181 else {
182 // if running in background, use alarm reset timer
183 if ((now-bb[i]->alarm_set)>option_a) {
184
185 // display appropriate level of detail via syslog
186 if ( ! option_v ) {
187 syslog(LOG_NOTICE,"source [%s] - %d qps\n",bb[i]->ip_addr,bb[i]->qps);
188 }
189 else if (option_v > 1) {
190 target = (char *)malloc(sizeof(char)*MAXSYSLOG);
191 newsize = MAXSYSLOG;
192 cursize = snprintf(target,newsize,"source [%s] - %d tcp qps : %d udp qps ",bb[i]->ip_addr,
193 (int)ceil( (float)(bb[i]->tcp_count/delta)),
194 (int)ceil( (float)(bb[i]->udp_count/delta))
195 );
196 newsize-=cursize;
197
198 for (j=0;types[j];j++ ) {
199 qps = (u_int)ceil((float)(bb[i]->qstats[types[j]]/delta));
200 if ( ( qps > 0) && ( newsize > 1 ) ) {
201 cursize = snprintf(target+(MAXSYSLOG-newsize),newsize,"[%d qps %s] ",qps,names[j]);
202 newsize-=cursize;
203 }
204 }
205 if (newsize <= 0 ) {
206 target[MAXSYSLOG-1]='\0';
207 }
208 syslog(LOG_NOTICE,"%s",target);
209 free(target);
210 }
211 else {
212 syslog(LOG_NOTICE,"source [%s] - %d tcp qps - %d udp qps\n",bb[i]->ip_addr,
213 (int)ceil( (float)(bb[i]->tcp_count/delta)),
214 (int)ceil( (float)(bb[i]->udp_count/delta))
215 );
216 }
217
218 // reset alarm
219 bb[i]->alarm_set = now;
220 }
221 }
222 }
223 }
224 }
225 }
226
227 // 'mark stats' if required and it is time
228 delta = now - bb[totals]->first_packet;
229 if ( (option_m > 0)&&(delta > 1)&&(delta >= option_m) ) {
230
231 // handle bindsnap mode
232 if (option_b) {
233 printf("[%s] totals - %d qps tcp : %d qps udp ",st_time,(int)ceil( (float)(bb[totals]->tcp_count/delta)),(int)ceil( (float)(bb[totals]->udp_count/delta)));
234 if (option_v) {
235 for (j=0;types[j];j++) {
236 qps = (u_int)ceil((float)(bb[totals]->qstats[types[j]]/delta));
237 if (qps){
238 printf("[%d qps %s] ",qps,names[j]);
239 }
240 }
241 }
242 printf("\n");
243 fflush(stdout);
244 }
245 else {
246 // agonizing high verbosity code
247 if (option_v) {
248 target = (char *)malloc(sizeof(char)*MAXSYSLOG);
249 newsize = MAXSYSLOG;
250 cursize = snprintf(target,newsize,"[totals] - %d tcp qps : %d udp qps ",
251 (int)ceil( (float)(bb[totals]->tcp_count/delta)),
252 (int)ceil( (float)(bb[totals]->udp_count/delta))
253 );
254 newsize-=cursize;
255
256 for (j=0;types[j];j++ ) {
257 qps = (u_int)ceil((float)(bb[totals]->qstats[types[j]]/delta));
258 if ( ( qps > 0) && ( newsize > 1 ) ) {
259 cursize = snprintf(target+(MAXSYSLOG-newsize),newsize,"[%d qps %s] ",qps,names[j]);
260 newsize-=cursize;
261 }
262 }
263 if (newsize <= 0 ) {
264 target[MAXSYSLOG-1]='\0';
265 }
266 syslog(LOG_NOTICE,"%s",target);
267 free(target);
268 }
269 else {
270 syslog(LOG_NOTICE,"[totals] - %d tcp qps : %d udp qps\n",
271 (int)ceil( (float)(bb[totals]->tcp_count/delta)),
272 (int)ceil( (float)(bb[totals]->udp_count/delta))
273 );
274 }
275 }
276 scour_bucket(totals);
277 }
278
279 return 1;
280 }
281
282 // purge and initialize all buckets
283 void init_buckets() {
284 u_int i;
285
286 // create bucket brigade (final bucket is for totals)
287 pthread_mutex_lock(&stats_lock);
288 if ( ( bb = malloc( sizeof(struct bucket *) * (option_x+1)) ) == NULL ) malloc_fail("bb", sizeof(struct bucket *) * (option_x+1));
289 for (i=0; i <=option_x; i++ ) {
290 if ( ( bb[i] = (struct bucket *)malloc( sizeof(struct bucket) ) ) == NULL) malloc_fail("bb[i]", sizeof(struct bucket) );
291 bb[i]->ip_addr=NULL;
292 scour_bucket(i);
293 }
294 pthread_mutex_unlock(&stats_lock);
295 }
296
297 // clean out a bucket while avoiding obvious memory leak
298 int scour_bucket( int i ) {
299 int j;
300
301 if ( bb[i]->ip_addr != NULL ) {
302 free ( bb[i]->ip_addr );
303 }
304 bb[i]->ip_addr=NULL;
305 bb[i]->tcp_count=0;
306 bb[i]->udp_count=0;
307 bb[i]->qps=0;
308 bb[i]->first_packet=time(0);
309 bb[i]->last_packet=(time_t)0;
310 bb[i]->alarm_set=(time_t)0;
311
312 for (j=0;j<256;j++) {
313 bb[i]->qstats[j]=0;
314 }
315 return 1;
316 }
317
318 // add a packet to a bucket
319 int add_to_bucket ( char * ip_src, int ip_proto, int num_queries, u_int8_t qtype) {
320 int bucket = 0;
321
322 // get the bucket to put packet in
323 pthread_mutex_lock(&stats_lock);
324 bucket = find_bucket(ip_src);
325
326 // set bucket fields
327 bb[bucket]->last_packet = time(0);
328 if (ip_proto == 6 ) {
329 bb[bucket]->tcp_count+=num_queries;
330 bb[totals]->tcp_count+=num_queries;
331 }
332 else {
333 bb[bucket]->udp_count+=num_queries;
334 bb[totals]->udp_count+=num_queries;
335 }
336
337 bb[bucket]->qstats[qtype]+=num_queries;
338 bb[totals]->qstats[qtype]+=num_queries;
339 pthread_mutex_unlock(&stats_lock);
340
341 return 1;
342 }
343
344 // figure out where to put this packet
345 int find_bucket(char *ip_src) {
346 int i, bucket=0;
347 time_t oldest=0;
348
349 // look for an existing bucket for this IP
350 for (i=0; i< option_x; i++ ){
351 // ip field of bucket is not null and seems to match the ip we are checking
352 if ((bb[i]->ip_addr != NULL)&&(strncmp(bb[i]->ip_addr, ip_src, strlen(bb[i]->ip_addr))==0)) {
353 return i;
354 }
355 }
356
357 // look for unused buckets
358 for (i=0; i< option_x; i++ ) {
359
360 // found an unused one - clean it, init it, and return it
361 if ( bb[i]->ip_addr == NULL ) {
362 scour_bucket(i);
363 if ( ( bb[i]->ip_addr = (char *)strdup(ip_src) ) == NULL) malloc_fail("bb[i]->ip_addr", strlen(ip_src) );
364 return i;
365 }
366
367 // find the most stagnant bucket in case we need it
368 // avoids another loop through the buckets
369 if ( ( bb[i]->last_packet != 0 ) && ((oldest==0)||( bb[i]->last_packet < oldest))) {
370 oldest = bb[i]->last_packet;
371 bucket = i;
372 }
373 }
374
375 // use the most stagnant bucket since all are in use
376 // clean it, init it, and return it
377 scour_bucket(bucket);
378 if ( ( bb[bucket]->ip_addr = (char *)strdup(ip_src) ) == NULL) malloc_fail("bb[bucket]->ip_addr", strlen(ip_src) );
379
380 return bucket;
381 }
382
383 // handle all packets we throw at it
384 void handle_IP(u_char *args, const struct pcap_pkthdr* pkthdr,const u_char* packet){
385 const struct ip* ip;
386 const struct my_dns *dns;
387 const struct tcphdr *tcp;
388 const struct udphdr *udp;
389 u_int length = pkthdr->len;
390 u_int caplen = pkthdr->caplen;
391 u_int hlen,off,version;
392 unsigned char dname[NS_MAXDNAME]="";
393 char *ip_src;
394 unsigned char *data;
395 u_int i,len,dpos;
396 u_int8_t qtype,qclass,tlen;
397
398 // skip the ethernet header
399 length -= sizeof(struct ether_header);
400
401 // make sure packet is a valid length
402 if (length < sizeof(struct ip)) {
403 return;
404 }
405
406 // snap off the ip portion
407 ip = (struct ip*)(packet + sizeof(struct ether_header));
408
409 // get utility params for sanity checking
410 len = ntohs(ip->ip_len);
411 hlen = ip->ip_hl;
412 version = ip->ip_v;
413
414 // let's not do ipv6 just yet
415 if(version != 4) {
416 return;
417 }
418
419 // make sure we have a sane header length
420 if(hlen < 5 ) {
421 return;
422 }
423
424 // do we have the everything we are supposed to?
425 if(length < len) {
426 return;
427 }
428
429 // make sure we are only processing the first fragment
430 off = ntohs(ip->ip_off);
431 if((off & 0x1fff) == 0 ) {
432
433 // get the source ip as a string (probably more efficient to use decimal)
434 ip_src = (char *)inet_ntoa(ip->ip_src);
435
436 // process udp packets
437 if ( ip->ip_p == 17 ) {
438 udp = (struct udphdr *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) );
439
440 // try to make sure it is safe to cast packet into dns structure
441 if ( (sizeof(struct my_dns)+sizeof(struct ether_header)+sizeof(struct ip)+sizeof(struct udphdr)) >= caplen ) {
442 return;
443 }
444 else {
445 // populate dns header
446 dns = (struct my_dns *) ( (char *) packet + sizeof(struct ether_header) + sizeof (struct ip) + sizeof (struct udphdr) );
447 data = (char *) packet +sizeof(struct ether_header) + sizeof (struct ip) + sizeof (struct udphdr) + sizeof(struct my_dns);
448 }
449 }
450
451 // process tcp packets
452 else if ( ip->ip_p == 6 ) {
453 tcp = (struct tcphdr *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) );
454
455 // ignore packets without push flag set
456 if (! tcp->th_flags & TH_PUSH) return;
457
458 // try to make sure it is safe to cast packet into dns structure
459 if ( (sizeof(struct my_dns)+sizeof(struct ether_header)+sizeof(struct ip)+(tcp->th_off * sizeof(u_int32_t))) >= caplen ) {
460 return;
461 }
462 else {
463 // populate dns header
464 dns = (struct my_dns *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) + (tcp->th_off * sizeof(u_int32_t)));
465 data = (char *) packet + sizeof(struct ether_header) + sizeof (struct ip) + (tcp->th_off * sizeof(u_int32_t)) + sizeof(struct my_dns);
466 }
467 }
468
469 // hmm.. not tcp, not udp.. move on.
470 else {
471 return;
472 }
473
474 // we only want queries, not responses
475 if ( dns->dns_flags1 & 0x80 ) {
476 return;
477 }
478
479 // ignore seemingly bogus queries with multiple flags set
480 if ((ntohs(dns->dns_qdcount)>0)+(ntohs(dns->dns_ancount)>0)+(ntohs(dns->dns_nscount)>0)+(ntohs(dns->dns_arcount)>0)>1 ) {
481 return;
482 }
483
484 // get the domain name and query type
485 tlen=dpos=0;
486 for (;(*data)&&((void *)data<((void *)packet+caplen-1)); data++) {
487 if (!tlen) tlen=*data;
488 for (;(tlen&&((void *)data<((void *)packet+caplen-1)));tlen--){
489 data++;
490 if (dpos<NS_MAXDNAME) dname[dpos++] = *data;
491 }
492 if (dpos<NS_MAXDNAME) dname[dpos++] = '.';
493 }
494 dname[dpos]='\0';
495
496 // be careful not to walk past the end of the captured data
497 if ( (void *)data < ((void *)packet+caplen-3) ) {
498 data+=2;
499 qtype = *data;
500 }
501 else {
502 return;
503 }
504
505 // add packet to bucket array
506 if (ntohs(dns->dns_qdcount)&&qtype) {
507 add_to_bucket( ip_src, ip->ip_p, 1, qtype );
508 }
509 }
510 return;
511 }
512
513 // main logic
514 // some pcap code borrowed from http://www.cet.nau.edu/~mc8/Socket/Tutorials/section1.html
515 int main(int argc,char **argv){
516 char *dev = NULL;
517 pthread_t thread;
518 char errbuf[PCAP_ERRBUF_SIZE];
519 pcap_t* descr;
520 struct bpf_program fp; /* hold compiled program */
521 bpf_u_int32 maskp=0; /* subnet mask */
522 bpf_u_int32 netp=0; /* ip */
523 char *filter = NULL;
524 char *dst_addr = NULL;
525 char *dst_mask = NULL;
526 struct sigaction sa;
527 struct in_addr addr;
528 u_int f_size;
529 char *args = NULL;
530 u_int c = 0;
531
532 // loop through command line options and get options
533 while(1) {
534 int option_index = 0;
535 c = getopt(argc, argv,"i:t:a:w:x:m:bdvh");
536
537 if (c==-1) break;
538 switch(c) {
539 case 0:
540 break;
541 case 'i':
542 if (optarg) {
543 if ( ( dev = (char *)strdup(optarg) ) == NULL) malloc_fail("dev", strlen(optarg) );
544 }
545 break;
546 case 't':
547 if (optarg) {
548 if ( abs (atoi(optarg)) > 0) {
549 option_t = abs( atoi(optarg));
550 }
551 }
552 break;
553 case 'a':
554 if (optarg) {
555 if ( abs (atoi(optarg)) > 10) {
556 option_a = abs( atoi(optarg));
557 }
558 }
559 break;
560 case 'w':
561 if (optarg) {
562 if ( abs (atoi(optarg)) > 1) {
563 option_w = abs( atoi(optarg));
564 }
565 }
566 break;
567 case 'x':
568 if (optarg) {
569 if ( abs (atoi(optarg)) > 10) {
570 option_x = abs( atoi(optarg));
571 }
572 }
573 break;
574 case 'm':
575 if (optarg) {
576 if ( abs (atoi(optarg)) > 0) {
577 option_m = abs( atoi(optarg));
578 }
579 }
580 break;
581 case 'b':
582 option_b = 1;
583 break;
584 case 'd':
585 option_d = 1;
586 break;
587 case 'v':
588 option_v++;
589 break;
590 case 'h':
591 option_h = 1;
592 default:
593 break;
594 }
595 }
596
597 // display usage info if needed
598 if (optind<argc) option_h = 1;
599 if (option_h) {
600 fprintf(stderr,"dns_flood_detector, version %s\n",VERSION);
601 fprintf(stderr,"Usage: %s [OPTION]\n\n",argv[0]);
602 fprintf(stderr,"-i IFNAME specify device name to listen on\n");
603 fprintf(stderr,"-t N alarm at >N queries per second\n");
604 fprintf(stderr,"-a N reset alarm after N seconds\n");
605 fprintf(stderr,"-w N calculate stats every N seconds\n");
606 fprintf(stderr,"-x N create N buckets\n");
607 fprintf(stderr,"-m N report overall stats every N seconds\n");
608 fprintf(stderr,"-b run in foreground in bindsnap mode\n");
609 fprintf(stderr,"-d run in background in daemon mode\n");
610 fprintf(stderr,"-v verbose output - use again for more verbosity\n");
611 fprintf(stderr,"-h display this usage information\n");
612 exit(1);
613 }
614
615 if ( ( ! option_d ) && ( ! option_b ) ) {
616 fprintf(stderr,"%s couldn't start\n",argv[0]);
617 fprintf(stderr,"You must specify either either -d (daemon) or -b (bindsnap)\n");
618 exit(1);
619 }
620 // set up for daemonized operation unless running in bindsnap mode
621 if ( ! option_b ) {
622 openlog("dns_flood_detector",LOG_PID|LOG_CONS,LOG_DAEMON);
623 syslog(LOG_NOTICE,"dns_flood_detector starting");
624
625 // daemonize unless running in bindsnap mode
626 daemonize();
627
628 // set up signal handlers
629 sa.sa_handler=exit;
630 sa.sa_flags=0;
631 if(sigaction(SIGTERM,&sa,NULL)) {
632 syslog(LOG_ERR,"Unable to set signal handler: %s. Exiting.",
633 strerror(errno));
634 }
635 }
636
637 // find a valid device to open
638 if(dev == NULL && ( (dev=pcap_lookupdev(errbuf)) == NULL ) ){
639 fprintf(stderr,"unable to bind to valid device\n");
640 exit(1);
641 }
642
643 // get network address and netmask for device
644 pcap_lookupnet(dev,&netp,&maskp,errbuf);
645
646 // set up filter with local network
647 addr.s_addr = (unsigned long int)netp;
648 if ( ( dst_addr = (char *)malloc( strlen((char *)inet_ntoa(addr))+1) ) == NULL ) malloc_fail("dest_addr", strlen((char *)inet_ntoa(addr))+1 );
649 strncpy(dst_addr,(char*)inet_ntoa(addr),strlen((char *)inet_ntoa(addr)));
650 dst_addr[strlen((char *)inet_ntoa(addr))]='\0';
651
652 addr.s_addr = (unsigned long int)maskp;
653 if ( ( dst_mask = (char *)malloc( strlen((char *)inet_ntoa(addr))+1) ) == NULL ) malloc_fail("dest_mask", strlen((char *)inet_ntoa(addr))+1 );
654 strncpy(dst_mask,(char*)inet_ntoa(addr),strlen((char *)inet_ntoa(addr)));
655 dst_mask[strlen((char *)inet_ntoa(addr))]='\0';
656
657 f_size = strlen("port 53 and dst net mask ")+ strlen(dst_mask)+ strlen(dst_addr);
658 if ( ( filter = (char *) malloc ( f_size+1) ) == NULL ) malloc_fail( "filter", f_size+1 );
659 snprintf( filter, f_size, "port 53 and dst net %s mask %s", dst_addr, dst_mask);
660
661 free (dst_mask);
662 free (dst_addr);
663
664 // open device for reading only local traffic
665 descr = pcap_open_live(dev,1500,0,1,errbuf);
666 if(descr == NULL) {
667 fprintf(stderr,"unable to open device %s\n",dev);
668 exit(1);
669 }
670
671 // compile filter
672 if(pcap_compile(descr,&fp,filter,0,netp) == -1) {
673 exit(1);
674 }
675
676 // set filter
677 if(pcap_setfilter(descr,&fp) == -1){
678 exit(1);
679 }
680
681 // initialize buckets and mark overall stats bucket
682 init_buckets();
683 totals = option_x;
684
685 // create mutex lock
686 if (pthread_mutex_init(&stats_lock, NULL) < 0) {
687 exit(1);
688 }
689
690 // launch watcher thread
691 if (pthread_create (&thread, NULL, run_stats, (void *)0)) {
692 exit(1);
693 }
694
695 // main pcap loop
696 pcap_loop(descr,-1,handle_IP,args);
697
698 // done
699 closelog();
700 return 0;
701 }
702
703 // daemonize the process
704 int daemonize(void) {
705 pid_t pid;
706 int fd;
707
708 fd=open("/dev/null",O_RDWR);
709 if(fd<0) {
710 syslog(LOG_ERR,"Failed to open /dev/null: %s. Exiting.",strerror(errno));
711 exit(1);
712 }
713
714 dup2(fd,0);
715 dup2(fd,1);
716 dup2(fd,2);
717
718 if((pid=fork())<0) {
719 syslog(LOG_ERR,"Fork failed: %s. Exiting.",strerror(errno));
720 exit(1);
721 }
722 else if (pid!=0) {
723 exit(0);
724 }
725
726 setsid();
727 chdir("/");
728 umask(0);
729 return 0;
730 }
731
732 int malloc_fail( char * var, int size ) {
733 // print error to stderr if running in bindsnap mode
734 if (option_b) {
735 fprintf(stderr, "our OS wouldn't let me malloc %d bytes for a new %s. giving up", size, var);
736 }
737 else {
738 syslog(LOG_ERR, "our OS wouldn't let me malloc %d bytes for a new %s. giving up", size, var);
739 }
740 exit(1);
741 }
0 /******************************************************************************
1
2 Program: dns_flood_detector.h
3 Author: Dennis Opacki <dopacki@adotout.com>
4 Date: Tue Mar 18 16:46:53 EST 2003
5 Purpose: Monitor DNS servers for abusive usage levels
6 and alarm to syslog
7
8 Copyright (C) 2003 Dennis Opacki
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
24 *******************************************************************************/
25
26 // definitions
27 #ifndef ETHER_HDRLEN
28 #define ETHER_HDRLEN 14
29 #endif
30 #define NS_MAXDNAME 1025
31 #define MAXSYSLOG 128
32
33 // evil Solaris hack
34 #ifdef __sun__
35 typedef uint8_t u_int8_t;
36 typedef uint16_t u_int16_t;
37 typedef uint32_t u_int32_t;
38 #endif
39
40 // prototypes
41 void handle_IP(u_char *args,const struct pcap_pkthdr* pkthdr,const u_char* packet);
42
43 // data structures
44 struct my_dns {
45 u_int16_t dns_id; /* query identification number */
46 u_int8_t dns_flags1; /* first byte of flags */
47 u_int8_t dns_flags2; /* second byte of flags */
48 u_int16_t dns_qdcount; /* number of question entries */
49 u_int16_t dns_ancount; /* number of answer entries */
50 u_int16_t dns_nscount; /* number of authority entries */
51 u_int16_t dns_arcount; /* number of resource entries */
52 };
53
54 struct bucket {
55 char * ip_addr;
56 unsigned int tcp_count;
57 unsigned int udp_count;
58 unsigned int qps;
59 int qstats[256];
60 time_t first_packet;
61 time_t last_packet;
62 time_t alarm_set;
63 };
64
0 #! /bin/sh
1
2 PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
3
4 test -f /usr/local/sbin/dns_flood_detector || exit 0
5
6 case "$1" in
7 start)
8 echo -n "Starting DNS flood detector: dns_flood_detector"
9 start-stop-daemon --start --quiet --exec /usr/local/sbin/dns_flood_detector -- -d
10 echo "."
11 ;;
12 stop)
13 echo -n "Stopping DNS flood detector: dns_flood_detector"
14 start-stop-daemon --stop --quiet --exec /usr/local/sbin/dns_flood_detector
15 killall dns_flood_detector
16 echo "."
17 ;;
18 restart|force-reload)
19 echo -n "Restarting DNS flood detector: dns_flood_detector... "
20 start-stop-daemon --stop --quiet --exec /usr/local/sbin/dns_flood_detector
21 sleep 2
22 start-stop-daemon --stop --quiet --exec /usr/local/sbin/dns_flood_detector
23 sleep 4
24 killall dns_flood_detector
25 sleep 2
26 start-stop-daemon --start --quiet --exec /usr/local/sbin/dns_flood_detector -- -d
27 echo "done."
28 ;;
29 *)
30 echo "Usage: /etc/init.d/dnsflood {start|stop|restart|force-reload}"
31 exit 1
32 ;;
33 esac
34
35 exit 0
0 CFLAGS+=-O -g
1 LDLIBS=-lpcap -pthread -lm
2
3 all: dns_flood_detector
4 strip dns_flood_detector
5 clean:
6 rm -rf dns_flood_detector *.o *~
7 install:
8 cp dns_flood_detector /usr/local/sbin/
9
10 dns_flood_detector: dns_flood_detector.c
0 CFLAGS+=-O -g
1 LDLIBS=-lpcap -pthread -lm
2
3 all: dns_flood_detector
4 strip dns_flood_detector
5 clean:
6 rm -rf dns_flood_detector *.o *~
7 install:
8 cp dns_flood_detector /usr/local/sbin/
9
10 dns_flood_detector: dns_flood_detector.c
0 CFLAGS=-O -D_BSD_SOURCE -g
1 LDLIBS=-lpcap -lpthread -lm
2
3 all: dns_flood_detector
4 strip dns_flood_detector
5 clean:
6 rm -rf dns_flood_detector *.o *~
7 install:
8 cp dns_flood_detector /usr/local/sbin/
9
10 dns_flood_detector: dns_flood_detector.c
0 CFLAGS+=-O -g -I/usr/local/include -I/usr/include
1 LDLIBS=-L/usr/local/lib -lpcap -lpthread -lm
2
3 all: dns_flood_detector
4 strip dns_flood_detector
5 clean:
6 rm -rf dns_flood_detector *.o *~
7 install:
8 cp dns_flood_detector /usr/local/sbin/
9
10 dns_flood_detector: dns_flood_detector.c
0 CFLAGS+=-O -g -I/usr/local/include -I/usr/include
1 LDLIBS=-L/usr/local/lib -L/usr/lib -lpcap -lpthread -lm -lsocket -lnsl
2
3 all: dns_flood_detector
4 strip dns_flood_detector
5 clean:
6 rm -rf dns_flood_detector *.o *~
7 install:
8 cp dns_flood_detector /usr/local/sbin/
9
10 dns_flood_detector: dns_flood_detector.c