├── .gitignore ├── LICENSE ├── README.md ├── build ├── unicornDNS_amd64.tar.gz ├── unicornDNS_arm.tar.gz ├── unicornDNS_arm64.tar.gz └── unicornDNS_x86_64.tar.gz ├── cache.go ├── config.go ├── dns_server.go ├── doc ├── RouterOS7_example.md ├── realese-notes.md └── rules.md ├── example ├── config_iptables.yaml └── config_nftables.yaml ├── go.mod ├── go.sum ├── http.go ├── install_easy.sh ├── main.go ├── net.go ├── rules.go ├── sh ├── iptables │ ├── add.sh │ ├── delete.sh │ └── onReset.sh └── nftables │ ├── add.sh │ ├── delete.sh │ ├── onReset.sh │ └── onStart.sh └── src ├── tmp_config_iptables.yaml └── tmp_config_nftables.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | /trash 2 | config.yaml 3 | rules.list 4 | /unicornDNS 5 | build.sh -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Это DNS сервер который на базе конфигураций ShadowRocket поможет обходить точечно блокировки подменяя адрес на локальный (fakeip). 2 | 3 | * Поддерживает ipv4/ipv6 4 | * Поддерживает все виды записей включая RRSIG (dnssec) 5 | * Правила имеют формат like ShadowRocket/ClashX 6 | * Кэширует запросы 7 | 8 | # Сборка и настройка проекта UnicornDNS 9 | 10 | ## 1. Установка 11 | 12 | ```sh 13 | sudo bash -c "$(curl -fsSL https://github.com/unicorn-style/unicornDNS/raw/main/install_easy.sh)" 14 | ``` 15 | 16 | ### Шаги для самостоятельной сборки 17 | 1. Сборка сервера: 18 | ```sh 19 | git clone https://github.com/unicorn-style/unicornDNS/ 20 | cd unicornDNS 21 | go build 22 | ``` 23 | В папке sh/iptables находятся sh скрипты. Им нужно дать право на исполнение 24 | ```sh 25 | sudo chmod +x sh/iptables 26 | # !!! Скрипты являются примером. В каждом скрипте нужно поменять 27 | # действующий интерфейс (сейчас там указан ens3) 28 | ``` 29 | Пример запуска 30 | ```sh 31 | ./unicornDNS -config example/config.yaml -rules example/rules.txt 32 | ``` 33 | 34 | Правила настраиваются по списку который -rules config/rules.txt. Пример не сложный. В текущей реализации он распознает только: 35 | 36 | * DOMAIN-SUFFIX – любой домен *.google.com или 37 | * DOMAIN-KEYWORD,google,PROXY - любое google.am, mail.google.com подходит под этот фильтр. Все что не подошло под правила – пропускается без каких-либо fakeip как есть. 38 | * RULE-SET - скачать список (в примере так же есть все) 39 | 40 | ## 2. Дополнительно 41 | 42 | Я встроил HTTP сервер для возможности удаленного сброса или обновления rules. 43 | * "/clearcache" Сброс кэша DNS с удалением правил 44 | * "/reload" Перезагрузка правил (RULES) 45 | * "/ips" Просмотр аренды fakeip адресов 46 | * "/rules" Список действующих правил 47 | 48 | При каких-либо проблемах я предусмотрел скрипты для отчистки. Сам сервер по завершению отчищает, но мало ли :) 49 | * sh/iptables/ipv4_reset_all_rules.sh 50 | * sh/iptables/ipv6_reset_all_rules.sh 51 | 52 | ### [Пример реализации](doc/ROS7.md) 53 | 54 | ### [Пример простой конфигурации](example/config_iptables.yaml) 55 | 56 | ### [Пример правил](example/rules.txt) 57 | 58 | ### [Документация по правилам](doc/rules_ru.md) -------------------------------------------------------------------------------- /build/unicornDNS_amd64.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unicorn-style/unicornDNS/25983e92708616d334cda53975fa317ad3647c40/build/unicornDNS_amd64.tar.gz -------------------------------------------------------------------------------- /build/unicornDNS_arm.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unicorn-style/unicornDNS/25983e92708616d334cda53975fa317ad3647c40/build/unicornDNS_arm.tar.gz -------------------------------------------------------------------------------- /build/unicornDNS_arm64.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unicorn-style/unicornDNS/25983e92708616d334cda53975fa317ad3647c40/build/unicornDNS_arm64.tar.gz -------------------------------------------------------------------------------- /build/unicornDNS_x86_64.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unicorn-style/unicornDNS/25983e92708616d334cda53975fa317ad3647c40/build/unicornDNS_x86_64.tar.gz -------------------------------------------------------------------------------- /cache.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "strings" 6 | "sync" 7 | "time" 8 | 9 | "github.com/miekg/dns" 10 | ) 11 | 12 | /*var ( 13 | cache []cacheAnswer 14 | cacheMutex sync.Mutex 15 | cacheChan = make(chan cacheTask) 16 | )*/ 17 | 18 | type CacheEntry struct { 19 | Action string 20 | Domain string 21 | Type uint16 22 | RealIP string 23 | AllocatedIPs string 24 | TTL uint32 25 | Expiry time.Time 26 | Msg []dns.RR 27 | } 28 | 29 | type cacheTask struct { 30 | Action string 31 | Request cacheRequest 32 | Response chan cacheResponse 33 | } 34 | 35 | type cacheRequest struct { 36 | Domain string 37 | Type uint16 38 | Entry CacheEntry 39 | Data []CacheEntry 40 | Msg *dns.Msg 41 | } 42 | 43 | type cacheAnswer struct { 44 | Domain string 45 | Type uint16 46 | Action string 47 | Msg *dns.Msg 48 | Entry []CacheEntry 49 | } 50 | 51 | type cacheResponse struct { 52 | Found bool 53 | Entry CacheEntry 54 | IsValid bool 55 | Msg *dns.Msg 56 | } 57 | 58 | var ( 59 | cache = make(map[string]cacheAnswer) 60 | cacheMutex sync.Mutex 61 | cacheChan = make(chan cacheTask) 62 | ) 63 | 64 | func cacheHandler() { 65 | for task := range cacheChan { 66 | cacheMutex.Lock() 67 | now := time.Now() 68 | 69 | // Очистка истекших записей в кэше 70 | for domain, answer := range cache { 71 | validEntries := []CacheEntry{} 72 | aCount := 0 73 | minTTL := time.Second * 5 74 | 75 | for _, entry := range answer.Entry { 76 | ttl := entry.Expiry.Sub(now).Seconds() 77 | var remainingTTL uint32 78 | if ttl > 0 { 79 | remainingTTL = uint32(ttl) 80 | } else { 81 | remainingTTL = 0 82 | } 83 | action, ok := config.Actions[entry.Action] 84 | if remainingTTL >= uint32(minTTL.Seconds()) { 85 | if len(entry.Msg) > 0 && entry.Msg[0].Header() != nil { 86 | sendTTL := remainingTTL 87 | if ok && sendTTL >= action.TTL.MaxTrasfer { 88 | sendTTL = action.TTL.MaxTrasfer 89 | } 90 | entry.Msg[0].Header().Ttl = sendTTL 91 | } 92 | if len(entry.Msg) > 0 && (entry.Msg[0].Header().Rrtype == dns.TypeA || entry.Msg[0].Header().Rrtype == dns.TypeAAAA) { 93 | aCount++ 94 | } 95 | 96 | newEntry := CacheEntry{ 97 | Action: entry.Action, 98 | Domain: entry.Domain, 99 | Type: entry.Type, 100 | RealIP: entry.RealIP, 101 | AllocatedIPs: entry.AllocatedIPs, 102 | TTL: remainingTTL, 103 | Expiry: entry.Expiry, 104 | Msg: entry.Msg, 105 | } 106 | 107 | validEntries = append(validEntries, newEntry) 108 | } 109 | } 110 | 111 | if len(validEntries) > 0 && aCount > 0 { 112 | answer.Entry = validEntries 113 | cache[domain] = answer 114 | } else { 115 | delete(cache, domain) 116 | } 117 | } 118 | 119 | var response cacheResponse 120 | 121 | switch task.Action { 122 | case "Add": 123 | domain := strings.TrimSuffix(task.Request.Domain, ".") 124 | answer, found := cache[domain] 125 | 126 | if found && answer.Type == task.Request.Entry.Type { 127 | answer.Entry = append(answer.Entry, task.Request.Entry) 128 | cache[domain] = answer 129 | } else { 130 | cache[domain] = cacheAnswer{ 131 | Domain: task.Request.Domain, 132 | Type: task.Request.Type, 133 | Entry: task.Request.Data, 134 | Msg: task.Request.Msg, 135 | } 136 | } 137 | 138 | response = cacheResponse{Found: true, Entry: task.Request.Entry, IsValid: true} 139 | 140 | case "Search": 141 | domain := strings.TrimSuffix(task.Request.Domain, ".") 142 | answer, found := cache[domain] 143 | 144 | if found && answer.Type == task.Request.Type { 145 | foundEntries := []dns.RR{} 146 | foundAnswer := answer.Msg.Copy() 147 | 148 | for _, entry := range answer.Entry { 149 | if entry.Expiry.After(now) { 150 | 151 | if len(entry.Msg) > 0 { 152 | foundEntries = append(foundEntries, entry.Msg[0]) 153 | } 154 | } 155 | } 156 | 157 | if len(foundEntries) > 0 { 158 | var combinedMsg dns.Msg 159 | combinedMsg.Answer = append(combinedMsg.Answer, foundEntries...) 160 | foundAnswer.Answer = combinedMsg.Answer 161 | 162 | response = cacheResponse{ 163 | Found: true, 164 | Entry: CacheEntry{ 165 | Domain: answer.Domain, 166 | Type: task.Request.Type, 167 | Msg: combinedMsg.Answer, 168 | }, 169 | IsValid: true, 170 | Msg: foundAnswer, 171 | } 172 | } 173 | } 174 | 175 | case "Delete": 176 | domain := strings.TrimSuffix(task.Request.Domain, ".") 177 | answer, found := cache[domain] 178 | 179 | if found && answer.Type == task.Request.Type { 180 | validEntries := []CacheEntry{} 181 | for _, entry := range answer.Entry { 182 | if !(strings.TrimSuffix(entry.Domain, ".") == task.Request.Domain && entry.Type == task.Request.Type) { 183 | validEntries = append(validEntries, entry) 184 | } 185 | } 186 | 187 | if len(validEntries) > 0 { 188 | answer.Entry = validEntries 189 | cache[domain] = answer 190 | } else { 191 | delete(cache, domain) 192 | } 193 | } 194 | response = cacheResponse{Found: false, IsValid: true} 195 | 196 | case "Reset": 197 | for ip := range ipMappings { 198 | removeRoute(ip) 199 | } 200 | cache = make(map[string]cacheAnswer) 201 | response = cacheResponse{Found: false, IsValid: true} 202 | } 203 | 204 | cacheMutex.Unlock() 205 | task.Response <- response 206 | } 207 | } 208 | 209 | func WriteCache(domains string, t uint16, ch []CacheEntry, msg *dns.Msg) { 210 | //fmt.Printf("111 %v\n", domains) 211 | writeCache := cacheRequest{ 212 | Domain: domains, 213 | Type: t, 214 | Data: ch, 215 | Msg: msg, 216 | } 217 | //fmt.Printf("WRITE t: %v\n", ch) 218 | cacheResp := make(chan cacheResponse) 219 | cacheChan <- cacheTask{Action: "Add", Request: writeCache, Response: cacheResp} 220 | <-cacheResp 221 | } 222 | 223 | func СlearCache() { 224 | log.Println("RESET ALL CACHE") 225 | cacheResp := make(chan cacheResponse) 226 | cacheChan <- cacheTask{Action: "RESET", Response: cacheResp} 227 | <-cacheResp 228 | log.Println("RESET DONE!") 229 | } 230 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "regexp" 7 | "strings" 8 | "sync" 9 | 10 | "gopkg.in/yaml.v3" 11 | ) 12 | 13 | type Config struct { 14 | Server struct { 15 | BindAddress string `yaml:"bind-address"` 16 | HttpServer string `yaml:"http-server"` 17 | DNSForward string `yaml:"dns-forward"` 18 | } `yaml:"server"` 19 | Networks map[string]Network `yaml:"networks"` 20 | Actions map[string]Action `yaml:"actions"` 21 | } 22 | 23 | type Network struct { 24 | IPv4 string `yaml:"ipv4"` 25 | IPv6 string `yaml:"ipv6"` 26 | } 27 | 28 | type Action struct { 29 | Mark string `yaml:"mark"` 30 | DNSForward string `yaml:"dns-forward"` 31 | Method string `yaml:"method"` 32 | FakeIPDelay uint32 `yaml:"fakeip-release-delay"` 33 | Variable map[string]string `yaml:"variable"` 34 | TTL TTL `yaml:"ttl"` 35 | FakeIPNet []string `yaml:"fakeip-networks"` 36 | Script Scripts `yaml:"script"` 37 | } 38 | 39 | type Scripts struct { 40 | Add string `yaml:"add"` 41 | Delete string `yaml:"delete"` 42 | OnStart string `yaml:"onstart"` 43 | OnReset string `yaml:"onreset"` 44 | } 45 | type TTL struct { 46 | MaxTrasfer uint32 `yaml:"max-transfer"` 47 | MinRewrite uint32 `yaml:"min-rewrite"` 48 | MaxRewrite uint32 `yaml:"max-rewrite"` 49 | } 50 | 51 | var ( 52 | config Config 53 | ipv4Pools map[string][]IPEntry 54 | ipv6Pools map[string][]IPEntry 55 | mutexes map[string]*sync.Mutex 56 | validNameRe = regexp.MustCompile(`^[a-zA-Z0-9_]+$`) 57 | ) 58 | 59 | func replaceVariables(text string, variables map[string]string) string { 60 | for key, value := range variables { 61 | // Меняем все вхождения переменных вида {key} на их значение 62 | placeholder := "{" + key + "}" 63 | text = strings.ReplaceAll(text, placeholder, value) 64 | } 65 | //log.Printf(text) 66 | return text 67 | } 68 | 69 | func loadConfig(filename string) { 70 | data, err := os.ReadFile(filename) 71 | if err != nil { 72 | log.Fatalf("Failed to read config file: %v", err) 73 | } 74 | 75 | err = yaml.Unmarshal(data, &config) 76 | if err != nil { 77 | log.Fatalf("Failed to parse config file: %v", err) 78 | } 79 | 80 | ipv4Pools = make(map[string][]IPEntry) 81 | ipv6Pools = make(map[string][]IPEntry) 82 | mutexes = make(map[string]*sync.Mutex) 83 | allocatedMutex = &sync.Mutex{} 84 | 85 | for name, network := range config.Networks { 86 | if !validNameRe.MatchString(name) { 87 | log.Fatalf("Invalid network name: %s", name) 88 | } 89 | log.Printf("CONFIG NET %s", name) 90 | if len(network.IPv4) > 0 { 91 | ipv4Pools[name] = generateIPPool(network.IPv4) 92 | } 93 | if len(network.IPv6) > 0 { 94 | ipv6Pools[name] = generateIPPool(network.IPv6) 95 | } 96 | mutexes[name] = &sync.Mutex{} 97 | } 98 | 99 | // Проходим по всем действиям и применяем замену переменных 100 | for actionName, action := range config.Actions { 101 | if len(action.Variable) > 0 { 102 | action.Script.Add = replaceVariables(action.Script.Add, action.Variable) 103 | action.Script.Delete = replaceVariables(action.Script.Delete, action.Variable) 104 | action.Script.OnStart = replaceVariables(action.Script.OnStart, action.Variable) 105 | action.Script.OnReset = replaceVariables(action.Script.OnReset, action.Variable) 106 | } 107 | config.Actions[actionName] = action 108 | log.Printf("Action %s processed", actionName) 109 | ScriptOnStart(actionName) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /dns_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os/exec" 7 | "strings" 8 | "sync" 9 | "time" 10 | 11 | "github.com/miekg/dns" 12 | ) 13 | 14 | var mu sync.Mutex 15 | 16 | func expireTime(i uint32) time.Time { 17 | return time.Now().Add(time.Duration(i) * time.Second) 18 | } 19 | 20 | // Input DNS Request 21 | func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { 22 | msg := dns.Msg{} 23 | msg.SetReply(r) 24 | //log.Printf("Received DNS request from %s\n", w.RemoteAddr().String()) 25 | //log.Printf("DNS Request:\n%s\n", r.String()) 26 | 27 | dnssecOK := false 28 | for _, extra := range r.Extra { 29 | if opt, ok := extra.(*dns.OPT); ok { 30 | if opt.Do() { 31 | dnssecOK = true 32 | break 33 | } 34 | } 35 | } 36 | // Check DNSSEC & Recursion 37 | if r.RecursionDesired { 38 | msg.RecursionAvailable = true 39 | } 40 | 41 | msg.AuthenticatedData = true 42 | 43 | for _, question := range r.Question { 44 | handleQuestion(w, r, &msg, &question, dnssecOK) 45 | } 46 | } 47 | 48 | func handleQuestion(w dns.ResponseWriter, r *dns.Msg, msg *dns.Msg, question *dns.Question, dnssec bool) { 49 | domain := strings.TrimSuffix(question.Name, ".") 50 | log.Printf("Received query for: [%s], type: []\n", domain) 51 | //NewMsg := new(dns.Msg) 52 | //NewMsg.Question = msg.Copy().Question 53 | //fmt.Printf("r.Response: %v\n", r) 54 | 55 | // Passthrought any request not AAAA/A/ 56 | /*if question.Qtype != dns.TypeA && question.Qtype != dns.TypeAAAA && question.Qtype != dns.TypeCNAME { 57 | log.Printf("Forwarding query for %s, type: %d to upstream server\n", domain, question.Qtype) 58 | ForwardAndRespond(w, r, msg, config.Server.DNSForward) 59 | return 60 | }*/ 61 | 62 | //Request to cache 63 | searchRequest := cacheRequest{Domain: question.Name, Type: question.Qtype} 64 | cacheResp := make(chan cacheResponse) 65 | cacheChan <- cacheTask{Action: "Search", Request: searchRequest, Response: cacheResp} 66 | resp := <-cacheResp 67 | 68 | if resp.Found { 69 | //CACHE FOUND 70 | log.Printf("START Cache hit for %s %s %v\n", domain, msg.Extra, len(msg.Answer)) 71 | msg.Answer = append(msg.Answer, resp.Entry.Msg...) 72 | //log.Printf("CACHE num %v\n", len(msg.Answer)) 73 | //log.Printf("CACHE { \n\n\n\n\n %v\n\n\n\n }", resp.Entry.Msg) 74 | w.WriteMsg(msg) 75 | //log.Printf("END CACHE HIT %s %s\n", domain, msg.Extra) 76 | return 77 | } 78 | 79 | rule, matched := MatchRules(domain, Rules) 80 | var prepareCache []CacheEntry 81 | if matched { 82 | actionName := rule.Action 83 | _, ok := config.Actions[actionName] 84 | if ok { 85 | cDNS := config.Actions[actionName].DNSForward 86 | if len(cDNS) == 0 { 87 | cDNS = config.Server.DNSForward 88 | } 89 | log.Printf("Match: %s, DNS: %s [%s]\n", rule.Type, cDNS, actionName) 90 | records, ns, extra := forwardDNSRequest(r, cDNS, dnssec) 91 | msg.Ns = append(msg.Ns, ns...) 92 | msg.Extra = append(msg.Extra, extra...) 93 | if config.Actions[actionName].Method == "fakeip" { 94 | var countAnswer uint32 = 0 95 | //var Newttl uint32 96 | for _, record := range records { 97 | //ttl constructor 98 | sendTTL := record.Header().Ttl 99 | //ttl.min-rewrite 100 | if record.Header().Ttl < config.Actions[actionName].TTL.MinRewrite { 101 | sendTTL = config.Actions[actionName].TTL.MinRewrite 102 | } 103 | if record.Header().Ttl > config.Actions[actionName].TTL.MaxRewrite { 104 | sendTTL = config.Actions[actionName].TTL.MaxRewrite 105 | } 106 | countAnswer++ 107 | switch record := record.(type) { 108 | case *dns.A: 109 | prepareCache = append(prepareCache, handleARecord(record.Header().Name, record.A.String(), sendTTL, actionName, msg)) 110 | case *dns.AAAA: 111 | prepareCache = append(prepareCache, handleAAAARecord(record.Header().Name, record.AAAA.String(), sendTTL, actionName, msg)) 112 | case *dns.CNAME: 113 | prepareCache = append(prepareCache, handleCNAMERecord(record.Header().Name, record.Target, sendTTL, msg)) 114 | default: 115 | log.Printf("RULE DNS: %s, DNS: %s [%s]\n", record.String(), cDNS, actionName) 116 | // Add original line to msg.Answer 117 | msg.Answer = append(msg.Answer, record) 118 | var Line []dns.RR 119 | Line = append(Line, record) 120 | 121 | // Добавляем в кеш оригинальную запись 122 | prepareCache = append(prepareCache, CacheEntry{ 123 | Domain: domain, 124 | Type: record.Header().Rrtype, 125 | AllocatedIPs: "", 126 | Expiry: expireTime(sendTTL), 127 | Msg: Line, 128 | }) 129 | } 130 | 131 | } 132 | } else { 133 | ForwardAndRespond(w, r, msg, cDNS) 134 | } 135 | } else { 136 | ForwardAndRespond(w, r, msg, config.Server.DNSForward) 137 | } 138 | } else { 139 | records, ns, extra := forwardDNSRequest(r, config.Server.DNSForward, dnssec) 140 | msg.Ns = append(msg.Ns, ns...) 141 | msg.Extra = append(msg.Extra, extra...) 142 | for _, record := range records { 143 | switch record := record.(type) { 144 | case *dns.A: 145 | prepareCache = append(prepareCache, handleARecord(record.Header().Name, record.A.String(), record.Header().Ttl, "", msg)) 146 | case *dns.AAAA: 147 | prepareCache = append(prepareCache, handleAAAARecord(record.Header().Name, record.AAAA.String(), record.Header().Ttl, "", msg)) 148 | case *dns.CNAME: 149 | prepareCache = append(prepareCache, handleCNAMERecord(record.Header().Name, record.Target, record.Header().Ttl, msg)) 150 | default: 151 | log.Printf("DIRECT DNS\n") 152 | // Добавляем оригинальную запись в msg.Answer 153 | msg.Answer = append(msg.Answer, record) 154 | var Line []dns.RR 155 | Line = append(Line, record) 156 | // Добавляем в кеш оригинальную запись 157 | prepareCache = append(prepareCache, CacheEntry{ 158 | Domain: domain, 159 | Type: record.Header().Rrtype, // Устанавливаем тип записи 160 | AllocatedIPs: "", // Для не A/AAAA записей AllocatedIPs не нужны 161 | Expiry: expireTime(record.Header().Ttl), 162 | Msg: Line, 163 | }) 164 | } 165 | } 166 | 167 | } 168 | //log.Printf("DIRECT DNS: %s %v\n", domain, question.Qtype, prepareCache) 169 | WriteCache(domain, question.Qtype, prepareCache, msg.Copy()) 170 | w.WriteMsg(msg) 171 | } 172 | 173 | // Handlers record 174 | func handleARecord(name, ip string, ttl uint32, actionName string, msg *dns.Msg) CacheEntry { 175 | log.Printf("Handling A record for %s\n", name) 176 | _, ok := config.Actions[actionName] 177 | if actionName != "" && ok { 178 | allocatedIPs := addRoute(name, "A", ip, ttl, actionName) 179 | if len(allocatedIPs) > 0 { 180 | //log.Printf("ADDROUTE: %s - %s\n", allocatedIPs, ip) 181 | var Line []dns.RR 182 | //ttl.max-transfare 183 | sendTTL := ttl 184 | if ttl > config.Actions[actionName].TTL.MaxTrasfer { 185 | sendTTL = config.Actions[actionName].TTL.MaxTrasfer 186 | } 187 | Line = append(Line, createARecord(name, allocatedIPs, sendTTL)) 188 | msg.Answer = append(msg.Answer, Line...) 189 | prepareCache := CacheEntry{ 190 | Action: actionName, 191 | Domain: name, 192 | Type: dns.TypeA, 193 | RealIP: ip, 194 | AllocatedIPs: allocatedIPs, 195 | Expiry: expireTime(ttl), 196 | Msg: Line, 197 | } 198 | return prepareCache 199 | } 200 | return CacheEntry{} 201 | //fmt.Printf("msg.Copy(): %v\n", msg.Copy()) 202 | } else { 203 | log.Printf("Passthrought: %s, DNS: %s\n", name, config.Server.DNSForward) 204 | msg.Answer = append(msg.Answer, createARecord(name, ip, ttl)) 205 | return CacheEntry{} 206 | } 207 | } 208 | 209 | func handleAAAARecord(name, ip string, ttl uint32, actionName string, msg *dns.Msg) CacheEntry { 210 | log.Printf("Handling AAAA record for %s\n", name) 211 | _, ok := config.Actions[actionName] 212 | if actionName != "" && ok { 213 | allocatedIPs := addRoute(name, "AAAA", ip, ttl, actionName) 214 | if len(allocatedIPs) > 0 { 215 | //log.Printf("ADDROUTE: %s - %s\n", allocatedIPs, ip) 216 | 217 | var Line []dns.RR 218 | //Line = append(Line, createAAAARecord(name, allocatedIPs[0], ttl)) 219 | //ttl.max-transfare 220 | sendTTL := ttl 221 | if ttl > config.Actions[actionName].TTL.MaxTrasfer { 222 | sendTTL = config.Actions[actionName].TTL.MaxTrasfer 223 | } 224 | Line = append(Line, createAAAARecord(name, allocatedIPs, sendTTL)) 225 | msg.Answer = append(msg.Answer, Line...) 226 | prepareCache := CacheEntry{ 227 | Action: actionName, 228 | Domain: name, 229 | Type: dns.TypeAAAA, 230 | RealIP: ip, 231 | AllocatedIPs: allocatedIPs, 232 | Expiry: expireTime(ttl), 233 | Msg: Line, 234 | } 235 | return prepareCache 236 | } 237 | return CacheEntry{} 238 | } else { 239 | log.Printf("Passthrought: %s, DNS: %s\n", name, config.Server.DNSForward) 240 | msg.Answer = append(msg.Answer, createAAAARecord(name, ip, ttl)) 241 | return CacheEntry{} 242 | } 243 | } 244 | 245 | func handleCNAMERecord(name, target string, ttl uint32, msg *dns.Msg) CacheEntry { 246 | var Line []dns.RR 247 | Line = append(Line, createCNAMERecord(name, target, ttl)) 248 | msg.Answer = append(msg.Answer, Line...) 249 | //msg.Answer = append(msg.Answer, createCNAMERecord(name, target, ttl)) 250 | //expiry := time.Now().Add(time.Duration(ttl) * time.Second) // Вычисляем время конца 251 | prepareCache := CacheEntry{ 252 | Domain: name, 253 | Type: dns.TypeCNAME, 254 | RealIP: target, 255 | Expiry: expireTime(ttl), 256 | Msg: Line, 257 | } 258 | return prepareCache 259 | } 260 | 261 | func createARecord(name, ip string, ttl uint32) dns.RR { 262 | rr, _ := dns.NewRR(fmt.Sprintf("%s %d IN A %s", name, ttl, ip)) 263 | return rr 264 | } 265 | 266 | func createAAAARecord(name, ip string, ttl uint32) dns.RR { 267 | rr, _ := dns.NewRR(fmt.Sprintf("%s %d IN AAAA %s", name, ttl, ip)) 268 | return rr 269 | } 270 | 271 | func createCNAMERecord(name, target string, ttl uint32) dns.RR { 272 | rr, _ := dns.NewRR(fmt.Sprintf("%s %d IN CNAME %s", name, ttl, target)) 273 | return rr 274 | } 275 | 276 | // Exec Add Rule 277 | func addRoute(domain, recordType string, realIP string, ttl uint32, actionName string) string { 278 | log.Printf("Adding rule for %s of type %s real IP %s and TTL %d\n", domain, recordType, realIP, ttl) 279 | 280 | action, ok := config.Actions[actionName] 281 | if !ok { 282 | log.Printf("No action found for %s", actionName) 283 | return "" 284 | } 285 | eTime := ttl 286 | if action.FakeIPDelay != 0 { 287 | eTime = ttl + action.FakeIPDelay 288 | } 289 | 290 | newExpiry := expireTime(eTime) 291 | //fmt.Println("ttl", eTime) 292 | //fmt.Println("fakeIPDelay", action.FakeIPDelay) 293 | // Check lease, if exists - update expiry 294 | if mapping, exists := ipMappings[realIP]; exists { 295 | if mapping.Action == actionName { 296 | // Если новое правило имеет большее TTL, обновляем его 297 | if newExpiry.After(mapping.Expiry) { 298 | mu.Lock() 299 | mapping.Expiry = newExpiry 300 | mu.Unlock() 301 | log.Printf("[1]New TTL for %s -> %v", mapping.RealIP, mapping.LocalIPs) 302 | log.Printf("Checked TTL") 303 | } 304 | return mapping.LocalIPs 305 | } 306 | } 307 | var localIPs string 308 | var rule, ruleD string 309 | rule = action.Script.Add 310 | //log.Println("ADD:", rule) 311 | ruleD = action.Script.Delete 312 | itype := "" 313 | for _, NetName := range action.FakeIPNet { 314 | if recordType == "A" { 315 | itype = "4" 316 | localIPs = allocateIP(NetName, 4, ipv4Pools) 317 | } 318 | if recordType == "AAAA" { 319 | localIPs = allocateIP(NetName, 6, ipv6Pools) 320 | itype = "6" 321 | } 322 | ipMappings[realIP] = &IPMapping{ 323 | Domain: domain, 324 | RealIP: realIP, 325 | LocalIPs: localIPs, 326 | Expiry: newExpiry, 327 | Action: actionName, 328 | RecordType: recordType, 329 | } 330 | 331 | // Добавляем новое сопоставление 332 | 333 | rule = strings.ReplaceAll(rule, fmt.Sprintf("{fakeIP_%v}", NetName), localIPs) 334 | rule = strings.ReplaceAll(rule, "{realIP}", realIP) 335 | rule = strings.ReplaceAll(rule, "{inet}", itype) 336 | //delete rules 337 | ruleD = strings.ReplaceAll(ruleD, "{realIP}", realIP) 338 | ruleD = strings.ReplaceAll(ruleD, fmt.Sprintf("{fakeIP_%v}", NetName), localIPs) 339 | ruleD = strings.ReplaceAll(ruleD, "{inet}", itype) 340 | ipMappings[realIP].CmdDelete = ruleD 341 | log.Println("ADD:", rule) 342 | //log.Println("DEL:", ruleD) 343 | 344 | exec.Command("sh", "-c", rule).Run() 345 | } 346 | return localIPs 347 | } 348 | 349 | // Exec Remove Rule 350 | func removeRoute(ip string) { 351 | _, ok := ipMappings[ip] 352 | if ok { 353 | log.Printf("Expired IP-local {%v} was released [%v - %v]", ip, ipMappings[ip].Domain, ipMappings[ip].RealIP) 354 | exec.Command("sh", "-c", ipMappings[ip].CmdDelete).Run() 355 | releaseIP(ipMappings[ip].LocalIPs) 356 | } 357 | } 358 | func forwardDNSRequest(r *dns.Msg, dnsForward string, dnssec bool) ([]dns.RR, []dns.RR, []dns.RR) { 359 | client := new(dns.Client) 360 | client.UDPSize = 1232 // увеличенный размер UDP для DNSSEC 361 | client.DialTimeout = time.Second * 2 362 | client.ReadTimeout = time.Second * 2 363 | 364 | // Dublicate 365 | req := r.Copy() 366 | 367 | // Если требуется, можно изменить параметры, например, установить размер EDNS0 368 | if edns0 := req.IsEdns0(); edns0 != nil { 369 | req.SetEdns0(edns0.UDPSize(), edns0.Do()) 370 | } else { 371 | req.SetEdns0(1232, dnssec) 372 | } 373 | 374 | // forward to DNS-Server 375 | response, _, err := client.Exchange(req, dnsForward) 376 | if err != nil { 377 | log.Printf("Failed to forward DNS request: %s\n", err) 378 | return nil, nil, nil 379 | } 380 | 381 | //debug answer 382 | //fmt.Printf("response: %v\n", response) 383 | 384 | return response.Answer, response.Ns, response.Extra 385 | } 386 | 387 | // Passthrought DNS query 388 | func ForwardAndRespond(w dns.ResponseWriter, r *dns.Msg, msg *dns.Msg, dnsForward string) { 389 | client := new(dns.Client) 390 | client.UDPSize = 1232 // UDP for DNSSEC 391 | client.DialTimeout = time.Second * 2 392 | client.ReadTimeout = time.Second * 2 393 | 394 | log.Printf("Passthrough: %s, DNS: %s\n", &r.Question[0], config.Server.DNSForward) 395 | req := r.Copy() 396 | 397 | response, _, err := client.Exchange(req, dnsForward) 398 | 399 | if err != nil { 400 | log.Printf("Failed to forward DNS request: %s\n", err) 401 | w.WriteMsg(msg) 402 | return 403 | } 404 | msg.Answer = append(msg.Answer, response.Answer...) 405 | msg.Ns = append(msg.Ns, response.Ns...) 406 | msg.Extra = append(msg.Extra, response.Extra...) 407 | w.WriteMsg(msg) 408 | } 409 | 410 | func startExpiryChecker() { 411 | ticker := time.NewTicker(1 * time.Second) 412 | defer ticker.Stop() 413 | 414 | for range ticker.C { 415 | now := time.Now() 416 | mu.Lock() 417 | for ip, mapping := range ipMappings { 418 | if now.After(mapping.Expiry) { 419 | log.Printf("EC Expired mapping for IP: %s -> %s [%s]", ip, mapping.LocalIPs, mapping.Action) 420 | removeRoute(mapping.RealIP) 421 | delete(ipMappings, mapping.RealIP) 422 | //delete(allocatedIPs, mapping.LocalIPs[0]) 423 | } 424 | } 425 | mu.Unlock() 426 | } 427 | } 428 | 429 | // Run reset script by name 430 | func ScriptOnReset(actionString string) { 431 | action, ok := config.Actions[actionString] 432 | var cmd error 433 | if ok { 434 | if len(action.Script.OnReset) > 0 { 435 | log.Printf("SH RUN SCRIPT ACTION (OnReset) [%v]", action.Script.OnReset) 436 | cmd = exec.Command("sh", "-c", action.Script.OnReset).Run() 437 | log.Printf("RESULT [%v]", cmd) 438 | } 439 | } 440 | } 441 | 442 | func ScriptOnStart(actionString string) { 443 | action, ok := config.Actions[actionString] 444 | var cmd error 445 | if ok { 446 | if len(action.Script.OnStart) > 0 { 447 | log.Printf("SH RUN SCRIPT ACTION (OnStart) [%v]", action.Script.OnStart) 448 | cmd = exec.Command("sh", "-c", action.Script.OnStart).Run() 449 | log.Printf("RESULT [%v]", cmd) 450 | } 451 | } 452 | } 453 | 454 | func ScriptResetAll() { 455 | for _, action := range config.Actions { 456 | if len(action.Script.OnReset) > 0 { 457 | log.Printf("SH RUN SCRIPT ACTION (OnReset) [%v]", action.Script.OnReset) 458 | exec.Command("sh", "-c", action.Script.OnReset).Run() 459 | } 460 | } 461 | } 462 | -------------------------------------------------------------------------------- /doc/RouterOS7_example.md: -------------------------------------------------------------------------------- 1 | ## Пример реализации RouterOS7 <-> Wireguard <-> VPS (с установленным unicornDNS) 2 | 3 | Пример подойдет для людей которые уже туннель создали и немного понимают как работает маршрутизация. 4 | 5 | ### Настройки туннеля на VPS unicornDNS (далее Gate) 6 | ```sh 7 | inteface name wg0 8 | [Interface] 9 | PrivateKey = ****** 10 | Address = 10.10.13.2/30, 2001:db8::2/64 11 | ListenPort = 13232 12 | 13 | [Peer] 14 | PublicKey = ****** 15 | Endpoint = address_ROS7:13231 16 | AllowedIPs = 10.56.0.0/24, 10.10.13.0/30, 2001:db8::/64 17 | ``` 18 | * 10.56.0.0/24 - Это моя внутренняя сеть за !ROS 19 | * 10.10.13.0/30 – Это сеть которая создается внутри туннеля 20 | * 2001:db8::/64 - Это сеть ipv6 которая так же создается внутри туннеля 21 | 22 | ### Настройки туннеля на стороне RouterOS 6/7 (далее ROS) 23 | ```sh 24 | inteface name wgate 25 | [Interface] 26 | PrivateKey = ****** 27 | Address = 10.10.13.1/30, 2001:db8::1/64 28 | ListenPort = 13232 29 | 30 | [Peer] 31 | PublicKey = ****** 32 | Endpoint = address_VPS:13231 33 | AllowedIPs = 0.0.0.0/0, ::/0 34 | ``` 35 | **Обратите внимание!** тут в разделе AllowedIPs указано весь трафик, это особенность RouterOS системы где трафик выделяется потом с помощью таблицы маршрутов. Если вы настраиваете по этой документации WireGuard туннель к примеру на смартфоне, то надо указывать [10.199.0.0/16, 2001:db9:aaaa:aaaa::/117] 36 | На ROS7 нам надо добавить сеть которая будет использоваться для маршрутизации. Для примера я буду использовать: 37 | 38 | * 10.199.0.0/24 для ipv4 39 | 40 | * 2001:db9:aaaa:aaaa::/117 для ipv6 41 | 42 | Если вам не нужно ipv6, вы можете опустить это. 43 | ```sh 44 | ip route add 10.199.0.0/16 %wgate 45 | ip6 route add 2001:db9:aaaa:aaaa::/117 %wgate 46 | ``` 47 | За счет маршрутов мы говорим ROS где искать конкретные адреса, а именно отправлять все запросы на 10.199.0.0 / 2001:db9:aaaa:aaaa:: в туннель 10.10.13.2 / 2001:db8::2 48 | 49 | ## 2. Переходим к настройке unicornDNS (Gate) 50 | 51 | Заходим и копируем конфигурацию в отдельную папку для теста. И переходим к редактированию. 52 | ```sh 53 | cd unicornDNS 54 | cp example config 55 | sudo chmod +x sh -R 56 | sudo chmod +x unicornDNS 57 | nano config/config.yaml 58 | ``` 59 | 60 | Тут задаются подсети которые будут использованы для генерации адресов. В этом примере секция должна выглядеть так: 61 | 62 | ```yaml 63 | networks: 64 | ipv4: 65 | cidr: "10.199.0.0/24" 66 | ipv6: 67 | cidr: "2001:db9:aaaa:aaaa::/117" 68 | ``` 69 | PS: Для большего понимания я оставил дополнительную справку в самих конфиг файлах: example/config.yaml, example/config_2.yaml 70 | 71 | 72 | bind-address секцию не трогаем на время тестирования работы. DNS сервер создается на порту 8053 73 | 74 | ```yaml 75 | bind-address: "0.0.0.0:8053" 76 | ``` 77 | Вам нужно зайти в каждый файл sh и изменить ens3 на интерфейс основной который у вас на VPS (интерфейс с интернетом). После этого можно начинать тестировать 78 | 79 | ```sh 80 | ls sh/iptables 81 | ``` 82 | После того как все это сделали все готово к, 83 | Запускаем тестирование 84 | 85 | ```sh 86 | ./unicornDNS -config config/config.yaml -rules config/rules.txt 87 | ``` 88 | 89 | После запуска сервера вам достаточно для теста сделать 90 | 91 | ```sh 92 | dig @10.10.13.2 -p 8053 youtube.com 93 | 94 | Вывод должен быть примерно таким: 95 | 96 | ;; QUESTION SECTION: 97 | ;youtube.com. IN A 98 | 99 | ;; ANSWER SECTION: 100 | youtube.com. 99 IN A 10.199.0.17 101 | ``` 102 | 103 | Далее меняем и запускаем. 104 | ```yaml 105 | bind-address: "0.0.0.0:53" 106 | ``` 107 | 108 | Остается на ROS сделать полностью передачу всех запросов или "точечно" с помощью FWD DNS на сервер 10.10.13.2. -------------------------------------------------------------------------------- /doc/realese-notes.md: -------------------------------------------------------------------------------- 1 | ### 1.11b 2 | 3 | Новое: 4 | 5 | * Скрипт install_easy.sh для автоматической установки (тест ubuntu) 6 | * Частично изменена структура файла конфигурации для улучшения читабельности 7 | * Добавлены новые опции для управления TTL: min-rewrite, max-rewrite 8 | * Добавлены опция **fakeip-lease-delay** – задержка снятия аренды выделенного IP-адреса. Опция позволяет избежать проблем при некоторых сценариях 9 | * Добавлен новый фильтр **DOMAIN** (только домен, без wildcard) 10 | * Добавлены атомарные счетчики для будующих функций 11 | 12 | Исправления: 13 | 14 | * Оптимизирована работа кэша-dns 15 | * Оптимизирована выдача fakeip адресов 16 | * Исправлена проблема (при некоторых сценариях) когда не все файлы удалялись 17 | * DNS-сервер указанный в Rules не применялся 18 | * Исправлена проблема запуска скриптов сброса IPv4Reset/IPv6Reset 19 | -------------------------------------------------------------------------------- /doc/rules.md: -------------------------------------------------------------------------------- 1 | 2 | ### Инструкция по созданию своих листов RULES 3 | 4 | На текущий момент DNS-сервер поддерживает два вида поиска: 5 | 6 | * DOMAIN-SUFFIX - wildcard домена, т.е любой домен заканчивающийся на указанное 7 | * DOMAIN-MATCH - домен содержит указанный текст в любом месте 8 | * DOMAIN - только домен полное соответствие 9 | 10 | Каждое правило занимает одну строку в формате 11 | 12 | [ВидПоиска],[Строка],[НазваниеДействия] 13 | 14 | Правила обрабатываются по порядку и первое подходящее правило останавливает обработку. Рекомендуется самые популярные списки (инстаграмм, ютуб и тд) указывать в начале списка 15 | 16 | Название [Действия] определяется в конфигурации DNS сервера. Если действие не определено в конфигурации (config.yaml) обработка пропускается 17 | 18 | [Действие] 19 | ```sh 20 | actions: 21 | PROXY: # Название действия PROXY 22 | #... 23 | PROXY2: # Название действия PROXY2 24 | #... 25 | ``` 26 | 27 | ### Примеры 28 | 29 | ```sh 30 | DOMAIN-SUFFIX,example.com,PROXY 31 | # -> example.com 32 | # -> test.example.com 33 | # -> test1.example.com ... etc 34 | DOMAIN-SUFFIX,example1.com,PROXY 35 | # -> example.com 36 | # -> test.example1.com 37 | # -> test1.example1.com ... etc 38 | DOMAIN-SUFFIX,youtube.com,PROXY 39 | # -> youtube.com 40 | # -> accounts.youtube.com ... etc 41 | DOMAIN-MATCH,you,PROXY 42 | # -> youtube.com 43 | # -> accounts.youtube.ae 44 | # -> you.com 45 | # -> yourself.com ... etc 46 | ``` 47 | 48 | ### Дополнительные опции 49 | 50 | * RULE-SET - список правил который будет загружен при загрузке Rules. Формат такой же как в этом документе. Действие в файле не учитывается и применяется указанное в RULE-SET. Загрузка по http/https. 51 | 52 | ```sh 53 | RULE-SET,https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Shadowrocket/Instagram/Instagram.list,PROXY 54 | # Каждому правило загруженному по этому адресу будет присвоено действие указанное после запятой 55 | ``` 56 | 57 | ### Пример реализации 58 | 59 | Пример списка можно найти в ./example/rules.txt 60 | 61 | -------------------------------------------------------------------------------- /example/config_iptables.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | bind-address: "0.0.0.0:8053" 3 | # DNS сервер по-умолчанию: 4 | dns-forward: "1.1.1.1:53" 5 | # HTTP сервер имеет только 2 действия: 6 | # - "/clearcache" Сброс кэша DNS с удалением правил 7 | # - "/reload" Перезагрузка конфигруации правил (пример правил лежит rules.txt 8 | http-server: "0.0.0.0:8081" 9 | cache: true 10 | # Генерация сетей локальных откуда будут выдаваться адреса 11 | # ВНИМАНИЕ! на ipv6 меньше 116 диапазона слишком много адресов и он может не запуститься вообще 12 | # При необходимости можно указать несколько диапозонов (см config_2.yaml) 13 | networks: 14 | private: 15 | ipv4: "10.199.0.0/16" 16 | ipv6: "fd00::/117" 17 | 18 | # Действия 19 | actions: 20 | PROXY: 21 | method: "fakeip" 22 | # method fakeip или passthrought 23 | fakeip-release-delay: 600 24 | # fakeip-release-delay опция доступна только при методе fakeip 25 | # задержка в секундах после которого выданный ip-адрес осовобождается 26 | # опция позволяет исправить проблему при некоторых сценариях использования 27 | dns-forward: "1.1.1.1:53" 28 | ttl: 29 | max-transfer: 120 30 | # Максимальное время которые пересылается от серверу к клиенту 31 | # Никак не влияет на запись которая хранится в cache сервере 32 | min-rewrite: 120 33 | # Если TTL записи ниже чем это значение, оно меняется на данное. 34 | max-rewrite: 600 35 | # Если TTL записи ВЫШЕ чем это значение, оно меняется на данное. 36 | variable: 37 | # Тут можно указать переменные в формате имя: значение 38 | # Текст в любой команде меняется по маске {имя} в командах 39 | interface: "ens3" 40 | mark: "PROXY" 41 | fakeip-networks: 42 | - "private" 43 | # название взято из раздела networks.[название списка] 44 | # в строке скрипта меняется на значение которое выдано dns-сервером из пула адресов {fakeIP_private} 45 | script: 46 | add: "sh sh/iptables/add.sh {fakeIP_private} {realIP} {mark} {wan} {tname}" 47 | delete: "sh sh/iptables/delete.sh {fakeIP_private} {realIP} {mark} {wan} {tname}" 48 | onreset: "sh sh/iptables/onreset.sh {nets}" -------------------------------------------------------------------------------- /example/config_nftables.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | bind-address: "0.0.0.0:8053" 3 | # DNS сервер по-умолчанию: 4 | dns-forward: "1.1.1.1:53" 5 | # HTTP сервер имеет только 2 действия: 6 | # - "/clearcache" Сброс кэша DNS с удалением правил 7 | # - "/reload" Перезагрузка конфигруации правил (пример правил лежит rules.txt 8 | http-server: "0.0.0.0:8081" 9 | cache: true 10 | # Генерация сетей локальных откуда будут выдаваться адреса 11 | # ВНИМАНИЕ! на ipv6 меньше 116 диапазона слишком много адресов и он может не запуститься вообще 12 | # При необходимости можно указать несколько диапозонов (см config_2.yaml) 13 | networks: 14 | private: 15 | ipv4: "10.199.0.0/16" 16 | ipv6: "fd00::/117" 17 | 18 | # Действия 19 | actions: 20 | PROXY: 21 | method: "fakeip" 22 | # method fakeip или passthrought 23 | fakeip-release-delay: 600 24 | # fakeip-release-delay опция доступна только при методе fakeip 25 | # задержка в секундах после которого выданный ip-адрес осовобождается 26 | # опция позволяет исправить проблему при некоторых сценариях использования 27 | dns-forward: "1.1.1.1:53" 28 | ttl: 29 | max-transfer: 120 30 | # Максимальное время которые пересылается от серверу к клиенту 31 | # Никак не влияет на запись которая хранится в cache сервере 32 | min-rewrite: 120 33 | # Если TTL записи ниже чем это значение, оно меняется на данное. 34 | max-rewrite: 600 35 | # Если TTL записи ВЫШЕ чем это значение, оно меняется на данное. 36 | variable: # Variables uses for replace in script strings 37 | wan: "ens3" # {wan} в строке скрипта меняется на это значение 38 | mark: "PROXY" # ... {mark} 39 | tname: "dnsnat" # ... {tname} 40 | nets: "ipv4" 41 | fakeip-networks: 42 | - "private" 43 | # название взято из раздела networks.[название списка] 44 | # в строке скрипта меняется на значение которое выдано dns-сервером из пула адресов {fakeIP_private} 45 | script: 46 | add: "sh sh/nftables/add.sh {fakeIP_private} {realIP} {mark} {wan} {tname}" 47 | delete: "sh sh/nftables/delete.sh {fakeIP_private} {realIP} {mark} {wan} {tname}" 48 | onreset: "sh sh/nftables/onreset.sh {tname} {nets}" 49 | onstart: "sh sh/nftables/onstart.sh {tname}" 50 | # availible types script: add, delete, onreset, onstart 51 | # constants: 52 | # {inet} = 4 or 6 53 | # {realIP} = real ip 54 | # {fakeIP_x} = where x == num of IP from list. 0,1 55 | 56 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/unicorn-style/unicornDNS 2 | 3 | go 1.22.5 4 | 5 | require ( 6 | github.com/miekg/dns v1.1.61 7 | gopkg.in/yaml.v3 v3.0.1 8 | ) 9 | 10 | require ( 11 | golang.org/x/mod v0.18.0 // indirect 12 | golang.org/x/net v0.26.0 // indirect 13 | golang.org/x/sync v0.7.0 // indirect 14 | golang.org/x/sys v0.21.0 // indirect 15 | golang.org/x/tools v0.22.0 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= 2 | github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= 3 | golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= 4 | golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 5 | golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= 6 | golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= 7 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 8 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 9 | golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= 10 | golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 11 | golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= 12 | golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 13 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 14 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 15 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 16 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 17 | -------------------------------------------------------------------------------- /http.go: -------------------------------------------------------------------------------- 1 | // http_server.go 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "html/template" 7 | "log" 8 | "net/http" 9 | ) 10 | 11 | func StartHTTPServer() { 12 | http.HandleFunc("/clearcache", clearCacheHandler) 13 | http.HandleFunc("/reload", reloadHandler) 14 | http.HandleFunc("/ips", capacityIPsHandler) 15 | http.HandleFunc("/rules", listRulesHandler) 16 | 17 | addr := config.Server.HttpServer 18 | log.Printf("Starting HTTP server on %s\n", addr) 19 | err := http.ListenAndServe(addr, nil) 20 | if err != nil { 21 | log.Fatalf("Failed to start HTTP server: %s\n", err.Error()) 22 | } 23 | } 24 | 25 | func clearCacheHandler(w http.ResponseWriter, r *http.Request) { 26 | СlearCache() 27 | fmt.Fprintln(w, "Cache cleared successfully") 28 | } 29 | 30 | func reloadHandler(w http.ResponseWriter, r *http.Request) { 31 | ParseRuleset(rulesFile) 32 | fmt.Fprintln(w, "Rules reloaded successfully") 33 | } 34 | 35 | func listRulesHandler(w http.ResponseWriter, r *http.Request) { 36 | fmt.Fprintln(w, PrintRules(Rules)) 37 | } 38 | 39 | func capacityIPsHandler(w http.ResponseWriter, r *http.Request) { 40 | log.Printf("TEST PAGE") 41 | 42 | type PoolStatus struct { 43 | Name string 44 | IPList []IPEntry 45 | } 46 | 47 | var statusData []PoolStatus 48 | 49 | for name, pool := range ipv4Pools { 50 | mutex := mutexes[name] 51 | mutex.Lock() 52 | statusData = append(statusData, PoolStatus{Name: name, IPList: pool}) 53 | mutex.Unlock() 54 | } 55 | 56 | /* 57 | for name, pool := range ipv6Pools { 58 | mutex := mutexes[name] 59 | mutex.Lock() 60 | statusData = append(statusData, PoolStatus{Name: name, IPList: pool}) 61 | mutex.Unlock() 62 | }*/ 63 | 64 | tmpl := ` 65 | 66 | 67 | 68 | IP Status 69 | 70 | 71 |

