Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b11db84072 | |||
| ad0b0d6910 | |||
| e3509c3152 | |||
| 9f8f19dbfe | |||
| a224104188 | |||
| 7de356fa85 | |||
| fd186fe50a | |||
| 67a36b150d | |||
| 62417c9201 | |||
| 5c36b6fea4 | |||
| 15ec840544 | |||
| 782debcd74 | |||
| ddb8297bc3 | |||
| 6569e7e577 | |||
| d439dbc26d | |||
| b97b4b212e |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.o
|
||||
denyip
|
||||
17
.vscode/c_cpp_properties.json
vendored
Normal file
17
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "gnu++17",
|
||||
"intelliSenseMode": "linux-gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
5
.vscode/extensions.json
vendored
Normal file
5
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"alibaba-cloud.tongyi-lingma"
|
||||
]
|
||||
}
|
||||
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@@ -1,13 +1,10 @@
|
||||
{
|
||||
"C_Cpp.errorSquiggles": "disabled",
|
||||
"files.associations": {
|
||||
"string.h": "c",
|
||||
"stdlib.h": "c",
|
||||
"ip2region.h": "c",
|
||||
"common.h": "c",
|
||||
"prctl.h": "c",
|
||||
"resource.h": "c",
|
||||
"wait.h": "c",
|
||||
"stdio.h": "c"
|
||||
"algorithm": "c",
|
||||
"cstdlib": "c",
|
||||
"libipset.h": "c"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -5,18 +5,22 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IPInfo struct {
|
||||
Code string `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data struct {
|
||||
Continent string `json:"continent"`
|
||||
Country string `json:"country"`
|
||||
} `json:"data"`
|
||||
IP string `json:"ip"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
func isValidIP(ip string) bool {
|
||||
return net.ParseIP(ip) != nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -24,16 +28,29 @@ func main() {
|
||||
log.Fatalf("用法: %s <IP>", os.Args[0])
|
||||
}
|
||||
|
||||
ip := os.Args[1]
|
||||
if !isValidIP(ip) {
|
||||
log.Fatalf("无效的 IP 地址: %s", ip)
|
||||
}
|
||||
|
||||
// 目标 URL
|
||||
url := "https://qifu.baidu.com/ip/geo/v1/district?ip=" + os.Args[1]
|
||||
url := "https://qifu.baidu.com/ip/geo/v1/district?ip=" + ip
|
||||
|
||||
// 创建 HTTP 客户端并设置超时时间
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
|
||||
// 发送 GET 请求
|
||||
resp, err := http.Get(url)
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
log.Fatalf("发送 GET 请求时出错: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查 HTTP 响应状态码
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Fatalf("HTTP 请求失败,状态码: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// 读取响应体
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
|
||||
339
LICENSE
Normal file
339
LICENSE
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{description}
|
||||
Copyright (C) {year} {fullname}
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
{signature of Ty Coon}, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
60
Makefile
60
Makefile
@@ -1,21 +1,71 @@
|
||||
CROSS_COMPILE ?=
|
||||
CC := $(CROSS_COMPILE)gcc
|
||||
STRIP := $(CROSS_COMPILE)strip
|
||||
CFLAGS += -g -Os -Wall -Iip2region -Iqqwry
|
||||
LIBS = -lm -static
|
||||
CFLAGS += -g -Wall -Iip2region
|
||||
LIBS =
|
||||
BIN := denyip
|
||||
|
||||
|
||||
# 检测操作系统并设置相应的变量
|
||||
OS := $(shell if cat /etc/issue | grep -i 'ubuntu' >/dev/null 2>&1; then \
|
||||
echo ubuntu; \
|
||||
elif test -f /etc/debian_version; then \
|
||||
echo debian; \
|
||||
elif test -f /etc/centos-release; then \
|
||||
echo centos; \
|
||||
else \
|
||||
echo unsupported; \
|
||||
fi)
|
||||
|
||||
OS_VER := $(shell if [ "$(OS)" = "ubuntu" ]; then \
|
||||
cat /etc/issue | head -n1 | awk '{print $$2}'; \
|
||||
elif [ "$(OS)" = "debian" ]; then \
|
||||
cat /etc/debian_version; \
|
||||
elif [ "$(OS)" = "centos" ]; then \
|
||||
cat /etc/centos-release | grep -o -E '[0-9.]{3,}' 2>/dev/null; \
|
||||
else \
|
||||
echo "N/A"; \
|
||||
fi)
|
||||
|
||||
KERNEL := $(shell uname -sr)
|
||||
ARCH := $(shell uname -m)
|
||||
|
||||
|
||||
|
||||
# 使用shell命令获取库的链接选项
|
||||
ifeq ($(OS), centos)
|
||||
LIBPCAP := /usr/lib64/libpcap.so
|
||||
LIBS =
|
||||
else
|
||||
LIBPCAP := $(shell pkg-config --libs --static libpcap)
|
||||
endif
|
||||
LIBCAP := $(shell pkg-config --libs --static libcap)
|
||||
LIBIPSET := $(shell pkg-config --libs --static libipset)
|
||||
|
||||
all: $(BIN) # 默认目标
|
||||
|
||||
# 系统信息打印目标
|
||||
info:
|
||||
@echo "Operating System: $(OS)"
|
||||
@echo "OS Version: $(OS_VER)"
|
||||
@echo "Kernel Version: $(KERNEL)"
|
||||
@echo "Architecture: $(ARCH)"
|
||||
@echo "Compiler: $(CC)"
|
||||
@echo "CFLAGS: $(CFLAGS)"
|
||||
@echo "Libraries: $(LIBS)"
|
||||
@echo "LIBPCAP: $(LIBPCAP)"
|
||||
@echo "LIBCAP: $(LIBCAP)"
|
||||
@echo "LIBIPSET: $(LIBIPSET)"
|
||||
|
||||
ipquery: # Go 构建目标
|
||||
cd IP_region_query && CGO_ENABLED=0 go build -ldflags '-w -s'
|
||||
|
||||
$(BIN): main.o common.o ip2region/ip2region.o ip2region/xdb_searcher.o qqwry/qqwry.o
|
||||
$(CC) $(CFLAGS) -o $(BIN) $^ $(LIBS)
|
||||
$(BIN): cap.o common.o ip2region/ip2region.o ip2region/xdb_searcher.o libipset.o
|
||||
$(CC) $(CFLAGS) -o $(BIN) $^ $(LIBPCAP) $(LIBCAP) $(LIBIPSET) $(LIBS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -rf $(BIN) ipquery
|
||||
rm -rf main.o common.o ip2region/ip2region.o ip2region/xdb_searcher.o qqwry/qqwry.o
|
||||
rm -rf cap.o common.o ip2region/ip2region.o ip2region/xdb_searcher.o libipset.o
|
||||
|
||||
141
README.md
141
README.md
@@ -1,47 +1,112 @@
|
||||
# denyip
|
||||
# DenyIP
|
||||
|
||||
大陆服务器禁止国外IP访问
|
||||
## 概述
|
||||
|
||||
## build
|
||||
`DenyIP` 是一个用于保护大陆服务器免受非本地业务访问的防火墙工具。通过结合 `libpcap`、`libipset`、`ip2region` 和 `Go ipquery`,该工具能够高效地捕获网络数据包、提取源IP地址、进行地理位置判断,并将非大陆IP地址添加到IP黑名单中,从而严格限制非大陆来源的访问。
|
||||
|
||||
~~~bash
|
||||
# Debian System
|
||||
apt install build-essential
|
||||
apt install golang
|
||||
apt install tcpdump procps iptables ipset
|
||||
## 特点
|
||||
|
||||
root@NIUYULING:/mnt/c/Users/root/Desktop/git.aixiao.me/DenyIP# make clean; make
|
||||
rm -rf denyip ipquery
|
||||
rm -rf main.o common.o ip2region/ip2region.o ip2region/xdb_searcher.o qqwry/qqwry.o
|
||||
gcc -g -Os -Wall -Iip2region -Iqqwry -c main.c -o main.o
|
||||
gcc -g -Os -Wall -Iip2region -Iqqwry -c common.c -o common.o
|
||||
gcc -g -Os -Wall -Iip2region -Iqqwry -c ip2region/ip2region.c -o ip2region/ip2region.o
|
||||
gcc -g -Os -Wall -Iip2region -Iqqwry -c ip2region/xdb_searcher.c -o ip2region/xdb_searcher.o
|
||||
gcc -g -Os -Wall -Iip2region -Iqqwry -c qqwry/qqwry.c -o qqwry/qqwry.o
|
||||
gcc -g -Os -Wall -Iip2region -Iqqwry -o denyip main.o common.o ip2region/ip2region.o ip2region/xdb_searcher.o qqwry/qqwry.o -lm -static
|
||||
root@NIUYULING:/mnt/c/Users/root/Desktop/git.aixiao.me/DenyIP#
|
||||
~~~
|
||||
- **数据包捕获**:使用 `libpcap` 捕获网络数据包,提取源IP地址。
|
||||
- **IP黑名单管理**:使用 `libipset` 管理IP黑名单,高效存储和匹配IP地址。
|
||||
- **初步IP地域判定**:使用 `ip2region` 库进行初步的IP地理位置判断。
|
||||
- **精确IP地理位置判断**:使用 `Go ipquery` 进一步精准识别IP地理位置。
|
||||
- **灵活的命令行选项**:支持守护进程模式、指定网络接口、启用/禁用Iptables规则等。
|
||||
|
||||
### help
|
||||
## 安装
|
||||
|
||||
~~~bash
|
||||
### 依赖项
|
||||
|
||||
root@NIUYULING:/mnt/c/Users/root/Desktop/git.aixiao.me/DenyIP# ./denyip -h
|
||||
DenyIp
|
||||
Linux system firewall, reject non-Chinese IP
|
||||
Email: aixiao@aixiao.me
|
||||
Version: 0.1
|
||||
Usage: ./denyip [-i eth0|-h|-?] [start|stop]
|
||||
Options:
|
||||
stop Enable firewall rules
|
||||
start Disable firewall rules
|
||||
Parameters:
|
||||
-h|? Help info
|
||||
-i interface name
|
||||
在 Debian 系统上安装所需的开发工具和库:
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install build-essential golang libpcap-dev libcap-dev libipset-dev libsystemd-dev
|
||||
```
|
||||
|
||||
在 Centos 7 系统上安装所需的开发工具和库:
|
||||
|
||||
```bash
|
||||
yum install ipset-devel libattr-devel libpcap-devel libcap-devel
|
||||
```
|
||||
|
||||
### 下载项目
|
||||
|
||||
克隆项目仓库:
|
||||
|
||||
```bash
|
||||
git clone https://git.aixiao.me/DenyIP.git
|
||||
cd DenyIP
|
||||
```
|
||||
|
||||
## 构建
|
||||
|
||||
编译项目:
|
||||
|
||||
```bash
|
||||
make clean
|
||||
make
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 命令行选项
|
||||
|
||||
```bash
|
||||
Usage: denyip [-d] [-i <interface>] [-s <start|stop>] [-h|-?]
|
||||
-d Daemon mode
|
||||
-i interface (default eth0)
|
||||
-s regular signal (default start|stop)
|
||||
start Enable Iptables rule
|
||||
stop Disable Iptables rule
|
||||
-h|-? Help Information
|
||||
```
|
||||
|
||||
### 示例命令
|
||||
|
||||
- **启动守护进程**:
|
||||
|
||||
```bash
|
||||
./denyip -d -i eth0
|
||||
```
|
||||
|
||||
- **启用Iptables规则**:
|
||||
|
||||
```bash
|
||||
./denyip -s start
|
||||
```
|
||||
|
||||
- **禁用Iptables规则**:
|
||||
|
||||
```bash
|
||||
./denyip -s stop
|
||||
```
|
||||
|
||||
- **查看帮助信息**:
|
||||
|
||||
```bash
|
||||
./denyip -h
|
||||
```
|
||||
|
||||
- **关闭守护进程**:
|
||||
|
||||
```bash
|
||||
killall -15 denyip
|
||||
```
|
||||
|
||||
|
||||
root@niuyuling:~/DenyIP# killall denyip # 关闭
|
||||
root@niuyuling:~/DenyIP# ./denyip -i eth0 # 启动
|
||||
root@niuyuling:~/DenyIP# ./denyip start # Iptables 规则打开
|
||||
root@niuyuling:~/DenyIP# ./denyip stop # Iptables 规则关闭
|
||||
~~~
|
||||
## 贡献
|
||||
|
||||
欢迎贡献代码和提出改进建议!请通过 Pull Request 或 Issue 的方式提交。
|
||||
|
||||
## 联系信息
|
||||
|
||||
- **邮箱**:<aixiao@aixiao.me>
|
||||
- **日期**:20241024
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目遵循 GNU 许可证,详情参见 [LICENSE](LICENSE) 文件。
|
||||
|
||||
---
|
||||
|
||||
希望这个 `README.md` 文件能够帮助您更好地介绍和使用 `DenyIP` 项目。如果有任何其他需求或修改,请随时告知。
|
||||
453
cap.c
Normal file
453
cap.c
Normal file
@@ -0,0 +1,453 @@
|
||||
#include "cap.h"
|
||||
#include "common.h"
|
||||
#include "libipset.h"
|
||||
|
||||
|
||||
pcap_if_t *alldevs, *device;
|
||||
pcap_t *handle; // 会话句柄
|
||||
struct bpf_program fp; // 编译后的过滤器
|
||||
|
||||
pid_t pid = -1; // 子进程全局PID
|
||||
|
||||
#define SHM_SIZE 1024 // 共享内存大小
|
||||
#define SHM_KEY 0124 // 共享内存键值
|
||||
int shmid = -1;
|
||||
int RULE_NAME_NUMBER = 0; // ipset 集合集合数
|
||||
char *RULE_NAME = NULL; // 共享内存
|
||||
char *ip2region_area = NULL; // ip2region 解析结果
|
||||
char *command_result = NULL; // 执行命令的结果
|
||||
|
||||
#define CACHE_TTL 600 // 设定缓存的存活时间为 600 秒 (10 分钟)
|
||||
#define MAX_CACHE_SIZE 100 // 缓存最多存储 100 个 IP 地址
|
||||
struct ip_cache_node *ip_cache_head = NULL; // 缓存链表的头节点
|
||||
int cache_size = 0; // 当前缓存中的 IP 数量
|
||||
|
||||
// 定义链表结构,用于缓存 IP 地址
|
||||
struct ip_cache_node {
|
||||
char ip[INET_ADDRSTRLEN]; // 存储 IP 地址
|
||||
time_t timestamp; // 记录缓存时间
|
||||
struct ip_cache_node *next; // 指向下一个节点
|
||||
};
|
||||
|
||||
|
||||
// 检查 IP 是否已在缓存中并是否过期
|
||||
int is_ip_in_cache(const char *ip)
|
||||
{
|
||||
time_t now = time(NULL); // 获取当前时间
|
||||
struct ip_cache_node *current = ip_cache_head;
|
||||
struct ip_cache_node *prev = NULL;
|
||||
|
||||
while (current != NULL) {
|
||||
// 如果 IP 匹配并且未过期
|
||||
if (strcmp(current->ip, ip) == 0) {
|
||||
if (now - current->timestamp <= CACHE_TTL) {
|
||||
return 1; // IP 在缓存中,且未过期
|
||||
} else {
|
||||
// 如果过期,从链表中移除这个节点
|
||||
if (prev == NULL) {
|
||||
ip_cache_head = current->next;
|
||||
} else {
|
||||
prev->next = current->next;
|
||||
}
|
||||
|
||||
free(current);
|
||||
cache_size--;
|
||||
return 0; // IP 过期,不再缓存
|
||||
}
|
||||
}
|
||||
|
||||
prev = current;
|
||||
current = current->next;
|
||||
}
|
||||
return 0; // IP 不在缓存中
|
||||
}
|
||||
|
||||
// 将新 IP 添加到缓存,若缓存过大则移除最早的 IP
|
||||
void add_ip_to_cache(const char *ip)
|
||||
{
|
||||
// 如果缓存大小超过限制,移除最早的 IP
|
||||
if (cache_size >= MAX_CACHE_SIZE) {
|
||||
struct ip_cache_node *current = ip_cache_head;
|
||||
struct ip_cache_node *prev = NULL;
|
||||
|
||||
// 找到链表的最后一个节点
|
||||
while (current->next != NULL) {
|
||||
prev = current;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// 移除最后一个节点(最早的 IP)
|
||||
if (prev != NULL) {
|
||||
prev->next = NULL;
|
||||
} else {
|
||||
ip_cache_head = NULL;
|
||||
}
|
||||
free(current);
|
||||
cache_size--;
|
||||
}
|
||||
|
||||
// 创建新的缓存节点并添加到链表头部
|
||||
struct ip_cache_node *new_node = (struct ip_cache_node *)malloc(sizeof(struct ip_cache_node));
|
||||
if (new_node == NULL) {
|
||||
perror("malloc");
|
||||
return;
|
||||
}
|
||||
strncpy(new_node->ip, ip, INET_ADDRSTRLEN);
|
||||
new_node->timestamp = time(NULL); // 记录当前时间
|
||||
new_node->next = ip_cache_head;
|
||||
ip_cache_head = new_node;
|
||||
cache_size++;
|
||||
}
|
||||
|
||||
// 清理缓存链表,释放所有节点的内存
|
||||
void free_ip_cache()
|
||||
{
|
||||
struct ip_cache_node *current = ip_cache_head;
|
||||
while (current != NULL) {
|
||||
struct ip_cache_node *next = current->next;
|
||||
free(current);
|
||||
current = next;
|
||||
}
|
||||
|
||||
ip_cache_head = NULL;
|
||||
cache_size = 0;
|
||||
}
|
||||
|
||||
// 回调函数,在捕获到每个数据包时调用
|
||||
void packet_handler(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
|
||||
{
|
||||
int ethernet_header_len = 14;
|
||||
struct ip *ip_header = (struct ip *)(packet + ethernet_header_len);
|
||||
char src_ip[INET_ADDRSTRLEN] = { 0 };
|
||||
|
||||
// 地域白名单
|
||||
char _region_list[WHITELIST_IP_NUM][WHITELIST_IP_NUM] = { { 0 }, { 0 } };
|
||||
char _REGION_LIST[BUFFER] = { 0 };
|
||||
const char *REGION_ENV = NULL;
|
||||
|
||||
int r = 0;
|
||||
//char *t = _time();
|
||||
|
||||
inet_ntop(AF_INET, &(ip_header->ip_src), src_ip, INET_ADDRSTRLEN);
|
||||
//_printf("%s\n", src_ip);
|
||||
|
||||
|
||||
// 如果 IP 地址已在缓存中且未过期,则跳过查询
|
||||
if (is_ip_in_cache(src_ip)) {
|
||||
_printf(RED "IP:%s 已在缓存中,跳过查询\n" REDEND, src_ip);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 执行查询并添加到缓存
|
||||
ip2region_area = ip2region("ip2region/ip2region.xdb", src_ip);
|
||||
if (ip2region_area == NULL) {
|
||||
_printf(RED "ip2region 解析地域错误\n" REDEND);
|
||||
return;
|
||||
}
|
||||
// 取环境变量
|
||||
REGION_ENV = getenv("REGION");
|
||||
if (REGION_ENV != NULL) {
|
||||
_printf("REGION: %s\n", REGION_ENV);
|
||||
strcpy(_REGION_LIST, REGION_ENV);
|
||||
} else {
|
||||
strcpy(_REGION_LIST, "局域网 内网 中国 ");
|
||||
}
|
||||
|
||||
split_string(_REGION_LIST, " ", _region_list);
|
||||
if (isregion(ip2region_area, _region_list) == 1) { // 返回1表示在白名单列表
|
||||
;
|
||||
} else {
|
||||
//puts(ip2region_area);
|
||||
|
||||
char ip_query_command[256] = { 0 };
|
||||
snprintf(ip_query_command, sizeof(ip_query_command), "./IP_region_query/ipquery %s", src_ip);
|
||||
if (cache_size < MAX_CACHE_SIZE) // 缓存IP数不够预备设定值
|
||||
{
|
||||
sleep(1);
|
||||
_printf("缓存 IP 数 %d\n", cache_size);
|
||||
}
|
||||
command_result = _execute_command(ip_query_command);
|
||||
if (command_result != NULL) {
|
||||
add_ip_to_cache(src_ip); // 添加 IP 到缓存
|
||||
|
||||
char *p = strstr(command_result, "中国");
|
||||
if (p == NULL) {
|
||||
_printf(RED "%s %s\n" REDEND, src_ip, command_result);
|
||||
r = add_ip_to_ipset(RULE_NAME, src_ip);
|
||||
_printf("add_ip_to_ipset() return %d\n", r);
|
||||
}
|
||||
|
||||
free(command_result);
|
||||
command_result = NULL;
|
||||
} else {
|
||||
;
|
||||
}
|
||||
|
||||
if (command_result != NULL)
|
||||
free(command_result);
|
||||
}
|
||||
|
||||
if (ip2region_area != NULL)
|
||||
{
|
||||
free(ip2region_area);
|
||||
ip2region_area = NULL;
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
void usage()
|
||||
{
|
||||
printf("DenyIP version %s\n", _VERSION);
|
||||
puts("拒绝Linux服务器非大陆IP工具");
|
||||
puts("MAIL: aixiao@aixiao.me");
|
||||
puts("Date: 20241024");
|
||||
puts(" Usage: denyip [-d] [-i <interface>] [-s <start|stop>] [-h|-?]");
|
||||
puts(" -d --daemon Daemon mode");
|
||||
puts(" -i --interface interface (default eth0)");
|
||||
puts(" -l print iptables rule");
|
||||
puts(" -s --signal regular signal (default start|stop) ");
|
||||
puts(" start Enable Iptables rule");
|
||||
puts(" stop Disable Iptables rule");
|
||||
puts(" -h|-? Help Information");
|
||||
puts("");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void cleanup_(int signum)
|
||||
{
|
||||
|
||||
_printf("Received signal %d, cleaning up...\n", signum);
|
||||
|
||||
// 终止子进程
|
||||
if (pid > 0) {
|
||||
kill(pid, SIGTERM);
|
||||
}
|
||||
|
||||
// 释放共享内存
|
||||
if (RULE_NAME != NULL) {
|
||||
shmdt(RULE_NAME);
|
||||
shmctl(shmid, IPC_RMID, NULL);
|
||||
}
|
||||
|
||||
// 在程序结束时,清理缓存链表
|
||||
free_ip_cache();
|
||||
|
||||
if (ip2region_area != NULL) {
|
||||
free(ip2region_area);
|
||||
ip2region_area = NULL;
|
||||
}
|
||||
if (command_result != NULL) {
|
||||
free(command_result);
|
||||
command_result = NULL;
|
||||
}
|
||||
|
||||
// 清理
|
||||
pcap_freecode(&fp);
|
||||
pcap_freealldevs(alldevs); // 释放设备列表
|
||||
//pcap_close(handle); // 关闭会话句柄
|
||||
|
||||
|
||||
|
||||
// 退出主进程
|
||||
exit(0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// 注册 SIGTERM 信号处理函数
|
||||
signal(SIGTERM, cleanup_);
|
||||
|
||||
int opt;
|
||||
char errbuf[PCAP_ERRBUF_SIZE]; // 错误缓冲区
|
||||
char protocol[] = "tcp";
|
||||
char interface[256] = "{ 0 }";
|
||||
char Ipset_Command[BUFFER];
|
||||
|
||||
strcpy(interface, "eth0");
|
||||
memset(&alldevs, 0, sizeof(alldevs));
|
||||
memset(&device, 0, sizeof(device));
|
||||
memset(errbuf, 0, PCAP_ERRBUF_SIZE);
|
||||
|
||||
int longindex = 0;
|
||||
char optstring[] = "di:s:lh?";
|
||||
static struct option longopts[] = {
|
||||
{ "interface", required_argument, 0, 'i' },
|
||||
{ "signal", required_argument, 0, 's' },
|
||||
{ "daemon", no_argument, 0, 'd' },
|
||||
{ "l", no_argument, 0, 'l' },
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "?", no_argument, 0, '?' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
while (-1 != (opt = getopt_long(argc, argv, optstring, longopts, &longindex)))
|
||||
{
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
if (daemon(1, 1)) {
|
||||
perror("daemon");
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
strcpy(interface, optarg);
|
||||
break;
|
||||
case 'l':
|
||||
system("iptables -L -v -n --line-numbers");
|
||||
exit(0);
|
||||
break;
|
||||
case 's':
|
||||
if (strcmp(optarg, "start") == 0) {
|
||||
memset(Ipset_Command, 0, BUFFER);
|
||||
// 将 MAXIPSET_RULT_NAME_NUM 替换为实际值
|
||||
snprintf(Ipset_Command, sizeof(Ipset_Command),
|
||||
"for n in $(seq 0 %d); do iptables -A INPUT -p tcp -m set --match-set root$n src -j DROP 2> /dev/null; done",
|
||||
MAXIPSET_RULT_NAME_NUM);
|
||||
system(Ipset_Command);
|
||||
exit(0);
|
||||
} else if (strcmp(optarg, "stop") == 0) {
|
||||
memset(Ipset_Command, 0, BUFFER);
|
||||
// 将 MAXIPSET_RULT_NAME_NUM 替换为实际值
|
||||
snprintf(Ipset_Command, sizeof(Ipset_Command),
|
||||
"for n in $(seq 0 %d); do iptables -D INPUT -p tcp -m set --match-set root$n src -j DROP 2> /dev/null; done",
|
||||
MAXIPSET_RULT_NAME_NUM);
|
||||
system(Ipset_Command);
|
||||
exit(0);
|
||||
} else {
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
usage();
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 创建共享内存
|
||||
shmid = shmget(SHM_KEY, SHM_SIZE, IPC_CREAT | 0666);
|
||||
if (shmid < 0) {
|
||||
perror("shmget");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// 连接共享内存到进程地址空间
|
||||
RULE_NAME = (char *)shmat(shmid, NULL, 0);
|
||||
if (RULE_NAME == (char *)-1) {
|
||||
perror("shmat");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
pid = fork(); // 创建子进程
|
||||
if (pid == 0) // 子进程
|
||||
{
|
||||
|
||||
int count = 0;
|
||||
snprintf(RULE_NAME, BUFFER, "root%d", RULE_NAME_NUMBER);
|
||||
|
||||
if (create_ipset(RULE_NAME) != 0) {
|
||||
_printf("创建 IPSet %s 失败\n", RULE_NAME);
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
_printf("子进程当前 Ipset Rule 名 %s\n", RULE_NAME);
|
||||
|
||||
count = get_ip_count_in_ipset(RULE_NAME);
|
||||
if (count >= 0) {
|
||||
_printf("IPSet %s 中的 IP 数量: %d\n", RULE_NAME, count);
|
||||
if (count >= MAXIPSET && RULE_NAME_NUMBER <= MAXIPSET_RULT_NAME_NUM) // RULE_中的IP数量不超过MAXIPSET,并且集合不能超过 MAXIPSET_RULT_NAME_NUM 个
|
||||
{
|
||||
RULE_NAME_NUMBER++;
|
||||
|
||||
snprintf(RULE_NAME, BUFFER, "root%d", RULE_NAME_NUMBER); // 更新规则名称
|
||||
// 创建新的 IPSet
|
||||
if (create_ipset(RULE_NAME) != 0) {
|
||||
_printf("创建 IPSet %s 失败\n", RULE_NAME);
|
||||
} else {
|
||||
char iptables_command[256];
|
||||
sprintf(iptables_command, "iptables -I INPUT -m set --match-set %s src -j DROP", RULE_NAME);
|
||||
system(iptables_command);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (RULE_NAME_NUMBER == MAXIPSET_RULT_NAME_NUM) {
|
||||
_printf("已达到最大规则数限制,停止创建新规则。\n");
|
||||
_printf("请手动清理Ipset规则\n");
|
||||
}
|
||||
}
|
||||
|
||||
sleep(3); // 每 3 秒检查一次
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 查找可用的网络设备
|
||||
if (pcap_findalldevs(&alldevs, errbuf) == -1) {
|
||||
fprintf(stderr, "无法找到设备: %s\n", errbuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 打印可用设备列表
|
||||
_printf("可用的设备:\n");
|
||||
for (device = alldevs; device != NULL; device = device->next) {
|
||||
_printf("设备: %s\n", device->name);
|
||||
}
|
||||
|
||||
// 打开设备以进行数据包捕获
|
||||
handle = pcap_open_live(interface, BUFSIZ, 1, 1000, errbuf);
|
||||
if (handle == NULL) {
|
||||
fprintf(stderr, "无法打开设备 %s: %s\n", interface, errbuf);
|
||||
pcap_freealldevs(alldevs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 编译过滤器
|
||||
if (pcap_compile(handle, &fp, protocol, 0, PCAP_NETMASK_UNKNOWN) == -1) {
|
||||
fprintf(stderr, "无法编译过滤器 %s: %s\n", protocol, pcap_geterr(handle));
|
||||
pcap_close(handle);
|
||||
pcap_freealldevs(alldevs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 设置过滤器
|
||||
if (pcap_setfilter(handle, &fp) == -1) {
|
||||
fprintf(stderr, "无法设置过滤器 %s: %s\n", protocol, pcap_geterr(handle));
|
||||
pcap_freecode(&fp);
|
||||
pcap_close(handle);
|
||||
pcap_freealldevs(alldevs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 开始捕获数据包
|
||||
if (pcap_loop(handle, 0, packet_handler, NULL) < 0) {
|
||||
fprintf(stderr, "捕获数据包时出错: %s\n", pcap_geterr(handle));
|
||||
pcap_freecode(&fp);
|
||||
pcap_close(handle);
|
||||
pcap_freealldevs(alldevs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 在程序结束时,清理缓存链表
|
||||
free_ip_cache();
|
||||
|
||||
// 清理
|
||||
pcap_freecode(&fp);
|
||||
pcap_freealldevs(alldevs); // 释放设备列表
|
||||
pcap_close(handle); // 关闭会话句柄
|
||||
|
||||
return 0;
|
||||
}
|
||||
31
cap.h
Normal file
31
cap.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef CAP_H
|
||||
#define CAP_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pcap.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/ip.h> // IP header
|
||||
#include <netinet/tcp.h> // TCP header
|
||||
#include <sys/types.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ip2region/ip2region.h"
|
||||
|
||||
#define RED "\033[31m"
|
||||
#define REDEND "\033[0m"
|
||||
|
||||
#define MAXIPSET 65535
|
||||
#define MAXIPSET_RULT_NAME_NUM 26
|
||||
|
||||
#define _VERSION "0.2"
|
||||
|
||||
#endif
|
||||
199
common.c
199
common.c
@@ -1,30 +1,32 @@
|
||||
#include "common.h"
|
||||
|
||||
// 计算字符串长度
|
||||
int _strlen(char *str)
|
||||
{
|
||||
char *_p = NULL;
|
||||
|
||||
int _strlen(const char *str) {
|
||||
if (str == NULL)
|
||||
return 0;
|
||||
|
||||
_p = strchr(str, '\0');
|
||||
|
||||
if (_p == NULL)
|
||||
return 0;
|
||||
|
||||
const char *_p = strchr(str, '\0');
|
||||
return _p - str;
|
||||
}
|
||||
|
||||
|
||||
// 自定义 printf 函数
|
||||
void my_printf(const char *format, ...)
|
||||
void _printf(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
// 打印到控制台
|
||||
vprintf(format, args);
|
||||
va_end(args); // 结束对变参列表的处理
|
||||
// 获取当前时间
|
||||
time_t now = time(NULL);
|
||||
struct tm local_time;
|
||||
localtime_r(&now, &local_time);
|
||||
char time_str[20]; // YYYY-MM-DD HH:MM:SS 格式
|
||||
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", &local_time);
|
||||
|
||||
// 打印时间戳到控制台
|
||||
printf("[%s] ", time_str);
|
||||
vprintf(format, args); // 打印内容到控制台
|
||||
va_end(args); // 结束对变参列表的处理
|
||||
|
||||
// 重新启动变参列表
|
||||
va_start(args, format);
|
||||
@@ -32,13 +34,6 @@ void my_printf(const char *format, ...)
|
||||
// 打开日志文件(追加模式)
|
||||
FILE *log_file = fopen(PRINT_LOG_FILE, "a");
|
||||
if (log_file != NULL) {
|
||||
// 获取当前时间
|
||||
time_t now = time(NULL);
|
||||
struct tm local_time;
|
||||
localtime_r(&now, &local_time);
|
||||
char time_str[20]; // YYYY-MM-DD HH:MM:SS 格式
|
||||
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", &local_time);
|
||||
|
||||
// 打印时间戳到日志文件
|
||||
fprintf(log_file, "[%s] ", time_str);
|
||||
|
||||
@@ -51,7 +46,7 @@ void my_printf(const char *format, ...)
|
||||
perror("Unable to open log file");
|
||||
}
|
||||
|
||||
va_end(args); // 结束对变参列表的处理
|
||||
va_end(args); // 结束对变参列表的处理
|
||||
}
|
||||
|
||||
void split_string(char string[], char delims[], char (*whitelist_ip)[WHITELIST_IP_NUM])
|
||||
@@ -85,7 +80,7 @@ int whitelist(char *client_ip, char (*whitelist_ip)[WHITELIST_IP_NUM])
|
||||
break;
|
||||
}
|
||||
|
||||
// 对比client_ip的前缀是否与白名单中的IP段匹配
|
||||
// 对比 client_ip 的前缀是否与白名单中的IP段匹配
|
||||
if (strncmp(client_ip, whitelist_ip[i], strlen(whitelist_ip[i])) == 0) {
|
||||
return 1; // 匹配成功
|
||||
}
|
||||
@@ -94,7 +89,6 @@ int whitelist(char *client_ip, char (*whitelist_ip)[WHITELIST_IP_NUM])
|
||||
return 0; // 未找到匹配
|
||||
}
|
||||
|
||||
|
||||
// 地域段白名单对比
|
||||
int isregion(char *str, char (*region_list)[WHITELIST_IP_NUM])
|
||||
{
|
||||
@@ -102,12 +96,12 @@ int isregion(char *str, char (*region_list)[WHITELIST_IP_NUM])
|
||||
char *p;
|
||||
|
||||
for (i = 0; i < WHITELIST_IP_NUM; i++) {
|
||||
// 如果region_list[i]为空字符串,跳出循环
|
||||
// 如果 region_list[i] 为空字符串,跳出循环
|
||||
if (region_list[i][0] == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
// 在str中查找region_list[i]
|
||||
// 在str中查找 region_list[i]
|
||||
p = strstr(str, region_list[i]);
|
||||
if (p != NULL) {
|
||||
return 1; // 匹配成功,返回1
|
||||
@@ -117,7 +111,7 @@ int isregion(char *str, char (*region_list)[WHITELIST_IP_NUM])
|
||||
return 0; // 没有匹配,返回0
|
||||
}
|
||||
|
||||
int8_t copy_new_mem(char *src, int src_len, char **dest)
|
||||
int8_t _copy_new_mem(char *src, int src_len, char **dest)
|
||||
{
|
||||
*dest = (char *)malloc(src_len + 1);
|
||||
if (*dest == NULL)
|
||||
@@ -128,44 +122,151 @@ int8_t copy_new_mem(char *src, int src_len, char **dest)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *_time()
|
||||
{
|
||||
char temp[BUFFER];
|
||||
char *wday[] = { "0", "1", "2", "3", "4", "5", "6" };
|
||||
time_t t;
|
||||
struct tm *p;
|
||||
time(&t);
|
||||
p = localtime(&t); // 取得当地时间
|
||||
// 返回的时间字符串存储在静态缓冲区中
|
||||
char *_time() {
|
||||
static char temp[BUFFER];
|
||||
const char *wday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
||||
time_t t = time(NULL);
|
||||
struct tm *p = localtime(&t);
|
||||
|
||||
memset(temp, 0, BUFFER);
|
||||
snprintf(temp, BUFFER, "[%d/%02d/%02d %s %02d:%02d:%02d] ", (1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday, wday[p->tm_wday], p->tm_hour, p->tm_min, p->tm_sec);
|
||||
if (!p) {
|
||||
perror("localtime failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return strdup(temp);
|
||||
snprintf(temp, sizeof(temp), "[%d/%02d/%02d %s %02d:%02d:%02d]",
|
||||
1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday,
|
||||
wday[p->tm_wday], p->tm_hour, p->tm_min, p->tm_sec);
|
||||
|
||||
return temp; // 返回静态缓冲区地址
|
||||
}
|
||||
|
||||
// 将字符串转换为IPv4地址
|
||||
int is_valid_ip(const char *ip)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
// 尝试将字符串转换为IPv4地址
|
||||
// 将字符串转换为IPv4地址
|
||||
int result = inet_pton(AF_INET, ip, &(sa.sin_addr));
|
||||
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
int nice_(int increment)
|
||||
{
|
||||
// 修改进程优先级
|
||||
int _nice(int increment) {
|
||||
// 获取当前优先级
|
||||
int oldprio = getpriority(PRIO_PROCESS, getpid());
|
||||
printf("%d\n", oldprio);
|
||||
if (oldprio == -1 && errno != 0) {
|
||||
perror("getpriority failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return setpriority(PRIO_PROCESS, getpid(), oldprio + increment);
|
||||
printf("Current priority: %d\n", oldprio);
|
||||
|
||||
// 检查是否溢出
|
||||
if ((increment > 0 && oldprio > INT_MAX - increment) ||
|
||||
(increment < 0 && oldprio < INT_MIN - increment)) {
|
||||
fprintf(stderr, "Priority overflow error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 计算新的优先级
|
||||
int newprio = oldprio + increment;
|
||||
|
||||
// 检查新的优先级是否在有效范围内
|
||||
if (newprio < PRIO_MIN || newprio > PRIO_MAX) {
|
||||
fprintf(stderr, "New priority out of range: %d (valid range is %d to %d)\n", newprio, PRIO_MIN, PRIO_MAX);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 设置新的优先级
|
||||
if (setpriority(PRIO_PROCESS, getpid(), newprio) == -1) {
|
||||
perror("setpriority failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("New priority: %d\n", newprio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 判断命令是否存在
|
||||
int command_exists(const char *command)
|
||||
{
|
||||
char buffer[BUFFER];
|
||||
snprintf(buffer, sizeof(buffer), "%s > /dev/null 2>&1", command);
|
||||
int status = system(buffer);
|
||||
int command_exists(const char *command) {
|
||||
const char *path_env = getenv("PATH");
|
||||
if (!path_env) {
|
||||
return 0; // 如果 PATH 不存在,返回不存在
|
||||
}
|
||||
|
||||
char filepath[1024]; // 缓冲区大小
|
||||
const char *dir = path_env;
|
||||
|
||||
while (dir && *dir) {
|
||||
// 查找 PATH 中的下一个目录
|
||||
const char *end = strchr(dir, ':');
|
||||
size_t len = end ? (size_t)(end - dir) : strlen(dir);
|
||||
|
||||
// 构建路径并检查长度
|
||||
if (snprintf(filepath, sizeof(filepath), "%.*s/%s", (int)len, dir, command) >= (int)sizeof(filepath)) {
|
||||
return 0; // 缓冲区溢出,返回不存在
|
||||
}
|
||||
|
||||
// 检查文件是否存在且可执行
|
||||
if (access(filepath, X_OK) == 0) {
|
||||
puts(filepath);
|
||||
return 1; // 命令存在
|
||||
}
|
||||
|
||||
// 更新 dir 指针
|
||||
dir = end ? end + 1 : NULL;
|
||||
}
|
||||
|
||||
return 0; // 命令不存在
|
||||
}
|
||||
|
||||
// 定义一个函数,执行命令并返回输出
|
||||
char *_execute_command(const char *command) {
|
||||
FILE *fp;
|
||||
char buffer[1024];
|
||||
char *output = NULL;
|
||||
size_t output_size = 0;
|
||||
size_t total_read = 0;
|
||||
|
||||
// 打开管道,执行命令
|
||||
fp = popen(command, "r");
|
||||
if (fp == NULL) {
|
||||
perror("popen");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 读取命令的输出
|
||||
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
|
||||
size_t len = strlen(buffer);
|
||||
if (total_read + len + 1 > output_size) {
|
||||
output_size = output_size == 0 ? 128 : output_size * 2;
|
||||
char *new_output = realloc(output, output_size);
|
||||
if (new_output == NULL) {
|
||||
perror("realloc");
|
||||
free(output); // 释放已分配的内存
|
||||
pclose(fp); // 关闭管道
|
||||
return NULL;
|
||||
}
|
||||
output = new_output;
|
||||
}
|
||||
// 复制内容并增加总计读取的长度
|
||||
strcpy(output + total_read, buffer);
|
||||
total_read += len;
|
||||
}
|
||||
|
||||
// 确保输出以 null 结尾
|
||||
if (output_size > 0) {
|
||||
output[total_read] = '\0';
|
||||
}
|
||||
|
||||
// 关闭管道
|
||||
if (pclose(fp) == -1) {
|
||||
perror("pclose");
|
||||
free(output); // pclose 失败时释放内存
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
return (status == 0);
|
||||
}
|
||||
18
common.h
18
common.h
@@ -17,26 +17,30 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#define PRINT_LOG_FILE "Gateway.log"
|
||||
#define PRINT_LOG_FILE "denyip.log"
|
||||
#define BUFFER 1024
|
||||
#define WHITELIST_IP_NUM 1024
|
||||
|
||||
extern int _strlen(char *str);
|
||||
extern void my_printf(const char *format, ...);
|
||||
|
||||
extern char *_time();
|
||||
extern int _strlen(const char *str);
|
||||
extern void _printf(const char *format, ...);
|
||||
extern int _nice(int increment);
|
||||
|
||||
extern void split_string(char string[], char delims[], char (*whitelist_ip)[WHITELIST_IP_NUM]);
|
||||
extern int whitelist(char *client_ip, char (*whitelist_ip)[WHITELIST_IP_NUM]);
|
||||
extern char *_time();
|
||||
extern int isregion(char *str, char (*region_list)[WHITELIST_IP_NUM]);
|
||||
|
||||
extern int8_t copy_new_mem(char *src, int src_len, char **dest);
|
||||
extern int8_t _copy_new_mem(char *src, int src_len, char **dest);
|
||||
|
||||
extern int is_valid_ip(const char *ip);
|
||||
extern int nice_(int increment);
|
||||
extern int command_exists(const char *command);
|
||||
extern int _command_exists(const char *command);
|
||||
extern char *_execute_command(const char *command);
|
||||
|
||||
#endif
|
||||
|
||||
Binary file not shown.
20
ip2region/main.c
Normal file
20
ip2region/main.c
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "ip2region.h"
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
// 执行查询并添加到缓存
|
||||
char *area = ip2region("ip2region.xdb", "1.1.1.1");
|
||||
if (area == NULL) {
|
||||
return -1;
|
||||
}
|
||||
puts(area);
|
||||
|
||||
|
||||
free(area);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Binary file not shown.
246
libipset.c
Normal file
246
libipset.c
Normal file
@@ -0,0 +1,246 @@
|
||||
#include "libipset.h"
|
||||
|
||||
// 自定义输出处理函数
|
||||
int custom_output_handler(struct ipset_session *session, void *p, const char *msg, ...)
|
||||
{
|
||||
int *ip_count = (int *)p;
|
||||
char buffer[BUFFER];
|
||||
|
||||
// 格式化输出消息
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
vsnprintf(buffer, sizeof(buffer), msg, args);
|
||||
va_end(args);
|
||||
|
||||
// 输出调试信息
|
||||
//printf("Buffer: %s\n", buffer); // 调试输出
|
||||
|
||||
if (strlen(buffer) > 1) {
|
||||
char temp[BUFFER];
|
||||
char *p1 = strstr(buffer, "Number of entries:");
|
||||
if (p1 != NULL) {
|
||||
char *p2 = strstr(p1, "\n");
|
||||
if (p2 != NULL) {
|
||||
size_t len = p2 - p1; // 计算长度
|
||||
if (len < BUFFER) {
|
||||
strncpy(temp, p1, len);
|
||||
temp[len] = '\0'; // 确保字符串结束
|
||||
//puts(temp);
|
||||
|
||||
// 查找冒号并提取数量
|
||||
char *p3 = strstr(temp, ":");
|
||||
if (p3 != NULL) {
|
||||
// 提取数字
|
||||
int count = atoi(p3 + 1); // 从冒号后面开始转换
|
||||
*ip_count += count; // 更新计数
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int custom_output_handler_(struct ipset_session *session, void *p, const char *msg, ...)
|
||||
{
|
||||
char buffer[BUFFER];
|
||||
|
||||
// 格式化输出消息
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
vsnprintf(buffer, sizeof(buffer), msg, args);
|
||||
va_end(args);
|
||||
|
||||
// 输出调试信息
|
||||
//printf("Buffer: %s\n", buffer); // 调试输出
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 自定义错误处理函数
|
||||
int custom_error_handler(struct ipset *ipset, void *p, int errnum, const char *msg, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
fprintf(stderr, "自定义错误: ");
|
||||
vfprintf(stderr, msg, args);
|
||||
va_end(args);
|
||||
return 0; // 返回0表示处理成功
|
||||
}
|
||||
|
||||
// 创建 IPSet,如果它不存在
|
||||
int create_ipset(char *set_name)
|
||||
{
|
||||
struct ipset *ipset = ipset_init();
|
||||
if (!ipset) {
|
||||
fprintf(stderr, "Failed to initialize IPSet\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ipset_load_types();
|
||||
|
||||
struct ipset_session *session = ipset_session(ipset);
|
||||
if (!session) {
|
||||
fprintf(stderr, "Failed to create IPSet session\n");
|
||||
ipset_fini(ipset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 设置自定义错误和输出处理函数
|
||||
if (ipset_custom_printf(ipset, custom_error_handler, NULL, custom_output_handler_, NULL) != 0) {
|
||||
fprintf(stderr, "设置自定义打印函数失败。\n");
|
||||
ipset_fini(ipset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 创建集合
|
||||
char *args[] = { "ipset", "create", set_name, "hash:ip", NULL };
|
||||
if (ipset_parse_argv(ipset, 4, args) != 0) {
|
||||
ipset_fini(ipset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
ipset_fini(ipset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 向指定的 ipset 添加 IP
|
||||
int add_ip_to_ipset(char *set_name, char *ip)
|
||||
{
|
||||
struct ipset *ipset = NULL;
|
||||
struct ipset_session *session = NULL;
|
||||
|
||||
// 初始化 ipset 和 session
|
||||
ipset = ipset_init();
|
||||
if (!ipset) {
|
||||
fprintf(stderr, "初始化 IPSet 失败\n");
|
||||
return -1; // 返回 -1,但不退出
|
||||
}
|
||||
|
||||
ipset_load_types();
|
||||
|
||||
session = ipset_session(ipset);
|
||||
if (!session) {
|
||||
fprintf(stderr, "创建 IPSet 会话失败\n");
|
||||
ipset_fini(ipset);
|
||||
return -1; // 返回 -1,但不退出
|
||||
}
|
||||
// 设置 IPSet 名称
|
||||
if (ipset_session_data_set(session, IPSET_SETNAME, set_name) != 0) {
|
||||
fprintf(stderr, "设置 IPSet 名称失败\n");
|
||||
ipset_session_fini(session);
|
||||
ipset_fini(ipset);
|
||||
return -1; // 返回 -1,但不退出
|
||||
}
|
||||
|
||||
// 设置自定义错误和输出处理函数
|
||||
if (ipset_custom_printf(ipset, custom_error_handler, NULL, custom_output_handler_, NULL) != 0) {
|
||||
fprintf(stderr, "设置自定义打印函数失败。\n");
|
||||
ipset_fini(ipset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 将ip添加到集合
|
||||
char *args[] = { "ipset", "add", set_name, ip, NULL };
|
||||
if (ipset_parse_argv(ipset, 4, args) != 0) {
|
||||
ipset_fini(ipset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
ipset_fini(ipset);
|
||||
|
||||
return 0; // 始终返回 0,表示执行成功
|
||||
}
|
||||
|
||||
// 定义一个函数来清空指定名称的 ipset 集合
|
||||
int flush_ipset(char *set_name)
|
||||
{
|
||||
struct ipset *ipset = ipset_init();
|
||||
if (!ipset) {
|
||||
fprintf(stderr, "Failed to initialize IPSet\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ipset_load_types();
|
||||
|
||||
// 设置自定义错误和输出处理函数
|
||||
if (ipset_custom_printf(ipset, custom_error_handler, NULL, custom_output_handler_, NULL) != 0) {
|
||||
fprintf(stderr, "设置自定义打印函数失败。\n");
|
||||
ipset_fini(ipset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 清空集合
|
||||
char *args[] = { "ipset", "flush", set_name, NULL };
|
||||
if (ipset_parse_argv(ipset, 3, args) != 0) {
|
||||
ipset_fini(ipset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("IPSet %s flushed successfully\n", set_name);
|
||||
ipset_fini(ipset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 获取指定 IPSet 中的 IP 数量
|
||||
int get_ip_count_in_ipset(char *set_name)
|
||||
{
|
||||
int ip_count = 0;
|
||||
struct ipset *ipset = ipset_init();
|
||||
if (!ipset) {
|
||||
fprintf(stderr, "初始化 ipset 失败。\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ipset_load_types();
|
||||
|
||||
// 设置自定义错误和输出处理函数
|
||||
if (ipset_custom_printf(ipset, custom_error_handler, NULL, custom_output_handler, &ip_count) != 0) {
|
||||
fprintf(stderr, "设置自定义打印函数失败。\n");
|
||||
ipset_fini(ipset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 列出集合
|
||||
char *args[] = { "ipset", "list", set_name, NULL };
|
||||
if (ipset_parse_argv(ipset, 3, args) != 0) {
|
||||
ipset_fini(ipset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 释放资源
|
||||
ipset_fini(ipset);
|
||||
return ip_count;
|
||||
}
|
||||
|
||||
/*
|
||||
int main()
|
||||
{
|
||||
int r;
|
||||
char *set_name = "root";
|
||||
char *ip = "1.1.1.2";
|
||||
|
||||
// 确保 IPSet 已存在
|
||||
r = create_ipset(set_name);
|
||||
printf("create_ipset %d\n", r);
|
||||
|
||||
r = flush_ipset(set_name);
|
||||
printf("flush_ipset %d\n", r);
|
||||
|
||||
// 尝试添加 IP
|
||||
r = add_ip_to_ipset(set_name, ip);
|
||||
printf("add_ip_to_ipset %d\n", r);
|
||||
|
||||
// 获取并打印 IPSet 中的 IP 数量
|
||||
int count = get_ip_count_in_ipset(set_name);
|
||||
if (count >= 0) {
|
||||
printf("IPSet %s 中的 IP 数量: %d\n", set_name, count);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
17
libipset.h
Normal file
17
libipset.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef LIBIPSET_H
|
||||
#define LIBIPSET_H
|
||||
|
||||
#include "common.h"
|
||||
#include <libipset/ipset.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <arpa/inet.h> // 包含 inet_pton 函数
|
||||
|
||||
#define BUFFER 1024
|
||||
#define MAX_CMD_LENGTH 256 // 或者根据需要调整
|
||||
|
||||
extern int create_ipset( char *set_name);
|
||||
extern int add_ip_to_ipset(char *set_name, char *ip);
|
||||
extern int get_ip_count_in_ipset(char *set_name);
|
||||
|
||||
#endif
|
||||
315
main.c
315
main.c
@@ -1,315 +0,0 @@
|
||||
#include "main.h"
|
||||
|
||||
|
||||
void denyip_help()
|
||||
{
|
||||
puts(" DenyIp");
|
||||
puts("Linux system firewall, reject non-Chinese IP");
|
||||
puts("Email: aixiao@aixiao.me");
|
||||
puts("Version: 0.1");
|
||||
puts("Usage: ./denyip [-i eth0|-h|-?] [start|stop] ");
|
||||
puts("Options:");
|
||||
puts(" stop Enable firewall rules");
|
||||
puts(" start Disable firewall rules");
|
||||
|
||||
puts("Parameters:");
|
||||
puts(" -h|? Help info ");
|
||||
puts(" -i interface name");
|
||||
puts("");
|
||||
puts("");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* 处理僵尸进程 */
|
||||
void sigchld_handler(int signal)
|
||||
{
|
||||
while (waitpid(-1, NULL, WNOHANG) > 0) ;
|
||||
}
|
||||
|
||||
void kill_tcpdump_processes()
|
||||
{
|
||||
int result = system("pkill tcpdump"); // 用 pkill 命令终止所有 tcpdump 进程
|
||||
if (result == -1) {
|
||||
perror("Failed to kill tcpdump processes");
|
||||
}
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
// 进程重启功能
|
||||
void restart_process(pid_t pid1, pid_t pid2, char *argv[])
|
||||
{
|
||||
// 发送 SIGTERM 信号终止两个子进程
|
||||
kill(pid1, SIGTERM);
|
||||
kill(pid2, SIGTERM);
|
||||
|
||||
// 等待子进程完全退出
|
||||
waitpid(pid1, NULL, 0);
|
||||
waitpid(pid2, NULL, 0);
|
||||
|
||||
// 终止 tcpdump 进程
|
||||
kill_tcpdump_processes();
|
||||
|
||||
// 使用 execvp 重新启动程序自身
|
||||
printf("重启进程...\n");
|
||||
execvp(argv[0], argv); // 重新启动程序
|
||||
perror("execvp failed"); // 如果 execvp 出错
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
void cleanup_(int signum)
|
||||
{
|
||||
int r;
|
||||
printf("Received signal %d, cleaning up...\n", signum);
|
||||
|
||||
// 终止子进程
|
||||
if (pid1 > 0) {
|
||||
kill(pid1, SIGTERM);
|
||||
}
|
||||
if (pid2 > 0) {
|
||||
kill(pid2, SIGTERM);
|
||||
}
|
||||
|
||||
// 终止所有 tcpdump 进程
|
||||
r = system("pkill tcpdump");
|
||||
if (r == 0) {
|
||||
puts("pkill tcpdump");
|
||||
}
|
||||
|
||||
// 退出主进程
|
||||
exit(0);
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
signal(SIGCHLD, sigchld_handler); // 防止子进程变成僵尸进程
|
||||
|
||||
// 主进程设置
|
||||
//prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||||
|
||||
// 注册 SIGTERM 信号处理函数
|
||||
signal(SIGTERM, cleanup_);
|
||||
|
||||
char interface[BUFFER] = { 0 };
|
||||
strcpy(interface, "eth0");
|
||||
|
||||
int r;
|
||||
|
||||
// 参数处理
|
||||
if (argc == 2) {
|
||||
if (0 == strcmp(argv[1], "start")) {
|
||||
if ((r = system("iptables -A INPUT -p tcp -m set --match-set root src -j DROP")) == -1) {
|
||||
puts("\"iptables -A INPUT -p tcp -m set --match-set root src -j DROP\" Error!");
|
||||
}
|
||||
exit(0);
|
||||
} else if (0 == strcmp(argv[1], "stop")) {
|
||||
if ((r = system("iptables -D INPUT -p tcp -m set --match-set root src -j DROP")) == -1) {
|
||||
puts("\"iptables -D INPUT -p tcp -m set --match-set root src -j DROP\" Error!");
|
||||
}
|
||||
exit(0);
|
||||
} else if (0 == strcmp(argv[1], "-h")) {
|
||||
denyip_help();
|
||||
} else if (0 == strcmp(argv[1], "-?")) {
|
||||
denyip_help();
|
||||
}
|
||||
}
|
||||
if (argc == 3) {
|
||||
if (0 == strcmp(argv[1], "-i")) {
|
||||
strcpy(interface, argv[2]);
|
||||
}
|
||||
}
|
||||
// 判断运行用户禁止非root用户运行
|
||||
if (geteuid() == 0) {
|
||||
;
|
||||
} else {
|
||||
printf("This process is not running as root.\n");
|
||||
printf("\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// 判断网卡是否存在
|
||||
char command_ifconfig[BUFFER + 20] = { 0 };
|
||||
snprintf(command_ifconfig, BUFFER + 20, "ifconfig %s", interface);
|
||||
// 判断必要命令是否存在
|
||||
if (command_exists(command_ifconfig)) {
|
||||
;
|
||||
} else {
|
||||
puts("The network card does not exist!");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// 后台运行
|
||||
if (daemon(1, 1)) {
|
||||
perror("daemon");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 进程优先级
|
||||
if (-1 == (nice_(-20)))
|
||||
perror("nice_");
|
||||
|
||||
// 哈希集合
|
||||
if ((r = system("ipset create root hash:ip > /dev/null 2>&1")) != -1) {
|
||||
;
|
||||
}
|
||||
|
||||
// 判断必要命令是否存在
|
||||
if (command_exists("which tcpdump")) {
|
||||
;
|
||||
} else {
|
||||
r = system("yum -y install tcpdump > /dev/null 2>&1");
|
||||
r = system("apt -y install tcpdump > /dev/null 2>&1");
|
||||
}
|
||||
|
||||
|
||||
// 子进程
|
||||
pid1 = fork(); // 创建子进程
|
||||
if (pid1 == 0) {
|
||||
while (1) {
|
||||
FILE *fp = popen("ipset list root | grep \"Number of entries\" | cut -d : -f 2 | xargs", "r");
|
||||
char line[BUFFER] = { 0 };
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
line[strcspn(line, "\n")] = '\0';
|
||||
}
|
||||
|
||||
if (atoi(line) >= MAXIPSET) {
|
||||
r = system("ipset flush root");
|
||||
}
|
||||
|
||||
printf("%s\n", line);
|
||||
pclose(fp);
|
||||
sleep(3);
|
||||
}
|
||||
}
|
||||
|
||||
// 子进程
|
||||
pid2 = fork(); // 创建子进程
|
||||
if (pid2 == 0) {
|
||||
// 缓冲区用于存储每行的输出
|
||||
char line[BUFFER];
|
||||
// 要执行的命令
|
||||
char command_tcpdump[BUFFER + 256] = { 0 };
|
||||
snprintf(command_tcpdump, BUFFER + 256, "tcpdump -i %s -n 'tcp' | awk '{print $3}' | cut -d '.' -f 1-4", interface);
|
||||
|
||||
// 地域白名单
|
||||
char _region_list[WHITELIST_IP_NUM][WHITELIST_IP_NUM] = { { 0 }, { 0 } };
|
||||
char qqwry_region_list[WHITELIST_IP_NUM][WHITELIST_IP_NUM] = { { 0 }, { 0 } };
|
||||
char _REGION_LIST_COPY[BUFFER] = { 0 };
|
||||
char QQWRY_REGION_LIST_COPY[BUFFER] = { 0 };
|
||||
|
||||
if (access(xdb_path, F_OK) == -1) { // 判断 ip2region 地址定位库是否存在
|
||||
xdb_path = "ip2region/ip2region.xdb";
|
||||
if (access(xdb_path, F_OK) == -1) {
|
||||
printf("ip2region.xdb DOESN'T EXIST!\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// 打开管道来执行命令
|
||||
FILE *fp = popen(command_tcpdump, "r");
|
||||
if (fp == NULL) {
|
||||
perror("popen failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 逐行读取命令输出
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
line[strcspn(line, "\n")] = '\0';
|
||||
|
||||
if (is_valid_ip(line)) {
|
||||
|
||||
char *qqwry_region = qqwry_(line);
|
||||
if (qqwry_region == NULL) {
|
||||
printf("qqwry 解析地域错误\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
char *area = ip2region(xdb_path, line);
|
||||
if (area == NULL) {
|
||||
printf("ip2region 解析地域错误\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 取环境变量
|
||||
const char *REGION_ENV = getenv("REGION");
|
||||
if (REGION_ENV != NULL) {
|
||||
printf("REGION: %s\n", REGION_ENV);
|
||||
strcpy(_REGION_LIST_COPY, REGION_ENV);
|
||||
strcpy(QQWRY_REGION_LIST_COPY, REGION_ENV);
|
||||
} else {
|
||||
strcpy(_REGION_LIST_COPY, "局域网 内网 中国 ");
|
||||
strcpy(QQWRY_REGION_LIST_COPY, "局域网 内网 中国 ");
|
||||
}
|
||||
//printf("REGION_LIST : %s\n", _REGION_LIST_COPY);
|
||||
|
||||
split_string(QQWRY_REGION_LIST_COPY, " ", qqwry_region_list); // 分割后存储在 qqwry_region_list
|
||||
if (isregion(qqwry_region, qqwry_region_list) == 1) { // 返回1表示在白名单列表
|
||||
;
|
||||
} else {
|
||||
split_string(_REGION_LIST_COPY, " ", _region_list);
|
||||
if (isregion(area, _region_list) == 1) { // 返回1表示在白名单列表
|
||||
;
|
||||
} else {
|
||||
char ipquery_command[BUFFER + 100] = { 0 };
|
||||
snprintf(ipquery_command, BUFFER + 100, "./IP_region_query/ipquery %s", line);
|
||||
FILE *fp = popen(ipquery_command, "r");
|
||||
if (fp == NULL) {
|
||||
perror("popen failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 创建足够大的缓冲区来存储命令输出
|
||||
char buffer[1024 * 2]; // 2KB 缓冲区
|
||||
size_t bytesRead = fread(buffer, 1, sizeof(buffer) - 1, fp);
|
||||
buffer[bytesRead] = '\0';
|
||||
|
||||
pclose(fp);
|
||||
sleep(1);
|
||||
|
||||
char *p = strstr(buffer, "中国");
|
||||
if (p == NULL) {
|
||||
printf("%s %s", line, buffer);
|
||||
|
||||
char command_ipset[BUFFER + 256] = { 0 };
|
||||
snprintf(command_ipset, sizeof(command_ipset), "ipset add root %s > /dev/null 2>&1", line);
|
||||
int r = system(command_ipset);
|
||||
if (r == -1) {
|
||||
perror("system command failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(qqwry_region);
|
||||
free(area);
|
||||
|
||||
} else { // 是正确IP
|
||||
printf("%s is not a valid IPv4 address.\n", line);
|
||||
}
|
||||
} // while
|
||||
|
||||
// 关闭管道
|
||||
pclose(fp);
|
||||
}
|
||||
|
||||
|
||||
// 父进程 主进程循环,检查子进程运行情况
|
||||
int iteration = 0;
|
||||
while (1) {
|
||||
iteration++;
|
||||
|
||||
if (iteration >= 3600*3) {
|
||||
iteration = 0;
|
||||
printf("准备重启进程...\n");
|
||||
restart_process(pid1, pid2, argv);
|
||||
}
|
||||
|
||||
sleep(1); // 每次检查间隔1秒
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
26
main.h
26
main.h
@@ -1,26 +0,0 @@
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/resource.h>
|
||||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "ip2region.h"
|
||||
#include "qqwry.h"
|
||||
#include "common.h"
|
||||
|
||||
#define RED "\033[31m"
|
||||
#define RESET "\033[0m"
|
||||
#define WHITELIST_IP_NUM 1024
|
||||
#define MAXIPSET 65534
|
||||
|
||||
char *xdb_path = "ip2region.xdb";
|
||||
pid_t pid1, pid2; // 保存子进程的 PID
|
||||
|
||||
#endif
|
||||
381
qqwry/qqwry.c
381
qqwry/qqwry.c
@@ -1,381 +0,0 @@
|
||||
|
||||
#include "qqwry.h"
|
||||
|
||||
ip_data ip_defaults = {.parent_data = NULL,.child_data = NULL,.index_size = 7,.isp = 1 };
|
||||
|
||||
int qqwry_init(char *file)
|
||||
{
|
||||
int buff;
|
||||
|
||||
ip_defaults.fp = fopen(file, "r");
|
||||
if (ip_defaults.fp == NULL) {
|
||||
fprintf(stderr, "failed to open %s\n", file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
readvalue(4, &buff); //first 4 bytes represents the offset of first index
|
||||
ip_defaults.first_item = buff;
|
||||
readvalue(4, &buff);
|
||||
ip_defaults.last_item = buff;
|
||||
ip_defaults.item_number = (ip_defaults.last_item - ip_defaults.first_item) / ip_defaults.index_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qqwry_match(char *pattern, char *subject)
|
||||
{
|
||||
regex_t regex;
|
||||
int reti, ret;
|
||||
char msgbuf[100];
|
||||
|
||||
/* Compile regular expression */
|
||||
reti = regcomp(®ex, pattern, 0);
|
||||
if (reti) {
|
||||
fprintf(stderr, "Could not compile regex\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Execute regular expression */
|
||||
reti = regexec(®ex, subject, 0, NULL, 0);
|
||||
if (!reti) {
|
||||
ret = 1;
|
||||
} else if (reti == REG_NOMATCH) {
|
||||
ret = 0;
|
||||
} else {
|
||||
regerror(reti, ®ex, msgbuf, sizeof(msgbuf));
|
||||
fprintf(stderr, "Regex match failed: %s\n", msgbuf);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
/* Free memory allocated to the pattern buffer by regcomp() */
|
||||
regfree(®ex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
iconv_t initialize_iconv(const char *target, const char *src) {
|
||||
// 创建转换描述符
|
||||
iconv_t iconvDesc = iconv_open(target, src);
|
||||
|
||||
// 检查 iconv_open 是否成功
|
||||
if (iconvDesc == (iconv_t) - 1) {
|
||||
// 如果失败,打印错误信息并返回 NULL
|
||||
fprintf(stderr, "Error: Conversion from '%s' to '%s' is not available.\n", src, target);
|
||||
return (iconv_t)NULL;
|
||||
}
|
||||
|
||||
// 成功时返回 iconv_t 描述符
|
||||
return iconvDesc;
|
||||
}
|
||||
|
||||
int gbk2utf8(char *utf8_str, char *gbk_str)
|
||||
{
|
||||
iconv_t iconvDesc = initialize_iconv("UTF-8//TRANSLIT//IGNORE", "GBK");
|
||||
size_t iconv_value, len, utf8len;
|
||||
//int len_start;
|
||||
|
||||
len = strlen(gbk_str) + 1;
|
||||
if (!len) {
|
||||
fprintf(stderr, "iconvISO2UTF8: input String is empty.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Assign enough space to put the UTF-8. */
|
||||
utf8len = 3 * len;
|
||||
if (!utf8_str) {
|
||||
fprintf(stderr, "iconvISO2UTF8: Calloc failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
iconv_value = iconv(iconvDesc, &gbk_str, &len, &utf8_str, &utf8len);
|
||||
/* Handle failures. */
|
||||
if (iconv_value == (size_t)-1) {
|
||||
switch (errno) {
|
||||
/* See "man 3 iconv" for an explanation. */
|
||||
case EILSEQ:
|
||||
fprintf(stderr, "iconv failed: Invalid multibyte sequence, in string '%s', length %d, out string '%s', length %d\n", gbk_str, (int)len, utf8_str, (int)utf8len);
|
||||
break;
|
||||
case EINVAL:
|
||||
fprintf(stderr, "iconv failed: Incomplete multibyte sequence, in string '%s', length %d, out string '%s', length %d\n", gbk_str, (int)len, utf8_str, (int)utf8len);
|
||||
break;
|
||||
case E2BIG:
|
||||
fprintf(stderr, "iconv failed: No more room, in string '%s', length %d, out string '%s', length %d\n", gbk_str, (int)len, utf8_str, (int)utf8len);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "iconv failed, in string '%s', length %d, out string '%s', length %d\n", gbk_str, (int)len, utf8_str, (int)utf8len);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (iconv_close(iconvDesc) != 0) {
|
||||
fprintf(stderr, "libicon close failed: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return utf8len;
|
||||
}
|
||||
|
||||
int readbyte(int size, int offset, int *buff)
|
||||
{
|
||||
int count;
|
||||
int nbytes = 1;
|
||||
*buff = 0;
|
||||
if (ip_defaults.fp != NULL) {
|
||||
//if offset is negative,keep the current offset unchanged
|
||||
if (offset >= 0) {
|
||||
qqwry_seek(offset);
|
||||
} else {
|
||||
int curr_pos = ftell(ip_defaults.fp);
|
||||
fseek(ip_defaults.fp, curr_pos, SEEK_SET);
|
||||
}
|
||||
|
||||
if ((count = fread(buff, nbytes, size, ip_defaults.fp)) != size) {
|
||||
return -1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int readvalue(unsigned int size, int *buff)
|
||||
{
|
||||
return readbyte(size, -1, buff);
|
||||
}
|
||||
|
||||
void set_ip_range(unsigned int offset)
|
||||
{
|
||||
readbyte(4, offset, (int *)(&ip_defaults.startip));
|
||||
//skip 3 bytes to read the next ip
|
||||
qqwry_forward(3);
|
||||
readvalue(4, (int *)(&ip_defaults.endip));
|
||||
}
|
||||
|
||||
void qqwry_seek(int offset)
|
||||
{
|
||||
fseek(ip_defaults.fp, offset, SEEK_SET);
|
||||
}
|
||||
|
||||
void qqwry_forward(unsigned int byte)
|
||||
{
|
||||
fseek(ip_defaults.fp, byte, SEEK_CUR);
|
||||
}
|
||||
|
||||
void qqwry_back(unsigned int byte)
|
||||
{
|
||||
int currPos = ftell(ip_defaults.fp);
|
||||
qqwry_seek(currPos - byte);
|
||||
}
|
||||
|
||||
|
||||
char *long2ip(int ip) {
|
||||
// 分配16字节内存用于存储IP字符串
|
||||
char *ip_str = malloc(16 * sizeof(char));
|
||||
|
||||
if (ip_str == NULL) {
|
||||
// 如果内存分配失败,返回NULL
|
||||
fprintf(stderr, "Memory allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 将IP转换为字符串
|
||||
snprintf(ip_str, 16, "%d.%d.%d.%d",
|
||||
(ip >> 24) & 0xFF,
|
||||
(ip >> 16) & 0xFF,
|
||||
(ip >> 8) & 0xFF,
|
||||
ip & 0xFF);
|
||||
|
||||
return ip_str;
|
||||
}
|
||||
|
||||
unsigned int ip2long(char *ip)
|
||||
{
|
||||
int nip = 0, tmp = 0, step = 24;
|
||||
char *copy = strdup(ip);
|
||||
char *token = strtok(copy, ".");
|
||||
|
||||
while (token) {
|
||||
tmp = (unsigned int)atoi(token);
|
||||
tmp <<= step;
|
||||
nip += tmp;
|
||||
step -= 8;
|
||||
token = strtok(NULL, ".");
|
||||
}
|
||||
free(copy);
|
||||
return nip;
|
||||
}
|
||||
|
||||
int search_record(char *ip)
|
||||
{
|
||||
int numeric_ip = ip2long(ip);
|
||||
int low = 0;
|
||||
int high = ip_defaults.item_number;
|
||||
return binary_search(low, high, numeric_ip);
|
||||
}
|
||||
|
||||
int binary_search(int low, int high, int ip)
|
||||
{
|
||||
unsigned int mid, offset, startip, endip;
|
||||
|
||||
if (low <= high) {
|
||||
mid = low + (high - low) / 2;
|
||||
offset = round(ip_defaults.first_item + mid * ip_defaults.index_size);
|
||||
set_ip_range(offset);
|
||||
startip = ip_defaults.startip;
|
||||
endip = ip_defaults.endip;
|
||||
if (ip >= startip && ip <= endip) {
|
||||
return offset;
|
||||
}
|
||||
//if ip is below the lower limit, decrease the upper limit
|
||||
if (ip < startip) {
|
||||
return binary_search(low, mid - 1, ip);
|
||||
}
|
||||
//if ip is above the lower limit, increase the lower limit
|
||||
return binary_search(mid + 1, high, ip);
|
||||
}
|
||||
return ip_defaults.last_item;
|
||||
}
|
||||
|
||||
static char *get_string()
|
||||
{
|
||||
unsigned int buff = 0;
|
||||
char *str = realloc(NULL, sizeof(char));
|
||||
char *tmp;
|
||||
int i = 0, c = 0;
|
||||
|
||||
if ((c = readvalue(1, (int *)(&buff))) != 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; buff != 0; i++) {
|
||||
str[i] = buff;
|
||||
tmp = realloc(str, (sizeof(char)) * (i + 2));
|
||||
str = tmp;
|
||||
readvalue(1, (int *)(&buff));
|
||||
}
|
||||
str[i] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *get_child_data()
|
||||
{
|
||||
unsigned int flag, offset;
|
||||
readvalue(1, (int *)(&flag));
|
||||
if (flag == 0) { //no child data
|
||||
return 0;
|
||||
} else if (flag == 1 || flag == 2) { // redirection for child data
|
||||
readvalue(3, (int *)(&offset));
|
||||
qqwry_seek(offset);
|
||||
return get_string();
|
||||
}
|
||||
// no redirection for child data
|
||||
qqwry_back(1);
|
||||
return get_string();
|
||||
}
|
||||
|
||||
int convert_data(char *parent_data, char *child_data)
|
||||
{
|
||||
ip_defaults.parent_data = malloc(strlen(parent_data) * 3); //in utf8,one chinese character could consume up to 3 bytes
|
||||
gbk2utf8(ip_defaults.parent_data, parent_data);
|
||||
ip_defaults.child_data = malloc(strlen(child_data) * 3);
|
||||
gbk2utf8(ip_defaults.child_data, child_data);
|
||||
|
||||
if (qqwry_match("移动", ip_defaults.child_data)) {
|
||||
ip_defaults.isp = 0x03;
|
||||
} else if (qqwry_match("联通", ip_defaults.child_data)) {
|
||||
ip_defaults.isp = 0x02;
|
||||
} else {
|
||||
ip_defaults.isp = 0x01;
|
||||
}
|
||||
free(parent_data);
|
||||
free(child_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qqwry_redirect(int bytes)
|
||||
{
|
||||
int redirect_offset;
|
||||
readvalue(bytes, &redirect_offset);
|
||||
qqwry_seek(redirect_offset);
|
||||
return redirect_offset;
|
||||
}
|
||||
|
||||
int get_data(int offset)
|
||||
{ //get record data
|
||||
int flag, redirect_offset;
|
||||
char *parent_data, *child_data;
|
||||
readbyte(1, offset + 4, &flag); //get the flag value to see if the data is stored elsewhere
|
||||
|
||||
if (flag == 1) { //this means we should look elsewhere for both
|
||||
redirect_offset = qqwry_redirect(3); //read 3 bytes to get a new offset and redirect there
|
||||
readvalue(1, &flag);
|
||||
if (flag == 2) {
|
||||
// child data is elsewhere
|
||||
qqwry_redirect(3);
|
||||
parent_data = get_string();
|
||||
qqwry_seek(redirect_offset + 4);
|
||||
child_data = get_child_data();
|
||||
} else { // no redirection for parent data
|
||||
qqwry_back(1);
|
||||
parent_data = get_string();
|
||||
child_data = get_child_data();
|
||||
}
|
||||
} else if (flag == 2) { //redirection for only parent
|
||||
qqwry_redirect(3);
|
||||
parent_data = get_string();
|
||||
qqwry_seek(offset + 8);
|
||||
child_data = get_child_data();
|
||||
} else { // no redirection for both parent and child
|
||||
qqwry_back(1);
|
||||
parent_data = get_string();
|
||||
child_data = get_string();
|
||||
}
|
||||
|
||||
convert_data(parent_data, child_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_location(char *ip)
|
||||
{
|
||||
//offset is the address where the ip is found. first 4 bytes is the start ip address of the ip range and the following 3 bytes is the offset pointing to the actual record data;
|
||||
unsigned int offset = search_record(ip);
|
||||
unsigned int tmp_offset;
|
||||
qqwry_seek(offset + 4); // skip 4 byte to get the offset value pointing to record data
|
||||
readvalue(3, (int *)(&tmp_offset)); // the offset pointing to the data
|
||||
get_data(tmp_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *qqwry_(char *ip)
|
||||
{
|
||||
char *qqdb_path = "qqwry.dat";
|
||||
if (access(qqdb_path, F_OK) == -1) { // 判断 ip2region 地址定位库是否存在
|
||||
qqdb_path = "qqwry/qqwry.dat";
|
||||
if (access(qqdb_path, F_OK) == -1) {
|
||||
printf("qqwry.dat DOESN'T EXIST!\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
qqwry_init(qqdb_path);
|
||||
|
||||
|
||||
get_location(ip);
|
||||
//printf("%s-%s %d\n", ip_defaults.parent_data, ip_defaults.child_data, ip_defaults.isp);
|
||||
//printf("QQWRY %s %s-%s\n", ip, ip_defaults.parent_data, ip_defaults.child_data);
|
||||
|
||||
// 计算拼接后的字符串所需的长度
|
||||
size_t len = strlen(ip_defaults.parent_data) + strlen(ip_defaults.child_data) + 2; // +2 for the hyphen and null terminator
|
||||
char *result = malloc(len);
|
||||
|
||||
if (result) {
|
||||
// 拼接字符串,格式为 "parent_data-child_data"
|
||||
snprintf(result, len, "%s-%s", ip_defaults.parent_data, ip_defaults.child_data);
|
||||
}
|
||||
|
||||
free(ip_defaults.parent_data);
|
||||
free(ip_defaults.child_data);
|
||||
fclose(ip_defaults.fp);
|
||||
|
||||
return result;
|
||||
}
|
||||
BIN
qqwry/qqwry.dat
BIN
qqwry/qqwry.dat
Binary file not shown.
@@ -1,31 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <iconv.h>
|
||||
#include <errno.h>
|
||||
#include <regex.h>
|
||||
|
||||
typedef struct {
|
||||
FILE *fp;
|
||||
unsigned int index_size;
|
||||
unsigned int first_item, last_item;
|
||||
unsigned int item_number, startip, endip, curr_data_offset;
|
||||
char *parent_data, *child_data;
|
||||
int isp;
|
||||
} ip_data;
|
||||
|
||||
int search_record(char *ip);
|
||||
int binary_search(int low, int high, int ip);
|
||||
int readbyte(int size, int offset, int *buff);
|
||||
int readvalue(unsigned int size, int *buff);
|
||||
void qqwry_seek(int offset);
|
||||
void qqwry_forward(unsigned int byte);
|
||||
void qqwry_back(unsigned int byte);
|
||||
int get_location(char *ip);
|
||||
int gbk2utf8(char *utf8_str, char *gbk_str);
|
||||
|
||||
extern char *qqwry_(char *ip);
|
||||
BIN
qqwry/qqwry.o
BIN
qqwry/qqwry.o
Binary file not shown.
Reference in New Issue
Block a user