IP Status

72 | {{range .}} 73 |

{{.Name}}

74 | 75 | 76 | 77 | 78 | 79 | {{range .IPList}} 80 | 81 | 82 | 83 | 84 | {{end}} 85 |
IPStatus
{{.IP}}{{if .InUse}}In Use{{else}}Available{{end}}
86 | {{end}} 87 | 88 | 89 | ` 90 | 91 | t, err := template.New("status").Parse(tmpl) 92 | if err != nil { 93 | http.Error(w, "Error generating page", http.StatusInternalServerError) 94 | return 95 | } 96 | 97 | err = t.Execute(w, statusData) 98 | if err != nil { 99 | http.Error(w, "Error generating page", http.StatusInternalServerError) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /install_easy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Начальные переменные 4 | IFACE="" 5 | URL="https://raw.githubusercontent.com/unicorn-style/unicornDNS/main" 6 | INSTALLTYPE="ipv4" 7 | INSTALLPATH="/usr/local/bin" 8 | RULES="" 9 | DEFAULT_IPV4_CIDR="10.199.0.0/16" 10 | DEFAULT_IPV6_CIDR="2001:db9:aaaa:aaaa::/117" 11 | HTTP_BINDADDRESS="127.0.0.1:8081" 12 | DNS_BINDADDRESS="0.0.0.0:53" 13 | DNS_FORWARD="1.1.1.1:53" 14 | FIREWALL_TYPE="iptables" # по умолчанию iptables 15 | TNAME_NAT="" 16 | 17 | # Получение архитектуры системы 18 | ARCH=$(uname -m) 19 | case "$ARCH" in 20 | x86_64) ARCH="x86_64" ;; 21 | aarch64) ARCH="arm64" ;; 22 | arm*) ARCH="arm" ;; 23 | *) echo "Not support: $ARCH"; exit 1 ;; 24 | esac 25 | 26 | # DOWNLOAD_URL="https://github.com/unicorn-style/unicornDNS/releases/latest/download/unicornDNS-$ARCH.tar.gz" 27 | DOWNLOAD_URL="https://raw.githubusercontent.com/unicorn-style/unicornDNS/main/build/unicornDNS_$ARCH.tar.gz" 28 | # Получение интерфейсов 29 | INTERFACES=$(ip -o link show | awk -F': ' '{print $2}') 30 | i=1 31 | for iface in $INTERFACES; do 32 | echo "$i. $iface" 33 | i=$((i + 1)) 34 | done 35 | 36 | # Выбор интерфейса 37 | echo "Enter WAN-interface number:" 38 | read IFACE_INDEX 39 | 40 | i=1 41 | for iface in $INTERFACES; do 42 | if [ $i -eq $IFACE_INDEX ]; then 43 | IFACE=$iface 44 | break 45 | fi 46 | i=$((i + 1)) 47 | done 48 | 49 | # Выбор типа установки 50 | echo "Select installation type:" 51 | echo "1. ipv4" 52 | echo "2. ipv4ipv6" 53 | echo "3. ipv6" 54 | read INSTALLTYPE_INDEX 55 | 56 | case $INSTALLTYPE_INDEX in 57 | 1) 58 | INSTALLTYPE="ipv4" 59 | DEFAULT_IPV6_CIDR="" 60 | ;; 61 | 2) 62 | INSTALLTYPE="ipv4ipv6" 63 | ;; 64 | 3) 65 | INSTALLTYPE="ipv6" 66 | DEFAULT_IPV4_CIDR="" 67 | ;; 68 | *) 69 | echo "Invalid choice. Defaulting to ipv4." 70 | INSTALLTYPE="ipv4" 71 | ;; 72 | esac 73 | 74 | # Запрос на выбор cidr для ipv4 75 | if [ "$INSTALLTYPE" = "ipv4" ] || [ "$INSTALLTYPE" = "ipv4ipv6" ]; then 76 | echo "Enter IPv4 CIDR (default $DEFAULT_IPV4_CIDR):" 77 | read IPV4_CIDR 78 | IPV4_CIDR=${IPV4_CIDR:-$DEFAULT_IPV4_CIDR} 79 | fi 80 | 81 | # Запрос на выбор cidr для ipv6 82 | if [ "$INSTALLTYPE" = "ipv6" ] || [ "$INSTALLTYPE" = "ipv4ipv6" ]; then 83 | echo "Enter IPv6 CIDR (default $DEFAULT_IPV6_CIDR):" 84 | read IPV6_CIDR 85 | IPV6_CIDR=${IPV6_CIDR:-$DEFAULT_IPV6_CIDR} 86 | fi 87 | 88 | # Запрос на выбор bind-address и dns-forward 89 | echo "Enter bind address and port for DNS-server(default $DNS_BINDADDRESS):" 90 | read BIND_ADDRESS 91 | BIND_ADDRESS=${BIND_ADDRESS:-$DNS_BINDADDRESS} 92 | 93 | # Проверка занятости порта 94 | BIND_PORT=$(echo $BIND_ADDRESS | awk -F':' '{print $2}') 95 | if netstat -tuln | grep -q ":$BIND_PORT "; then 96 | echo "Port $BIND_PORT is already in use. Please choose another port. End." 97 | exit 1 98 | fi 99 | 100 | echo "Enter default DNS forward address (default $DNS_FORWARD):" 101 | read DNS_FORWARD_INPUT 102 | DNS_FORWARD=${DNS_FORWARD_INPUT:-$DNS_FORWARD} 103 | 104 | echo "Enter default HTTP address ip:port for controll this server. This (default $HTTP_BINDADDRESS):" 105 | read HTTP_BINDADDRESS_INPUT 106 | HTTP_BINDADDRESS=${HTTP_BINDADDRESS_INPUT:-$HTTP_BINDADDRESS} 107 | 108 | # Проверка наличия nftables и выбор механизма firewall 109 | : <<'COMMENT' 110 | if command -v nft > /dev/null 2>&1; then 111 | echo "Select firewall mechanism:" 112 | echo "1. iptables" 113 | echo "2. nftables" 114 | read FIREWALL_INDEX 115 | 116 | case $FIREWALL_INDEX in 117 | 1) 118 | FIREWALL_TYPE="iptables" 119 | ;; 120 | 2) 121 | FIREWALL_TYPE="nftables" 122 | # Предложение создать таблицы для маршрутизации 123 | echo "Do you want to create nftables chains? [Y/N]:" 124 | read CREATE_TABLES 125 | if [ "$CREATE_TABLES" = "Y" ] || [ "$CREATE_TABLES" = "y" ]; then 126 | # Уточнение имени цепочки/фильтра 127 | echo "Enter NAT chain name (default: nat):" 128 | read TNAME_NAT_INPUT 129 | TNAME_NAT=${TNAME_NAT_INPUT:-"nat"} 130 | 131 | # echo "Enter filter name (default: dnsFILTER):" 132 | # read TNAME_FILTER_INPUT 133 | # TNAME_FILTER=${TNAME_FILTER_INPUT:-"dnsFILTER"} 134 | fi 135 | ;; 136 | *) 137 | echo "Invalid choice. Defaulting to iptables." 138 | FIREWALL_TYPE="iptables" 139 | ;; 140 | esac 141 | else 142 | echo "nftables not found. Use default iptables." 143 | FIREWALL_TYPE="iptables" 144 | fi 145 | COMMENT 146 | 147 | # Выбор пути установки 148 | echo "Would you like to change the installation directory (default: $INSTALLPATH)? [Y/N]:" 149 | read CHANGE_INSTALLPATH 150 | 151 | if [ "$CHANGE_INSTALLPATH" = "Y" ] || [ "$CHANGE_INSTALLPATH" = "y" ]; then 152 | echo "Enter new installation directory:" 153 | read INSTALLPATH 154 | fi 155 | 156 | # Подтверждение выбора 157 | echo "Your settings:" 158 | echo "- WAN Interface: $IFACE" 159 | echo "- Installation networks: $INSTALLTYPE" 160 | echo "– IPv4 CIDR: ${IPV4_CIDR:-Not Set}" 161 | echo "- IPv6 CIDR: ${IPV6_CIDR:-Not Set}" 162 | echo "- Bind address: $BIND_ADDRESS" 163 | echo "- DNS forward address: $DNS_FORWARD" 164 | echo "- HTTP bind address: $HTTP_BINDADDRESS" 165 | echo "- Firewall: $FIREWALL_TYPE" 166 | if [ "$FIREWALL_TYPE" = "nftables" ]; then 167 | echo "NAT chain name: $TNAME_NAT" 168 | echo "Filter name: $TNAME_FILTER" 169 | fi 170 | echo "Installation directory: $INSTALLPATH" 171 | echo "System architecture: $ARCH" 172 | echo "Do you confirm the installation? [Y/N]:" 173 | read CONFIRM 174 | 175 | if [ "$CONFIRM" != "Y" ] && [ "$CONFIRM" != "y" ]; then 176 | echo "Installation canceled." 177 | exit 0 178 | fi 179 | 180 | # Выполнение установки 181 | INSTALL_DIR="$INSTALLPATH/unicornDNS" 182 | CONFIG_FILE="$INSTALL_DIR/config.yaml" 183 | 184 | # Создание директории 185 | mkdir -p "$INSTALL_DIR" 186 | mkdir -p "$INSTALL_DIR/sh" 187 | 188 | # Генерация конфигурационного файла 189 | 190 | export BIND_ADDRESS="$BIND_ADDRESS" 191 | export DNS_FORWARD="$DNS_FORWARD" 192 | export HTTP_BINDADDRESS="$HTTP_BINDADDRESS" 193 | export IFACE="$IFACE" 194 | export TNAME_NAT="$TNAME_NAT" 195 | export DEFAULT_IPV4_CIDR="$DEFAULT_IPV4_CIDR" 196 | export DEFAULT_IPV6_CIDR="$DEFAULT_IPV6_CIDR" 197 | export INSTALLTYPE="$INSTALLTYPE" 198 | 199 | curl -o "tmp_config_$FIREWALL_TYPE.yaml" "$URL/src/tmp_config_$FIREWALL_TYPE.yaml" 200 | envsubst < tmp_config_$FIREWALL_TYPE.yaml > "$INSTALL_DIR/config.yaml" 201 | rm tmp_config_$FIREWALL_TYPE.yaml 202 | 203 | # Скачивание и распаковка сборки в зависимости от архитектуры 204 | curl -o "$INSTALL_DIR/unicornDNS.tar.gz" "$DOWNLOAD_URL" 205 | mkdir $INSTALL_DIR 206 | tar -xzf "$INSTALL_DIR/unicornDNS.tar.gz" -C "$INSTALL_DIR" 207 | rm "$INSTALL_DIR/unicornDNS.tar.gz" 208 | 209 | # Скачивание скриптов 210 | curl -o "$INSTALL_DIR/sh/add.sh" "$URL/sh/$FIREWALL_TYPE/add.sh" 211 | curl -o "$INSTALL_DIR/sh/delete.sh" "$URL/sh/$FIREWALL_TYPE/delete.sh" 212 | curl -o "$INSTALL_DIR/sh/onReset.sh" "$URL/sh/$FIREWALL_TYPE/onReset.sh" 213 | curl -o "$INSTALL_DIR/rules.list" "https://raw.githubusercontent.com/unicorn-style/unicorndns_rkn/main/rules.list" 214 | 215 | echo "Do you want to install UnicornDNS as a system service? [Y/N]:" 216 | read INSTALL_SERVICE 217 | 218 | if [ "$INSTALL_SERVICE" = "Y" ] || [ "$INSTALL_SERVICE" = "y" ]; then 219 | # Detect system type and install service 220 | if command -v systemctl > /dev/null 2>&1; then 221 | # Using systemd 222 | echo "Systemd detected. Installing service." 223 | 224 | SERVICE_FILE="/etc/systemd/system/unicorndns.service" 225 | 226 | # Create service file 227 | cat < "$SERVICE_FILE" 228 | [Unit] 229 | Description=UnicornDNS Service 230 | After=network.target 231 | 232 | [Service] 233 | ExecStart=$INSTALL_DIR/unicornDNS -rules $INSTALL_DIR/rules.list -config $INSTALL_DIR/config.yaml 234 | WorkingDirectory=$INSTALL_DIR 235 | Restart=on-failure 236 | 237 | [Install] 238 | WantedBy=multi-user.target 239 | EOF 240 | # Install service in systemd 241 | systemctl daemon-reload 242 | systemctl enable unicorndns 243 | systemctl start unicorndns 244 | echo "UnicornDNS successfully installed and started as a service." 245 | fi 246 | fi 247 | 248 | echo "UnicornDNS successfully installed at $INSTALLPATH" 249 | echo " – Config file: $INSTALLPATH/config.yaml" 250 | echo " – Rules file: $INSTALLPATH/rules.list" 251 | echo "Use these commands to controll" 252 | echo " – http://$HTTP_BINDADDRESS/reset - reset firewall and cache" 253 | echo " – http://$HTTP_BINDADDRESS/clearcache - flush DNS-cache server" 254 | echo " – http://$HTTP_BINDADDRESS/reload - reload rule list" 255 | 256 | unset BIND_ADDRESS 257 | unset DNS_FORWARD 258 | unset HTTP_BINDADDRESS 259 | unset IFACE 260 | unset TNAME_NAT 261 | unset DEFAULT_IPV4_CIDR 262 | unset DEFAULT_IPV6_CIDR 263 | unset INSTALLTYPE 264 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | 10 | "github.com/miekg/dns" 11 | ) 12 | 13 | var ( 14 | configFile string 15 | rulesFile string 16 | ) 17 | 18 | func init() { 19 | // cmd flags 20 | flag.StringVar(&configFile, "config", "config.yaml", "Path to the configuration file") 21 | flag.StringVar(&rulesFile, "rules", "rules.list", "Path to the rules file") 22 | } 23 | 24 | func main() { 25 | // Parce input CMD 26 | flag.Parse() 27 | 28 | // Loading config 29 | loadConfig(configFile) 30 | 31 | ParseRuleset(rulesFile) // Load rules slice 32 | log.Println("Rules: ", Cnt["RulesAll"].Get()) 33 | log.Println("...accepted: ", Cnt["RulesActive"].Get()) 34 | //log.Println(config.Actions["PROXY"]) 35 | //log.Println(PrintRules(Rules)) 36 | go StartHTTPServer() 37 | 38 | //cacheChan = make(chan cacheRequest) 39 | go cacheHandler() 40 | go startExpiryChecker() 41 | 42 | // Обработка системных сигналов для корректного завершения работы 43 | sigChan := make(chan os.Signal, 1) 44 | signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) 45 | 46 | dns.HandleFunc(".", handleDNSRequest) 47 | 48 | server := &dns.Server{Addr: config.Server.BindAddress, Net: "udp"} 49 | server.UDPSize = 1232 50 | log.Printf("Starting DNS server on %s\n", config.Server.BindAddress) 51 | 52 | go func() { 53 | err := server.ListenAndServe() 54 | if err != nil { 55 | log.Fatalf("Failed to start server: %s\n", err.Error()) 56 | } 57 | }() 58 | 59 | // Exit 60 | sig := <-sigChan 61 | log.Printf("Received signal: %s. Shutting down...", sig) 62 | 63 | // Вызов функции очистки кэша 64 | СlearCache() 65 | 66 | // Остановка сервера 67 | server.Shutdown() 68 | 69 | // Отчистка всех правил 70 | ScriptResetAll() 71 | 72 | log.Println("Server gracefully stopped") 73 | } 74 | -------------------------------------------------------------------------------- /net.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | type IPMapping struct { 12 | Domain string 13 | RealIP string 14 | LocalIPs string 15 | Action string 16 | Expiry time.Time 17 | RecordType string 18 | CmdDelete string 19 | } 20 | 21 | var ipMappings = make(map[string]*IPMapping) 22 | 23 | type IPEntry struct { 24 | IP string 25 | ListName string // Имя пула, из которого выделен IP 26 | } 27 | 28 | var allocatedIPs = make(map[string]*IPEntry) 29 | var allocatedMutex *sync.Mutex 30 | 31 | func allocateIP(listName string, inet int8, pools map[string][]IPEntry) string { 32 | var allocatedIPsList string 33 | mutex, ok := mutexes[listName] 34 | if !ok { 35 | log.Fatalf("No mutex found for list %s", listName) 36 | } 37 | pool, ok := pools[listName] 38 | if !ok { 39 | log.Fatalf("No pool found for list %s", listName) 40 | } 41 | //if len(net.IPv4) > 0 && inet == 4 { 42 | mutex.Lock() 43 | for i := range pool { 44 | if _, ok := allocatedIPs[pool[i].IP]; !ok { 45 | allocatedIPsList = pool[i].IP 46 | allocatedIPs[pool[i].IP] = &pool[i] // Сохраняем ссылку на выделенный IP 47 | fmt.Println(allocatedIPs[pool[i].IP]) 48 | break 49 | } 50 | } 51 | log.Printf("%v\n", inet) 52 | mutex.Unlock() 53 | //} 54 | /*if len(net.IPv6) > 0 && inet == 6 { 55 | mutex.Lock() 56 | for i := range pool { 57 | if _, ok := allocatedIPs[pool[i].IP]; !ok { 58 | allocatedIPsList = pool[i].IP 59 | allocatedIPs[pool[i].IP] = &pool[i] // Сохраняем ссылку на выделенный IP 60 | fmt.Println(allocatedIPs[pool[i].IP]) 61 | break 62 | } 63 | } 64 | mutex.Unlock() 65 | }*/ 66 | return allocatedIPsList 67 | } 68 | 69 | func releaseIP(ip string) { 70 | allocatedMutex.Lock() 71 | defer allocatedMutex.Unlock() 72 | _, ok := allocatedIPs[ip] 73 | if ok { 74 | log.Printf("IP %s released", ip) 75 | delete(allocatedIPs, ip) // Удаляем из мапы при освобождении 76 | 77 | for _, NetName := range allocatedIPs { 78 | log.Printf("RIP %s released", NetName) 79 | } 80 | } else { 81 | log.Printf("IP %s not found in allocated IPs", ip) 82 | } 83 | } 84 | 85 | func generateIPPool(cidr string) []IPEntry { 86 | _, ipnet, err := net.ParseCIDR(cidr) 87 | if err != nil { 88 | log.Fatalf("Failed to parse CIDR: %s\n", err) 89 | } 90 | 91 | var pool []IPEntry 92 | for ip := ipnet.IP.Mask(ipnet.Mask); ipnet.Contains(ip); incrementIP(ip) { 93 | pool = append(pool, IPEntry{IP: ip.String()}) 94 | } 95 | 96 | // Removing network and broadcast addresses for IPv4 97 | if ipnet.IP.To4() != nil { 98 | return pool[1 : len(pool)-1] 99 | } 100 | return pool 101 | } 102 | 103 | func incrementIP(ip net.IP) { 104 | for j := len(ip) - 1; j >= 0; j-- { 105 | ip[j]++ 106 | if ip[j] > 0 { 107 | break 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /rules.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | "strconv" 10 | "strings" 11 | "sync" 12 | "sync/atomic" 13 | ) 14 | 15 | // Структура для каждого атомарного счетчика 16 | type AtomicCounter struct { 17 | value int64 18 | } 19 | 20 | // Increment увеличивает значение счетчика 21 | func (c *AtomicCounter) Increment() { 22 | atomic.AddInt64(&c.value, 1) 23 | } 24 | 25 | // Get возвращает текущее значение счетчика 26 | func (c *AtomicCounter) Get() int64 { 27 | return atomic.LoadInt64(&c.value) 28 | } 29 | 30 | type Rule struct { 31 | Type string 32 | Text string 33 | Passthrough bool 34 | TTL int32 35 | Action string 36 | DNS string 37 | Counter *AtomicCounter 38 | } 39 | 40 | var Rules []Rule 41 | 42 | // Глобальный массив общие счетчики 43 | var Cnt = map[string]*AtomicCounter{ 44 | "RulesAll": {}, 45 | "RulesActive": {}, 46 | "AllRequest": {}, 47 | } 48 | 49 | var rulemu sync.RWMutex 50 | 51 | func ParseRuleset(filename string) { 52 | rulemu.Lock() 53 | defer rulemu.Unlock() 54 | 55 | Rules = []Rule{} 56 | file, err := os.Open(filename) 57 | if err != nil { 58 | log.Fatalf("Error opening file: %v", err) 59 | } 60 | defer file.Close() 61 | 62 | scanner := bufio.NewScanner(file) 63 | for scanner.Scan() { 64 | line := scanner.Text() 65 | if strings.HasPrefix(line, "RULE-SET") { 66 | Rules = append(Rules, parseRuleSet(line)...) 67 | } else { 68 | rule := parseLine(line) 69 | if rule != (Rule{}) { 70 | Cnt["RulesActive"].Increment() 71 | Rules = append(Rules, rule) 72 | } 73 | } 74 | } 75 | } 76 | 77 | func parseLine(line string) Rule { 78 | parts := strings.Split(line, ",") 79 | if len(parts) < 3 { 80 | return Rule{} 81 | } 82 | 83 | _, ok := config.Actions[parts[2]] 84 | Cnt["RulesAll"].Increment() 85 | if ok { 86 | rule := Rule{ 87 | Type: parts[0], 88 | Text: parts[1], 89 | Action: parts[2], 90 | Counter: &AtomicCounter{}, 91 | } 92 | 93 | // Options 94 | for _, part := range parts[3:] { 95 | if part == "passthrought" { 96 | rule.Passthrough = true 97 | } else if strings.HasPrefix(part, "ttl=") { 98 | fmt.Sscanf(part, "ttl=%d", &rule.TTL) 99 | } else if strings.HasPrefix(part, "dns=") { 100 | rule.DNS = strings.TrimPrefix(part, "dns=") 101 | } 102 | } 103 | 104 | // 105 | if rule.Type == "DOMAIN-SUFFIX" || rule.Type == "DOMAIN-KEYWORD" || rule.Type == "DOMAIN" { 106 | //Cnt["RulesActive"].Increment() 107 | return rule 108 | } 109 | } 110 | 111 | return Rule{} 112 | } 113 | 114 | // Parcing URL-data 115 | func parseRuleSet(line string) []Rule { 116 | parts := strings.Split(line, ",") 117 | if len(parts) < 3 { 118 | return nil 119 | } 120 | 121 | url := parts[1] 122 | action := parts[2] 123 | return fetchRuleSet(url, action) 124 | } 125 | 126 | func fetchRuleSet(url string, action string) []Rule { 127 | resp, err := http.Get(url) 128 | if err != nil { 129 | log.Fatalf("Error fetching rule set: %v", err) 130 | return nil 131 | } 132 | defer resp.Body.Close() 133 | 134 | var Rules []Rule 135 | scanner := bufio.NewScanner(resp.Body) 136 | for scanner.Scan() { 137 | line := scanner.Text() 138 | parts := strings.Split(line, ",") 139 | if len(parts) < 2 { 140 | continue 141 | } 142 | Cnt["RulesAll"].Increment() 143 | _, ok := config.Actions[action] 144 | if ok { 145 | rule := Rule{ 146 | Type: parts[0], 147 | Text: parts[1], 148 | Action: action, 149 | Counter: &AtomicCounter{}, 150 | } 151 | // Добавляем только правила типа DOMAIN-SUFFIX и DOMAIN-KEYWORD 152 | if rule.Type == "DOMAIN-SUFFIX" || rule.Type == "DOMAIN-KEYWORD" || rule.Type == "DOMAIN" { 153 | Cnt["RulesActive"].Increment() 154 | Rules = append(Rules, rule) 155 | } 156 | } 157 | } 158 | 159 | return Rules 160 | } 161 | 162 | func MatchRules(s string, Rules []Rule) (Rule, bool) { 163 | rulemu.RLock() 164 | defer rulemu.RUnlock() 165 | for _, rule := range Rules { 166 | switch rule.Type { 167 | case "DOMAIN-SUFFIX": 168 | if strings.HasSuffix(s, rule.Text) { 169 | rule.Counter.Increment() 170 | return rule, true 171 | } 172 | case "DOMAIN-KEYWORD": 173 | if strings.Contains(s, rule.Text) { 174 | rule.Counter.Increment() 175 | return rule, true 176 | } 177 | case "DOMAIN": 178 | if s == rule.Text { 179 | rule.Counter.Increment() 180 | return rule, true 181 | } 182 | } 183 | } 184 | return Rule{}, false 185 | } 186 | 187 | func PrintRules(Rules []Rule) string { 188 | var t string 189 | t += "All Rules:\n" 190 | i := 0 191 | for _, rule := range Rules { 192 | i++ 193 | t += strconv.Itoa(i) 194 | t += fmt.Sprintf("Type: %s, Text: %s, Action: %s\n", rule.Type, rule.Text, rule.Action) 195 | } 196 | return t 197 | } 198 | -------------------------------------------------------------------------------- /sh/iptables/add.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 5 ]; then 4 | echo "Usage: $0 {inet} {localIP} {destination} {mark} {interface}" 5 | exit 1 6 | fi 7 | 8 | INET=$1 9 | LOCAL_IP=$2 10 | DESTINATION=$3 11 | MARK=$4 12 | INTERFACE=$5 13 | 14 | # Create rules 15 | if [ "$INET" = "4" ]; then 16 | iptables -t nat -A PREROUTING -d $LOCAL_IP -j DNAT --to-destination $DESTINATION -m comment --comment "$MARK" && 17 | iptables -t nat -A POSTROUTING -d $DESTINATION -o $INTERFACE -j MASQUERADE -m comment --comment "$MARK" && 18 | iptables -A FORWARD -d $DESTINATION -j ACCEPT -m comment --comment "$MARK" 19 | fi 20 | if [ "$INET" = "6" ]; then 21 | ip6tables -t nat -A PREROUTING -d $LOCAL_IP -j DNAT --to-destination $DESTINATION -m comment --comment "$MARK" && 22 | ip6tables -t nat -A POSTROUTING -d $DESTINATION -o $INTERFACE -j MASQUERADE -m comment --comment "$MARK" && 23 | ip6tables -A FORWARD -d $DESTINATION -j ACCEPT -m comment --comment "$MARK" 24 | fi 25 | 26 | echo "Rules added successfully" 27 | -------------------------------------------------------------------------------- /sh/iptables/delete.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 5 ]; then 4 | echo "Usage: $0 {localIP} {destination} {mark} {interface}" 5 | exit 1 6 | fi 7 | 8 | INET=$1 9 | LOCAL_IP=$2 10 | DESTINATION=$3 11 | MARK=$4 12 | INTERFACE=$5 13 | 14 | # Delete rules 15 | if [ "$INET" = "4" ]; then 16 | iptables -t nat -D PREROUTING -d $LOCAL_IP -j DNAT --to-destination $DESTINATION -m comment --comment "$MARK" && 17 | iptables -t nat -D POSTROUTING -d $DESTINATION -o $INTERFACE -j MASQUERADE -m comment --comment "$MARK" && 18 | iptables -D FORWARD -d $DESTINATION -j ACCEPT -m comment --comment "$MARK" 19 | fi 20 | if [ "$INET" = "6" ]; then 21 | ip6tables -t nat -D PREROUTING -d $LOCAL_IP -j DNAT --to-destination $DESTINATION -m comment --comment "$MARK" && 22 | ip6tables -t nat -D POSTROUTING -d $DESTINATION -o $INTERFACE -j MASQUERADE -m comment --comment "$MARK" && 23 | ip6tables -D FORWARD -d $DESTINATION -j ACCEPT -m comment --comment "$MARK" 24 | fi 25 | 26 | echo "Rules removed successfully" 27 | -------------------------------------------------------------------------------- /sh/iptables/onReset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 2 ]; then 4 | echo "Usage: $0 {mark} {NETTYPE}" 5 | exit 1 6 | fi 7 | 8 | MARK=$1 9 | NETTYPE=$2 10 | 11 | # Function delete iptables rules with mark 12 | delete_rules_with_mark() { 13 | local TABLE=$1 14 | local CHAIN=$2 15 | local MARK=$3 16 | 17 | # Получение номеров правил с пометкой 18 | RULE_NUMS=$(iptables -t $TABLE -L $CHAIN --line-numbers -n | grep -E "\b$MARK\b" | awk '{print $1}' | sort -nr) 19 | 20 | # Удаление правил с конца списка 21 | for RULE_NUM in $RULE_NUMS; do 22 | iptables -t $TABLE -D $CHAIN $RULE_NUM 23 | done 24 | } 25 | 26 | delete_rules_with_mark6() { 27 | local TABLE=$1 28 | local CHAIN=$2 29 | local MARK=$3 30 | 31 | # Получение номеров правил с пометкой 32 | RULE_NUMS=$(ip6tables -t $TABLE -L $CHAIN --line-numbers -n | grep -E "\b$MARK\b" | awk '{print $1}' | sort -nr) 33 | 34 | # Удаление правил с конца списка 35 | for RULE_NUM in $RULE_NUMS; do 36 | ip6tables -t $TABLE -D $CHAIN $RULE_NUM 37 | done 38 | } 39 | 40 | if [ "$NETTYPE" = "ipv4" ] || [ "$NETTYPE" = "ipv4ipv6" ]; then 41 | delete_rules_with_mark nat PREROUTING $MARK 42 | delete_rules_with_mark nat POSTROUTING $MARK 43 | delete_rules_with_mark filter FORWARD $MARK 44 | fi 45 | if [ "$NETTYPE" = "ipv6" ] || [ "$NETTYPE" = "ipv4ipv6" ]; then 46 | delete_rules_with_mark6 nat PREROUTING $MARK 47 | delete_rules_with_mark6 nat POSTROUTING $MARK 48 | delete_rules_with_mark6 filter FORWARD $MARK 49 | fi 50 | 51 | echo "All rules with mark \"$MARK\" have been removed successfully" 52 | -------------------------------------------------------------------------------- /sh/nftables/add.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 6 ]; then 4 | echo "Usage: $0 {inet} {localIP} {destination} {mark} {interface} {table_name_nat} {table_name_filter}" 5 | exit 1 6 | fi 7 | 8 | INET=$1 9 | LOCAL_IP=$2 10 | DESTINATION=$3 11 | MARK=$4 12 | INTERFACE=$5 13 | TABLE_NAME_NAT=$6 14 | # TABLE_NAME_FILTER=$6 15 | 16 | if [ "$INET" = "4" ]; then 17 | nft add rule ip $TABLE_NAME_NAT prerouting ip daddr $LOCAL_IP dnat to $DESTINATION comment \"$MARK\" && 18 | nft add rule ip $TABLE_NAME_NAT postrouting ip daddr $DESTINATION oif $INTERFACE masquerade comment \"$MARK\" && 19 | nft add rule ip $TABLE_NAME_NAT forward ip daddr $DESTINATION accept comment \"$MARK\" 20 | fi 21 | if [ "$INET" = "6" ]; then 22 | nft add rule ip6 $TABLE_NAME_NAT prerouting ip daddr $LOCAL_IP dnat to $DESTINATION comment \"$MARK\" && 23 | nft add rule ip6 $TABLE_NAME_NAT postrouting ip daddr $DESTINATION oif $INTERFACE masquerade comment \"$MARK\" && 24 | nft add rule ip6 $TABLE_NAME_FILTER forward ip daddr $DESTINATION accept comment \"$MARK\" 25 | fi 26 | 27 | echo "Rules added successfully with TTL of $TTL seconds" -------------------------------------------------------------------------------- /sh/nftables/delete.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 6 ]; then 4 | echo "Usage: $0 {mark}" 5 | exit 1 6 | fi 7 | 8 | INET=$1 9 | LOCAL_IP=$2 10 | DESTINATION=$3 11 | MARK=$4 12 | INTERFACE=$5 13 | TABLE_NAME_NAT=$6 14 | 15 | if [ "$INET" = "4" ]; then 16 | nft delete rule ip $TABLE_NAME_NAT prerouting ip daddr $LOCAL_IP dnat to $DESTINATION comment \"$MARK\" && 17 | nft delete rule ip $TABLE_NAME_NAT postrouting ip daddr $DESTINATION oif $INTERFACE masquerade comment \"$MARK\" && 18 | nft delete rule ip $TABLE_NAME_FILTER forward ip daddr $DESTINATION accept comment \"$MARK\" 19 | fi 20 | if [ "$INET" = "6" ]; then 21 | nft delete rule ip6 $TABLE_NAME_NAT prerouting ip daddr $LOCAL_IP dnat to $DESTINATION comment \"$MARK\" && 22 | nft delete rule ip6 $TABLE_NAME_NAT postrouting ip daddr $DESTINATION oif $INTERFACE masquerade comment \"$MARK\" && 23 | nft delete rule ip6 $TABLE_NAME_FILTER forward ip daddr $DESTINATION accept comment \"$MARK\" 24 | fi 25 | 26 | echo "Rules with mark \"$MARK\" deleted successfully" 27 | -------------------------------------------------------------------------------- /sh/nftables/onReset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 2 ]; then 4 | echo "Usage: $0 {tablename} {NETTYPE}" 5 | exit 1 6 | fi 7 | 8 | TABLE_NAME_NAT=$1 9 | NETTYPE=$2 10 | 11 | # Удаление правил с меткой 12 | if [ "$NETTYPE" = "ipv4" ] || [ "$NETTYPE" = "ipv4ipv6" ]; then 13 | nft flush table ip $TABLE_NAME_NAT 14 | fi 15 | if [ "$NETTYPE" = "ipv6" ] || [ "$NETTYPE" = "ipv4ipv6" ]; then 16 | nft flush table ip6 $TABLE_NAME_NAT 17 | fi 18 | 19 | echo "All rules flushed" -------------------------------------------------------------------------------- /sh/nftables/onStart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 1 ]; then 4 | echo "Usage: $0 {tablename}" 5 | exit 1 6 | fi 7 | 8 | # Название таблицы 9 | TNAME_NAT_INPUT=$1 10 | 11 | # Проверка наличия таблицы 12 | if ! nft list tables | grep -q "$TNAME_NAT_INPUT"; then 13 | echo "nftables script ipv4_onstart.sh $TNAME_NAT_INPUT not found...Creating" 14 | 15 | # Создание таблицы и цепочек 16 | nft add table inet $TNAME_NAT_INPUT 17 | nft add chain inet $TNAME_NAT_INPUT prerouting { type nat hook prerouting priority -100 \; } 18 | nft add chain inet $TNAME_NAT_INPUT postrouting { type nat hook postrouting priority 100 \; } 19 | nft add chain inet $TNAME_NAT_INPUT forward { type filter hook forward priority 0 \; } 20 | else 21 | echo "nftables script ipv4_onstart.sh $TNAME_NAT_INPUT exists" 22 | fi -------------------------------------------------------------------------------- /src/tmp_config_iptables.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | bind-address: "$BIND_ADDRESS" 3 | dns-forward: "$DNS_FORWARD" 4 | http-server: "$HTTP_BINDADDRESS" 5 | 6 | networks: 7 | private: 8 | ipv4: "$DEFAULT_IPV4_CIDR" 9 | ipv6: "$DEFAULT_IPV6_CIDR" 10 | 11 | actions: 12 | PROXY: 13 | method: "fakeip" 14 | fakeip-release-delay: 600 15 | ttl: 16 | max-transfer: 120 17 | min_rewrite: 100 18 | max_rewrite: 300 19 | dns-forward: "$DNS_FORWARD" 20 | variable: 21 | wan: "$IFACE" 22 | mark: "PROXY" 23 | nets: "$INSTALLTYPE" 24 | fakeip-networks: 25 | - "private" 26 | script: 27 | add: "sh sh/add.sh {inet} {fakeIP_private} {realIP} {mark} {wan}" 28 | delete: "sh sh/delete.sh {inet} {fakeIP_private} {realIP} {mark} {wan}" 29 | onreset: "sh sh/onReset.sh {mark} {nets}" -------------------------------------------------------------------------------- /src/tmp_config_nftables.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | bind-address: "$BIND_ADDRESS" 3 | dns-forward: "$DNS_FORWARD" 4 | http-server: "$HTTP_BINDADDRESS" 5 | 6 | networks: 7 | private: 8 | ipv4: "$DEFAULT_IPV4_CIDR" 9 | ipv6: "$DEFAULT_IPV6_CIDR" 10 | 11 | actions: 12 | PROXY: 13 | method: "fakeip" 14 | fakeip-release-delay: 600 15 | ttl: 16 | max-transfer: 120 17 | min_rewrite: 100 18 | max_rewrite: 300 19 | dns-forward: "$DNS_FORWARD" 20 | variable: 21 | wan: "$IFACE" 22 | mark: "PROXY" 23 | tname: "$TNAME_NAT" 24 | nets: "$INSTALLTYPE" 25 | fakeip-networks: 26 | - "private" 27 | script: 28 | add: "sh sh/add.sh {fakeIP_private} {realIP} {mark} {wan} {tname}" 29 | delete: "sh sh/delete.sh {fakeIP_private} {realIP} {mark} {wan} {tname}" 30 | onreset: "sh sh/onReset.sh {tname} {nets}" 31 | onstart: "sh sh/onStart.sh {tname}" --------------------------------------------------------------------------------