Browse Source

first commit

Gogs 1 week ago
commit
3d12f79ad2
14 changed files with 3609 additions and 0 deletions
  1. 674 0
      LICENSE
  2. 114 0
      README.md
  3. 443 0
      install.sh
  4. 6 0
      sing-box.sh
  5. 20 0
      src/bbr.sh
  6. 57 0
      src/caddy.sh
  7. 1729 0
      src/core.sh
  8. 83 0
      src/dns.sh
  9. 67 0
      src/download.sh
  10. 94 0
      src/help.sh
  11. 79 0
      src/import.sh
  12. 141 0
      src/init.sh
  13. 44 0
      src/log.sh
  14. 58 0
      src/systemd.sh

+ 674 - 0
LICENSE

@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.

+ 114 - 0
README.md

@@ -0,0 +1,114 @@
+# 介绍
+
+最好用的 sing-box 一键安装脚本 & 管理脚本
+
+# 特点
+
+- 快速安装
+- 无敌好用
+- 零学习成本
+- 自动化 TLS
+- 简化所有流程
+- 兼容 sing-box 命令
+- 强大的快捷参数
+- 支持所有常用协议
+- 一键添加 VLESS-REALITY (默认)
+- 一键添加 TUIC
+- 一键添加 Trojan
+- 一键添加 Hysteria2
+- 一键添加 Shadowsocks 2022
+- 一键添加 VMess-(TCP/HTTP/QUIC)
+- 一键添加 VMess-(WS/H2/HTTPUpgrade)-TLS
+- 一键添加 VLESS-(WS/H2/HTTPUpgrade)-TLS
+- 一键添加 Trojan-(WS/H2/HTTPUpgrade)-TLS
+- 一键启用 BBR
+- 一键更改伪装网站
+- 一键更改 (端口/UUID/密码/域名/路径/加密方式/SNI/等...)
+- 还有更多...
+
+# 设计理念
+
+设计理念为:**高效率,超快速,极易用**
+
+脚本基于作者的自身使用需求,以 **多配置同时运行** 为核心设计
+
+并且专门优化了,添加、更改、查看、删除、这四项常用功能
+
+你只需要一条命令即可完成 添加、更改、查看、删除、等操作
+
+例如,添加一个配置仅需不到 1 秒!瞬间完成添加!其他操作亦是如此!
+
+脚本的参数非常高效率并且超级易用,请掌握参数的使用
+
+# 文档
+
+安装及使用:https://233boy.com/sing-box/sing-box-script/
+
+# 帮助
+
+使用:`sing-box help`
+
+```
+sing-box script v1.0 by 233boy
+Usage: sing-box [options]... [args]...
+
+基本:
+   v, version                                      显示当前版本
+   ip                                              返回当前主机的 IP
+   pbk                                             同等于 sing-box generate reality-keypair
+   get-port                                        返回一个可用的端口
+   ss2022                                          返回一个可用于 Shadowsocks 2022 的密码
+
+一般:
+   a, add [protocol] [args... | auto]              添加配置
+   c, change [name] [option] [args... | auto]      更改配置
+   d, del [name]                                   删除配置**
+   i, info [name]                                  查看配置
+   qr [name]                                       二维码信息
+   url [name]                                      URL 信息
+   log                                             查看日志
+更改:
+   full [name] [...]                               更改多个参数
+   id [name] [uuid | auto]                         更改 UUID
+   host [name] [domain]                            更改域名
+   port [name] [port | auto]                       更改端口
+   path [name] [path | auto]                       更改路径
+   passwd [name] [password | auto]                 更改密码
+   key [name] [Private key | atuo] [Public key]    更改密钥
+   method [name] [method | auto]                   更改加密方式
+   sni [name] [ ip | domain]                       更改 serverName
+   new [name] [...]                                更改协议
+   web [name] [domain]                             更改伪装网站
+
+进阶:
+   dns [...]                                       设置 DNS
+   dd, ddel [name...]                              删除多个配置**
+   fix [name]                                      修复一个配置
+   fix-all                                         修复全部配置
+   fix-caddyfile                                   修复 Caddyfile
+   fix-config.json                                 修复 config.json
+   import                                          导入 sing-box/v2ray 脚本配置
+
+管理:
+   un, uninstall                                   卸载
+   u, update [core | sh | caddy] [ver]             更新
+   U, update.sh                                    更新脚本
+   s, status                                       运行状态
+   start, stop, restart [caddy]                    启动, 停止, 重启
+   t, test                                         测试运行
+   reinstall                                       重装脚本
+
+测试:
+   debug [name]                                    显示一些 debug 信息, 仅供参考
+   gen [...]                                       同等于 add, 但只显示 JSON 内容, 不创建文件, 测试使用
+   no-auto-tls [...]                               同等于 add, 但禁止自动配置 TLS, 可用于 *TLS 相关协议
+其他:
+   bbr                                             启用 BBR, 如果支持
+   bin [...]                                       运行 sing-box 命令, 例如: sing-box bin help
+   [...] [...]                                     兼容绝大多数的 sing-box 命令, 例如: sing-box generate uuid
+   h, help                                         显示此帮助界面
+
+谨慎使用 del, ddel, 此选项会直接删除配置; 无需确认
+反馈问题) https://github.com/233boy/sing-box/issues
+文档(doc) https://233boy.com/sing-box/sing-box-script/
+```

+ 443 - 0
install.sh

@@ -0,0 +1,443 @@
+#!/bin/bash
+
+author=233boy
+# github=https://github.com/233boy/sing-box
+
+# bash fonts colors
+red='\e[31m'
+yellow='\e[33m'
+gray='\e[90m'
+green='\e[92m'
+blue='\e[94m'
+magenta='\e[95m'
+cyan='\e[96m'
+none='\e[0m'
+_red() { echo -e ${red}$@${none}; }
+_blue() { echo -e ${blue}$@${none}; }
+_cyan() { echo -e ${cyan}$@${none}; }
+_green() { echo -e ${green}$@${none}; }
+_yellow() { echo -e ${yellow}$@${none}; }
+_magenta() { echo -e ${magenta}$@${none}; }
+_red_bg() { echo -e "\e[41m$@${none}"; }
+
+is_err=$(_red_bg 错误!)
+is_warn=$(_red_bg 警告!)
+
+err() {
+    echo -e "\n$is_err $@\n" && exit 1
+}
+
+warn() {
+    echo -e "\n$is_warn $@\n"
+}
+
+# root
+[[ $EUID != 0 ]] && err "当前非 ${yellow}ROOT用户.${none}"
+
+# yum or apt-get, ubuntu/debian/centos
+cmd=$(type -P apt-get || type -P yum)
+[[ ! $cmd ]] && err "此脚本仅支持 ${yellow}(Ubuntu or Debian or CentOS)${none}."
+
+# systemd
+[[ ! $(type -P systemctl) ]] && {
+    err "此系统缺少 ${yellow}(systemctl)${none}, 请尝试执行:${yellow} ${cmd} update -y;${cmd} install systemd -y ${none}来修复此错误."
+}
+
+# wget installed or none
+is_wget=$(type -P wget)
+
+# x64
+case $(uname -m) in
+amd64 | x86_64)
+    is_arch=amd64
+    ;;
+*aarch64* | *armv8*)
+    is_arch=arm64
+    ;;
+*)
+    err "此脚本仅支持 64 位系统..."
+    ;;
+esac
+
+is_core=sing-box
+is_core_name=sing-box
+is_core_dir=/develop/$is_core
+is_core_bin=$is_core_dir/bin/$is_core
+is_core_repo=SagerNet/$is_core
+is_conf_dir=$is_core_dir/conf
+is_log_dir=/var/log/$is_core
+is_sh_bin=/usr/local/bin/$is_core
+is_sh_dir=$is_core_dir/sh
+is_sh_repo=$author/$is_core
+is_pkg="wget tar"
+is_config_json=$is_core_dir/config.json
+tmp_var_lists=(
+    tmpcore
+    tmpsh
+    tmpjq
+    is_core_ok
+    is_sh_ok
+    is_jq_ok
+    is_pkg_ok
+)
+
+# tmp dir
+tmpdir=$(mktemp -u)
+[[ ! $tmpdir ]] && {
+    tmpdir=/tmp/tmp-$RANDOM
+}
+
+# set up var
+for i in ${tmp_var_lists[*]}; do
+    export $i=$tmpdir/$i
+done
+
+# load bash script.
+load() {
+    . $is_sh_dir/src/$1
+}
+
+# wget add --no-check-certificate
+_wget() {
+    [[ $proxy ]] && export https_proxy=$proxy
+    wget --no-check-certificate $*
+}
+
+# print a mesage
+msg() {
+    case $1 in
+    warn)
+        local color=$yellow
+        ;;
+    err)
+        local color=$red
+        ;;
+    ok)
+        local color=$green
+        ;;
+    esac
+
+    echo -e "${color}$(date +'%T')${none}) ${2}"
+}
+
+# show help msg
+show_help() {
+    echo -e "Usage: $0 [-f xxx | -l | -p xxx | -v xxx | -h]"
+    echo -e "  -f, --core-file <path>          自定义 $is_core_name 文件路径, e.g., -f /root/$is_core-linux-amd64.tar.gz"
+    echo -e "  -l, --local-install             本地获取安装脚本, 使用当前目录"
+    echo -e "  -p, --proxy <addr>              使用代理下载, e.g., -p http://127.0.0.1:2333"
+    echo -e "  -v, --core-version <ver>        自定义 $is_core_name 版本, e.g., -v v1.8.13"
+    echo -e "  -h, --help                      显示此帮助界面\n"
+
+    exit 0
+}
+
+# install dependent pkg
+install_pkg() {
+    cmd_not_found=
+    for i in $*; do
+        [[ ! $(type -P $i) ]] && cmd_not_found="$cmd_not_found,$i"
+    done
+    if [[ $cmd_not_found ]]; then
+        pkg=$(echo $cmd_not_found | sed 's/,/ /g')
+        msg warn "安装依赖包 >${pkg}"
+        $cmd install -y $pkg &>/dev/null
+        if [[ $? != 0 ]]; then
+            [[ $cmd =~ yum ]] && yum install epel-release -y &>/dev/null
+            $cmd update -y &>/dev/null
+            $cmd install -y $pkg &>/dev/null
+            [[ $? == 0 ]] && >$is_pkg_ok
+        else
+            >$is_pkg_ok
+        fi
+    else
+        >$is_pkg_ok
+    fi
+}
+
+# download file
+download() {
+    case $1 in
+    core)
+        [[ ! $is_core_ver ]] && is_core_ver=$(_wget -qO- "https://api.github.com/repos/${is_core_repo}/releases/latest?v=$RANDOM" | grep tag_name | grep -E -o 'v([0-9.]+)')
+        [[ $is_core_ver ]] && link="https://download.030208.xyz/sing-box/sing-box-1.12.12-linux-amd64.tar.gz"
+        name=$is_core_name
+        tmpfile=$tmpcore
+        is_ok=$is_core_ok
+        ;;
+    sh)
+        link=https://download.030208.xyz/code.tar.gz
+        name="$is_core_name 脚本"
+        tmpfile=$tmpsh
+        is_ok=$is_sh_ok
+        ;;
+    jq)
+        link=https://download.030208.xyz/jq-linux-amd64
+        name="jq"
+        tmpfile=$tmpjq
+        is_ok=$is_jq_ok
+        ;;
+    esac
+
+    [[ $link ]] && {
+        msg warn "下载 ${name} > ${link}"
+        if _wget -t 3 -q -c $link -O $tmpfile; then
+            mv -f $tmpfile $is_ok
+        fi
+    }
+}
+
+# get server ip
+get_ip() {
+    [[ $ip || $is_no_auto_tls || $is_gen || $is_dont_get_ip ]] && return
+
+    # 尝试通过 IPv4 获取
+    ip=$(_wget -4 -qO- https://ifconfig.me/ip 2>/dev/null)
+    # 如果 IPv4 获取失败,尝试通过 IPv6 获取
+    [[ -z "$ip" ]] && ip=$(_wget -6 -qO- https://ifconfig.me/ip 2>/dev/null)
+    
+    # 验证获取到的 IP 地址格式是否合法(简单的 IPv4 校验)
+    if [[ -z "$ip" || ! "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+        err "获取服务器 IP 失败.."
+    fi
+}
+# check background tasks status
+check_status() {
+    # dependent pkg install fail
+    [[ ! -f $is_pkg_ok ]] && {
+        msg err "安装依赖包失败"
+        msg err "请尝试手动安装依赖包: $cmd update -y; $cmd install -y $is_pkg"
+        is_fail=1
+    }
+
+    # download file status
+    if [[ $is_wget ]]; then
+        [[ ! -f $is_core_ok ]] && {
+            msg err "下载 ${is_core_name} 失败"
+            is_fail=1
+        }
+        [[ ! -f $is_sh_ok ]] && {
+            msg err "下载 ${is_core_name} 脚本失败"
+            is_fail=1
+        }
+        [[ ! -f $is_jq_ok ]] && {
+            msg err "下载 jq 失败"
+            is_fail=1
+        }
+    else
+        [[ ! $is_fail ]] && {
+            is_wget=1
+            [[ ! $is_core_file ]] && download core &
+            [[ ! $local_install ]] && download sh &
+            [[ $jq_not_found ]] && download jq &
+            get_ip
+            wait
+            check_status
+        }
+    fi
+
+    # found fail status, remove tmp dir and exit.
+    [[ $is_fail ]] && {
+        exit_and_del_tmpdir
+    }
+}
+
+# parameters check
+pass_args() {
+    while [[ $# -gt 0 ]]; do
+        case $1 in
+        -f | --core-file)
+            [[ -z $2 ]] && {
+                err "($1) 缺少必需参数, 正确使用示例: [$1 /root/$is_core-linux-amd64.tar.gz]"
+            } || [[ ! -f $2 ]] && {
+                err "($2) 不是一个常规的文件."
+            }
+            is_core_file=$2
+            shift 2
+            ;;
+        -l | --local-install)
+            [[ ! -f ${PWD}/src/core.sh || ! -f ${PWD}/$is_core.sh ]] && {
+                err "当前目录 (${PWD}) 非完整的脚本目录."
+            }
+            local_install=1
+            shift 1
+            ;;
+        -p | --proxy)
+            [[ -z $2 ]] && {
+                err "($1) 缺少必需参数, 正确使用示例: [$1 http://127.0.0.1:2333 or -p socks5://127.0.0.1:2333]"
+            }
+            proxy=$2
+            shift 2
+            ;;
+        -v | --core-version)
+            [[ -z $2 ]] && {
+                err "($1) 缺少必需参数, 正确使用示例: [$1 v1.8.13]"
+            }
+            is_core_ver=v${2//v/}
+            shift 2
+            ;;
+        -h | --help)
+            show_help
+            ;;
+        *)
+            echo -e "\n${is_err} ($@) 为未知参数...\n"
+            show_help
+            ;;
+        esac
+    done
+    [[ $is_core_ver && $is_core_file ]] && {
+        err "无法同时自定义 ${is_core_name} 版本和 ${is_core_name} 文件."
+    }
+}
+
+# exit and remove tmpdir
+exit_and_del_tmpdir() {
+    rm -rf $tmpdir
+    [[ ! $1 ]] && {
+        msg err "哦豁.."
+        msg err "安装过程出现错误..."
+        echo -e "反馈问题) https://github.com/${is_sh_repo}/issues"
+        echo
+        exit 1
+    }
+    exit
+}
+
+# main
+main() {
+
+    # check old version
+    [[ -f $is_sh_bin && -d $is_core_dir/bin && -d $is_sh_dir && -d $is_conf_dir ]] && {
+        err "检测到脚本已安装, 如需重装请使用${green} ${is_core} reinstall ${none}命令."
+    }
+
+    # check parameters
+    [[ $# -gt 0 ]] && pass_args $@
+
+    # show welcome msg
+    clear
+    echo
+    echo "........... $is_core_name script by $author .........."
+    echo
+
+    # start installing...
+    msg warn "开始安装..."
+    [[ $is_core_ver ]] && msg warn "${is_core_name} 版本: ${yellow}$is_core_ver${none}"
+    [[ $proxy ]] && msg warn "使用代理: ${yellow}$proxy${none}"
+    # create tmpdir
+    mkdir -p $tmpdir
+    # if is_core_file, copy file
+    [[ $is_core_file ]] && {
+        cp -f $is_core_file $is_core_ok
+        msg warn "${yellow}${is_core_name} 文件使用 > $is_core_file${none}"
+    }
+    # local dir install sh script
+    [[ $local_install ]] && {
+        >$is_sh_ok
+        msg warn "${yellow}本地获取安装脚本 > $PWD ${none}"
+    }
+
+    timedatectl set-ntp true &>/dev/null
+    [[ $? != 0 ]] && {
+        is_ntp_on=1
+    }
+
+    # install dependent pkg
+    install_pkg $is_pkg &
+
+    # jq
+    if [[ $(type -P jq) ]]; then
+        >$is_jq_ok
+    else
+        jq_not_found=1
+    fi
+    # if wget installed. download core, sh, jq, get ip
+    [[ $is_wget ]] && {
+        [[ ! $is_core_file ]] && download core &
+        [[ ! $local_install ]] && download sh &
+        [[ $jq_not_found ]] && download jq &
+        get_ip
+    }
+
+    # waiting for background tasks is done
+    wait
+
+    # check background tasks status
+    check_status
+
+    # test $is_core_file
+    if [[ $is_core_file ]]; then
+        mkdir -p $tmpdir/testzip
+        tar zxf $is_core_ok --strip-components 1 -C $tmpdir/testzip &>/dev/null
+        [[ $? != 0 ]] && {
+            msg err "${is_core_name} 文件无法通过测试."
+            exit_and_del_tmpdir
+        }
+        [[ ! -f $tmpdir/testzip/$is_core ]] && {
+            msg err "${is_core_name} 文件无法通过测试."
+            exit_and_del_tmpdir
+        }
+    fi
+
+    # get server ip.
+    [[ ! $ip ]] && {
+        msg err "获取服务器 IP 失败."
+        exit_and_del_tmpdir
+    }
+
+    # create sh dir...
+    mkdir -p $is_sh_dir
+
+    # copy sh file or unzip sh zip file.
+    if [[ $local_install ]]; then
+        cp -rf $PWD/* $is_sh_dir
+    else
+        tar zxf $is_sh_ok -C $is_sh_dir
+    fi
+
+    # create core bin dir
+    mkdir -p $is_core_dir/bin
+    # copy core file or unzip core zip file
+    if [[ $is_core_file ]]; then
+        cp -rf $tmpdir/testzip/* $is_core_dir/bin
+    else
+        tar zxf $is_core_ok --strip-components 1 -C $is_core_dir/bin
+    fi
+
+    # add alias
+    echo "alias sb=$is_sh_bin" >>/root/.bashrc
+    echo "alias $is_core=$is_sh_bin" >>/root/.bashrc
+
+    # core command
+    ln -sf $is_sh_dir/$is_core.sh $is_sh_bin
+    ln -sf $is_sh_dir/$is_core.sh ${is_sh_bin/$is_core/sb}
+
+    # jq
+    [[ $jq_not_found ]] && mv -f $is_jq_ok /usr/bin/jq
+
+    # chmod
+    chmod +x $is_core_bin $is_sh_bin /usr/bin/jq ${is_sh_bin/$is_core/sb}
+
+    # create log dir
+    mkdir -p $is_log_dir
+
+    # show a tips msg
+    msg ok "生成配置文件..."
+
+    # create systemd service
+    load systemd.sh
+    is_new_install=1
+    install_service $is_core &>/dev/null
+
+    # create condf dir
+    mkdir -p $is_conf_dir
+
+    load core.sh
+    # create a reality config
+    add reality
+    # remove tmp dir and exit.
+    exit_and_del_tmpdir ok
+}
+
+# start.
+main $@

+ 6 - 0
sing-box.sh

@@ -0,0 +1,6 @@
+#!/bin/bash
+
+args=$@
+is_sh_ver=v1.13
+
+. /develop/sing-box/sh/src/init.sh

+ 20 - 0
src/bbr.sh

@@ -0,0 +1,20 @@
+_open_bbr() {
+	sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.conf
+	sed -i '/net.core.default_qdisc/d' /etc/sysctl.conf
+	echo "net.ipv4.tcp_congestion_control = bbr" >>/etc/sysctl.conf
+	echo "net.core.default_qdisc = fq" >>/etc/sysctl.conf
+	sysctl -p &>/dev/null
+	echo
+	_green "..已经启用 BBR 优化...."
+	echo
+}
+
+_try_enable_bbr() {
+	local _test1=$(uname -r | cut -d\. -f1)
+	local _test2=$(uname -r | cut -d\. -f2)
+	if [[ $_test1 -eq 4 && $_test2 -ge 9 ]] || [[ $_test1 -ge 5 ]]; then
+		_open_bbr
+	else
+		err "不支持启用 BBR 优化."
+	fi
+}

+ 57 - 0
src/caddy.sh

@@ -0,0 +1,57 @@
+caddy_config() {
+    is_caddy_site_file=$is_caddy_conf/${host}.conf
+    case $1 in
+    new)
+        mkdir -p $is_caddy_dir $is_caddy_dir/sites $is_caddy_conf
+        cat >$is_caddyfile <<-EOF
+# don't edit this file #
+# for more info, see https://233boy.com/$is_core/caddy-auto-tls/
+# 不要编辑这个文件 #
+# 更多相关请阅读此文章: https://233boy.com/$is_core/caddy-auto-tls/
+# https://caddyserver.com/docs/caddyfile/options
+{
+  admin off
+  http_port $is_http_port
+  https_port $is_https_port
+}
+import $is_caddy_conf/*.conf
+import $is_caddy_dir/sites/*.conf
+EOF
+        ;;
+    *ws* | *http*)
+        cat >${is_caddy_site_file} <<<"
+${host}:${is_https_port} {
+    reverse_proxy ${path} 127.0.0.1:${port}
+    import ${is_caddy_site_file}.add
+}"
+        ;;
+    *h2*)
+        cat >${is_caddy_site_file} <<<"
+${host}:${is_https_port} {
+    reverse_proxy ${path} h2c://127.0.0.1:${port} {
+        transport http {
+			tls_insecure_skip_verify
+		}
+    }
+    import ${is_caddy_site_file}.add
+}"
+        ;;
+    *grpc*)
+        cat >${is_caddy_site_file} <<<"
+${host}:${is_https_port} {
+    reverse_proxy /${path}/* h2c://127.0.0.1:${port}
+    import ${is_caddy_site_file}.add
+}"
+        ;;
+    proxy)
+
+        cat >${is_caddy_site_file}.add <<<"
+reverse_proxy https://$proxy_site {
+        header_up Host {upstream_hostport}
+}"
+        ;;
+    esac
+    [[ $1 != "new" && $1 != 'proxy' ]] && {
+        [[ ! -f ${is_caddy_site_file}.add ]] && echo "# see https://233boy.com/$is_core/caddy-auto-tls/" >${is_caddy_site_file}.add
+    }
+}

+ 1729 - 0
src/core.sh

@@ -0,0 +1,1729 @@
+#!/bin/bash
+
+protocol_list=(
+    TUIC
+    Trojan
+    Hysteria2
+    VMess-WS
+    VMess-TCP
+    VMess-HTTP
+    VMess-QUIC
+    Shadowsocks
+    VMess-H2-TLS
+    VMess-WS-TLS
+    VLESS-H2-TLS
+    VLESS-WS-TLS
+    Trojan-H2-TLS
+    Trojan-WS-TLS
+    VMess-HTTPUpgrade-TLS
+    VLESS-HTTPUpgrade-TLS
+    Trojan-HTTPUpgrade-TLS
+    VLESS-REALITY
+    VLESS-HTTP2-REALITY
+    # Direct
+    Socks
+)
+ss_method_list=(
+    aes-128-gcm
+    aes-256-gcm
+    chacha20-ietf-poly1305
+    xchacha20-ietf-poly1305
+    2022-blake3-aes-128-gcm
+    2022-blake3-aes-256-gcm
+    2022-blake3-chacha20-poly1305
+)
+mainmenu=(
+    "添加配置"
+    "更改配置"
+    "查看配置"
+    "删除配置"
+    "运行管理"
+    "更新"
+    "卸载"
+    "帮助"
+    "其他"
+    "关于"
+)
+info_list=(
+    "协议 (protocol)"
+    "地址 (address)"
+    "端口 (port)"
+    "用户ID (id)"
+    "传输协议 (network)"
+    "伪装类型 (type)"
+    "伪装域名 (host)"
+    "路径 (path)"
+    "传输层安全 (TLS)"
+    "应用层协议协商 (Alpn)"
+    "密码 (password)"
+    "加密方式 (encryption)"
+    "链接 (URL)"
+    "目标地址 (remote addr)"
+    "目标端口 (remote port)"
+    "流控 (flow)"
+    "SNI (serverName)"
+    "指纹 (Fingerprint)"
+    "公钥 (Public key)"
+    "用户名 (Username)"
+    "跳过证书验证 (allowInsecure)"
+    "拥塞控制算法 (congestion_control)"
+)
+change_list=(
+    "更改协议"
+    "更改端口"
+    "更改域名"
+    "更改路径"
+    "更改密码"
+    "更改 UUID"
+    "更改加密方式"
+    "更改目标地址"
+    "更改目标端口"
+    "更改密钥"
+    "更改 SNI (serverName)"
+    "更改伪装网站"
+    "更改用户名 (Username)"
+)
+servername_list=(
+    www.amazon.com
+    www.ebay.com
+    www.paypal.com
+    www.cloudflare.com
+    dash.cloudflare.com
+    aws.amazon.com
+)
+
+is_random_ss_method=${ss_method_list[$(shuf -i 4-6 -n1)]} # random only use ss2022
+is_random_servername=${servername_list[$(shuf -i 0-${#servername_list[@]} -n1) - 1]}
+
+msg() {
+    echo -e "$@"
+}
+
+msg_ul() {
+    echo -e "\e[4m$@\e[0m"
+}
+
+# pause
+pause() {
+    echo
+    echo -ne "按 $(_green Enter 回车键) 继续, 或按 $(_red Ctrl + C) 取消."
+    read -rs -d $'\n'
+    echo
+}
+
+get_uuid() {
+    tmp_uuid=$(cat /proc/sys/kernel/random/uuid)
+}
+
+get_ip() {
+    [[ $ip || $is_no_auto_tls || $is_gen || $is_dont_get_ip ]] && return
+
+    # 尝试通过 IPv4 获取
+    ip=$(_wget -4 -qO- https://ifconfig.me/ip 2>/dev/null)
+    # 如果 IPv4 获取失败,尝试通过 IPv6 获取
+    [[ -z "$ip" ]] && ip=$(_wget -6 -qO- https://ifconfig.me/ip 2>/dev/null)
+    
+    # 验证获取到的 IP 地址格式是否合法(简单的 IPv4 校验)
+    if [[ -z "$ip" || ! "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+        err "获取服务器 IP 失败.."
+    fi
+}
+
+get_port() {
+    is_count=0
+    while :; do
+        ((is_count++))
+        if [[ $is_count -ge 233 ]]; then
+            err "自动获取可用端口失败次数达到 233 次, 请检查端口占用情况."
+        fi
+        tmp_port=$(shuf -i 445-65535 -n 1)
+        [[ ! $(is_test port_used $tmp_port) && $tmp_port != $port ]] && break
+    done
+}
+
+get_pbk() {
+    is_tmp_pbk=($($is_core_bin generate reality-keypair | sed 's/.*://'))
+    is_public_key=${is_tmp_pbk[1]}
+    is_private_key=${is_tmp_pbk[0]}
+}
+
+show_list() {
+    PS3=''
+    COLUMNS=1
+    select i in "$@"; do echo; done &
+    wait
+    # i=0
+    # for v in "$@"; do
+    #     ((i++))
+    #     echo "$i) $v"
+    # done
+    # echo
+
+}
+
+is_test() {
+    case $1 in
+    number)
+        echo $2 | grep -E '^[1-9][0-9]?+$'
+        ;;
+    port)
+        if [[ $(is_test number $2) ]]; then
+            [[ $2 -le 65535 ]] && echo ok
+        fi
+        ;;
+    port_used)
+        [[ $(is_port_used $2) && ! $is_cant_test_port ]] && echo ok
+        ;;
+    domain)
+        echo $2 | grep -E -i '^\w(\w|\-|\.)?+\.\w+$'
+        ;;
+    path)
+        echo $2 | grep -E -i '^\/\w(\w|\-|\/)?+\w$'
+        ;;
+    uuid)
+        echo $2 | grep -E -i '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
+        ;;
+    esac
+
+}
+
+is_port_used() {
+    if [[ $(type -P netstat) ]]; then
+        [[ ! $is_used_port ]] && is_used_port="$(netstat -tunlp | sed -n 's/.*:\([0-9]\+\).*/\1/p' | sort -nu)"
+        echo $is_used_port | sed 's/ /\n/g' | grep ^${1}$
+        return
+    fi
+    if [[ $(type -P ss) ]]; then
+        [[ ! $is_used_port ]] && is_used_port="$(ss -tunlp | sed -n 's/.*:\([0-9]\+\).*/\1/p' | sort -nu)"
+        echo $is_used_port | sed 's/ /\n/g' | grep ^${1}$
+        return
+    fi
+    is_cant_test_port=1
+    msg "$is_warn 无法检测端口是否可用."
+    msg "请执行: $(_yellow "${cmd} update -y; ${cmd} install net-tools -y") 来修复此问题."
+}
+
+# ask input a string or pick a option for list.
+ask() {
+    case $1 in
+    set_ss_method)
+        is_tmp_list=(${ss_method_list[@]})
+        is_default_arg=$is_random_ss_method
+        is_opt_msg="\n请选择加密方式:\n"
+        is_opt_input_msg="(默认\e[92m $is_default_arg\e[0m):"
+        is_ask_set=ss_method
+        ;;
+    set_protocol)
+        is_tmp_list=(${protocol_list[@]})
+        [[ $is_no_auto_tls ]] && {
+            unset is_tmp_list
+            for v in ${protocol_list[@]}; do
+                [[ $(grep -i tls$ <<<$v) ]] && is_tmp_list=(${is_tmp_list[@]} $v)
+            done
+        }
+        is_opt_msg="\n请选择协议:\n"
+        is_ask_set=is_new_protocol
+        ;;
+    set_change_list)
+        is_tmp_list=()
+        for v in ${is_can_change[@]}; do
+            is_tmp_list+=("${change_list[$v]}")
+        done
+        is_opt_msg="\n请选择更改:\n"
+        is_ask_set=is_change_str
+        is_opt_input_msg=$3
+        ;;
+    string)
+        is_ask_set=$2
+        is_opt_input_msg=$3
+        ;;
+    list)
+        is_ask_set=$2
+        [[ ! $is_tmp_list ]] && is_tmp_list=($3)
+        is_opt_msg=$4
+        is_opt_input_msg=$5
+        ;;
+    get_config_file)
+        is_tmp_list=("${is_all_json[@]}")
+        is_opt_msg="\n请选择配置:\n"
+        is_ask_set=is_config_file
+        ;;
+    mainmenu)
+        is_tmp_list=("${mainmenu[@]}")
+        is_ask_set=is_main_pick
+        is_emtpy_exit=1
+        ;;
+    esac
+    msg $is_opt_msg
+    [[ ! $is_opt_input_msg ]] && is_opt_input_msg="请选择 [\e[91m1-${#is_tmp_list[@]}\e[0m]:"
+    [[ $is_tmp_list ]] && show_list "${is_tmp_list[@]}"
+    while :; do
+        echo -ne $is_opt_input_msg
+        read REPLY
+        [[ ! $REPLY && $is_emtpy_exit ]] && exit
+        [[ ! $REPLY && $is_default_arg ]] && export $is_ask_set=$is_default_arg && break
+        [[ "$REPLY" == "${is_str}2${is_get}3${is_opt}3" && $is_ask_set == 'is_main_pick' ]] && {
+            msg "\n${is_get}2${is_str}3${is_msg}3b${is_tmp}o${is_opt}y\n" && exit
+        }
+        if [[ ! $is_tmp_list ]]; then
+            [[ $(grep port <<<$is_ask_set) ]] && {
+                [[ ! $(is_test port "$REPLY") ]] && {
+                    msg "$is_err 请输入正确的端口, 可选(1-65535)"
+                    continue
+                }
+                if [[ $(is_test port_used $REPLY) && $is_ask_set != 'door_port' ]]; then
+                    msg "$is_err 无法使用 ($REPLY) 端口."
+                    continue
+                fi
+            }
+            [[ $(grep path <<<$is_ask_set) && ! $(is_test path "$REPLY") ]] && {
+                [[ ! $tmp_uuid ]] && get_uuid
+                msg "$is_err 请输入正确的路径, 例如: /$tmp_uuid"
+                continue
+            }
+            [[ $(grep uuid <<<$is_ask_set) && ! $(is_test uuid "$REPLY") ]] && {
+                [[ ! $tmp_uuid ]] && get_uuid
+                msg "$is_err 请输入正确的 UUID, 例如: $tmp_uuid"
+                continue
+            }
+            [[ $(grep ^y$ <<<$is_ask_set) ]] && {
+                [[ $(grep -i ^y$ <<<"$REPLY") ]] && break
+                msg "请输入 (y)"
+                continue
+            }
+            [[ $REPLY ]] && export $is_ask_set=$REPLY && msg "使用: ${!is_ask_set}" && break
+        else
+            [[ $(is_test number "$REPLY") ]] && is_ask_result=${is_tmp_list[$REPLY - 1]}
+            [[ $is_ask_result ]] && export $is_ask_set="$is_ask_result" && msg "选择: ${!is_ask_set}" && break
+        fi
+
+        msg "输入${is_err}"
+    done
+    unset is_opt_msg is_opt_input_msg is_tmp_list is_ask_result is_default_arg is_emtpy_exit
+}
+
+# create file
+create() {
+    case $1 in
+    server)
+        is_tls=none
+        get new
+        # listen
+        is_listen='listen: "::"'
+        # file name
+        if [[ $host ]]; then
+            is_config_name=$2-${host}.json
+            is_listen='listen: "127.0.0.1"'
+        else
+            is_config_name=$2-${port}.json
+        fi
+        is_json_file=$is_conf_dir/$is_config_name
+        # get json
+        [[ $is_change || ! $json_str ]] && get protocol $2
+        [[ $net == "reality" ]] && is_add_public_key=",outbounds:[{type:\"direct\"},{tag:\"public_key_$is_public_key\",type:\"direct\"}]"
+        is_new_json=$(jq "{inbounds:[{tag:\"$is_config_name\",type:\"$is_protocol\",$is_listen,listen_port:$port,$json_str}]$is_add_public_key}" <<<{})
+        [[ $is_test_json ]] && return # tmp test
+        # only show json, dont save to file.
+        [[ $is_gen ]] && {
+            msg
+            jq <<<$is_new_json
+            msg
+            return
+        }
+        # del old file
+        [[ $is_config_file ]] && is_no_del_msg=1 && del $is_config_file
+        # save json to file
+        cat <<<$is_new_json >$is_json_file
+        if [[ $is_new_install ]]; then
+            # config.json
+            create config.json
+        fi
+        # caddy auto tls
+        [[ $is_caddy && $host && ! $is_no_auto_tls ]] && {
+            create caddy $net
+        }
+        # restart core
+        manage restart &
+        ;;
+    client)
+        is_tls=tls
+        is_client=1
+        get info $2
+        [[ ! $is_client_id_json ]] && err "($is_config_name) 不支持生成客户端配置."
+        is_new_json=$(jq '{outbounds:[{tag:'\"$is_config_name\"',protocol:'\"$is_protocol\"','"$is_client_id_json"','"$is_stream"'}]}' <<<{})
+        msg
+        jq <<<$is_new_json
+        msg
+        ;;
+    caddy)
+        load caddy.sh
+        [[ $is_install_caddy ]] && caddy_config new
+        [[ ! $(grep "$is_caddy_conf" $is_caddyfile) ]] && {
+            msg "import $is_caddy_conf/*.conf" >>$is_caddyfile
+        }
+        [[ ! -d $is_caddy_conf ]] && mkdir -p $is_caddy_conf
+        caddy_config $2
+        manage restart caddy &
+        ;;
+    config.json)
+        is_log='log:{output:"/var/log/'$is_core'/access.log",level:"info","timestamp":true}'
+        is_dns='dns:{}'
+        is_ntp='ntp:{"enabled":true,"server":"time.apple.com"},'
+        if [[ -f $is_config_json ]]; then
+            [[ $(jq .ntp.enabled $is_config_json) != "true" ]] && is_ntp=
+        else
+            [[ ! $is_ntp_on ]] && is_ntp=
+        fi
+        is_outbounds='outbounds:[{tag:"direct",type:"direct"}]'
+        is_server_config_json=$(jq "{$is_log,$is_dns,$is_ntp$is_outbounds}" <<<{})
+        cat <<<$is_server_config_json >$is_config_json
+        manage restart &
+        ;;
+    esac
+}
+
+# change config file
+change() {
+    is_change=1
+    is_dont_show_info=1
+    if [[ $2 ]]; then
+        case ${2,,} in
+        full)
+            is_change_id=full
+            ;;
+        new)
+            is_change_id=0
+            ;;
+        port)
+            is_change_id=1
+            ;;
+        host)
+            is_change_id=2
+            ;;
+        path)
+            is_change_id=3
+            ;;
+        pass | passwd | password)
+            is_change_id=4
+            ;;
+        id | uuid)
+            is_change_id=5
+            ;;
+        ssm | method | ss-method | ss_method)
+            is_change_id=6
+            ;;
+        dda | door-addr | door_addr)
+            is_change_id=7
+            ;;
+        ddp | door-port | door_port)
+            is_change_id=8
+            ;;
+        key | publickey | privatekey)
+            is_change_id=9
+            ;;
+        sni | servername | servernames)
+            is_change_id=10
+            ;;
+        web | proxy-site)
+            is_change_id=11
+            ;;
+        *)
+            [[ $is_try_change ]] && return
+            err "无法识别 ($2) 更改类型."
+            ;;
+        esac
+    fi
+    [[ $is_try_change ]] && return
+    [[ $is_dont_auto_exit ]] && {
+        get info $1
+    } || {
+        [[ $is_change_id ]] && {
+            is_change_msg=${change_list[$is_change_id]}
+            [[ $is_change_id == 'full' ]] && {
+                [[ $3 ]] && is_change_msg="更改多个参数" || is_change_msg=
+            }
+            [[ $is_change_msg ]] && _green "\n快速执行: $is_change_msg"
+        }
+        info $1
+        [[ $is_auto_get_config ]] && msg "\n自动选择: $is_config_file"
+    }
+    is_old_net=$net
+    [[ $is_tcp_http ]] && net=http
+    [[ $host ]] && net=$is_protocol-$net-tls
+    [[ $is_reality && $net_type =~ 'http' ]] && net=rh2
+
+    [[ $3 == 'auto' ]] && is_auto=1
+    # if is_dont_show_info exist, cant show info.
+    is_dont_show_info=
+    # if not prefer args, show change list and then get change id.
+    [[ ! $is_change_id ]] && {
+        ask set_change_list
+        is_change_id=${is_can_change[$REPLY - 1]}
+    }
+    case $is_change_id in
+    full)
+        add $net ${@:3}
+        ;;
+    0)
+        # new protocol
+        is_set_new_protocol=1
+        add ${@:3}
+        ;;
+    1)
+        # new port
+        is_new_port=$3
+        [[ $host && ! $is_caddy || $is_no_auto_tls ]] && err "($is_config_file) 不支持更改端口, 因为没啥意义."
+        if [[ $is_new_port && ! $is_auto ]]; then
+            [[ ! $(is_test port $is_new_port) ]] && err "请输入正确的端口, 可选(1-65535)"
+            [[ $is_new_port != 443 && $(is_test port_used $is_new_port) ]] && err "无法使用 ($is_new_port) 端口"
+        fi
+        [[ $is_auto ]] && get_port && is_new_port=$tmp_port
+        [[ ! $is_new_port ]] && ask string is_new_port "请输入新端口:"
+        if [[ $is_caddy && $host ]]; then
+            net=$is_old_net
+            is_https_port=$is_new_port
+            load caddy.sh
+            caddy_config $net
+            manage restart caddy &
+            info
+        else
+            add $net $is_new_port
+        fi
+        ;;
+    2)
+        # new host
+        is_new_host=$3
+        [[ ! $host ]] && err "($is_config_file) 不支持更改域名."
+        [[ ! $is_new_host ]] && ask string is_new_host "请输入新域名:"
+        old_host=$host # del old host
+        add $net $is_new_host
+        ;;
+    3)
+        # new path
+        is_new_path=$3
+        [[ ! $path ]] && err "($is_config_file) 不支持更改路径."
+        [[ $is_auto ]] && get_uuid && is_new_path=/$tmp_uuid
+        [[ ! $is_new_path ]] && ask string is_new_path "请输入新路径:"
+        add $net auto auto $is_new_path
+        ;;
+    4)
+        # new password
+        is_new_pass=$3
+        if [[ $ss_password || $password ]]; then
+            [[ $is_auto ]] && {
+                get_uuid && is_new_pass=$tmp_uuid
+                [[ $ss_password ]] && is_new_pass=$(get ss2022)
+            }
+        else
+            err "($is_config_file) 不支持更改密码."
+        fi
+        [[ ! $is_new_pass ]] && ask string is_new_pass "请输入新密码:"
+        password=$is_new_pass
+        ss_password=$is_new_pass
+        is_socks_pass=$is_new_pass
+        add $net
+        ;;
+    5)
+        # new uuid
+        is_new_uuid=$3
+        [[ ! $uuid ]] && err "($is_config_file) 不支持更改 UUID."
+        [[ $is_auto ]] && get_uuid && is_new_uuid=$tmp_uuid
+        [[ ! $is_new_uuid ]] && ask string is_new_uuid "请输入新 UUID:"
+        add $net auto $is_new_uuid
+        ;;
+    6)
+        # new method
+        is_new_method=$3
+        [[ $net != 'ss' ]] && err "($is_config_file) 不支持更改加密方式."
+        [[ $is_auto ]] && is_new_method=$is_random_ss_method
+        [[ ! $is_new_method ]] && {
+            ask set_ss_method
+            is_new_method=$ss_method
+        }
+        add $net auto auto $is_new_method
+        ;;
+    7)
+        # new remote addr
+        is_new_door_addr=$3
+        [[ $net != 'direct' ]] && err "($is_config_file) 不支持更改目标地址."
+        [[ ! $is_new_door_addr ]] && ask string is_new_door_addr "请输入新的目标地址:"
+        door_addr=$is_new_door_addr
+        add $net
+        ;;
+    8)
+        # new remote port
+        is_new_door_port=$3
+        [[ $net != 'direct' ]] && err "($is_config_file) 不支持更改目标端口."
+        [[ ! $is_new_door_port ]] && {
+            ask string door_port "请输入新的目标端口:"
+            is_new_door_port=$door_port
+        }
+        add $net auto auto $is_new_door_port
+        ;;
+    9)
+        # new is_private_key is_public_key
+        is_new_private_key=$3
+        is_new_public_key=$4
+        [[ ! $is_reality ]] && err "($is_config_file) 不支持更改密钥."
+        if [[ $is_auto ]]; then
+            get_pbk
+            add $net
+        else
+            [[ $is_new_private_key && ! $is_new_public_key ]] && {
+                err "无法找到 Public key."
+            }
+            [[ ! $is_new_private_key ]] && ask string is_new_private_key "请输入新 Private key:"
+            [[ ! $is_new_public_key ]] && ask string is_new_public_key "请输入新 Public key:"
+            if [[ $is_new_private_key == $is_new_public_key ]]; then
+                err "Private key 和 Public key 不能一样."
+            fi
+            is_tmp_json=$is_conf_dir/$is_config_file-$uuid
+            cp -f $is_conf_dir/$is_config_file $is_tmp_json
+            sed -i s#$is_private_key #$is_new_private_key# $is_tmp_json
+            $is_core_bin check -c $is_tmp_json &>/dev/null
+            if [[ $? != 0 ]]; then
+                is_key_err=1
+                is_key_err_msg="Private key 无法通过测试."
+            fi
+            sed -i s#$is_new_private_key #$is_new_public_key# $is_tmp_json
+            $is_core_bin check -c $is_tmp_json &>/dev/null
+            if [[ $? != 0 ]]; then
+                is_key_err=1
+                is_key_err_msg+="Public key 无法通过测试."
+            fi
+            rm $is_tmp_json
+            [[ $is_key_err ]] && err $is_key_err_msg
+            is_private_key=$is_new_private_key
+            is_public_key=$is_new_public_key
+            is_test_json=
+            add $net
+        fi
+        ;;
+    10)
+        # new serverName
+        is_new_servername=$3
+        [[ ! $is_reality ]] && err "($is_config_file) 不支持更改 serverName."
+        [[ $is_auto ]] && is_new_servername=$is_random_servername
+        [[ ! $is_new_servername ]] && ask string is_new_servername "请输入新的 serverName:"
+        is_servername=$is_new_servername
+        [[ $(grep -i "^233boy.com$" <<<$is_servername) ]] && {
+            err "你干嘛~哎呦~"
+        }
+        add $net
+        ;;
+    11)
+        # new proxy site
+        is_new_proxy_site=$3
+        [[ ! $is_caddy && ! $host ]] && {
+            err "($is_config_file) 不支持更改伪装网站."
+        }
+        [[ ! -f $is_caddy_conf/${host}.conf.add ]] && err "无法配置伪装网站."
+        [[ ! $is_new_proxy_site ]] && ask string is_new_proxy_site "请输入新的伪装网站 (例如 example.com):"
+        proxy_site=$(sed 's#^.*//##;s#/$##' <<<$is_new_proxy_site)
+        [[ $(grep -i "^233boy.com$" <<<$proxy_site) ]] && {
+            err "你干嘛~哎呦~"
+        } || {
+            load caddy.sh
+            caddy_config proxy
+            manage restart caddy &
+        }
+        msg "\n已更新伪装网站为: $(_green $proxy_site) \n"
+        ;;
+    12)
+        # new socks user
+        [[ ! $is_socks_user ]] && err "($is_config_file) 不支持更改用户名 (Username)."
+        ask string is_socks_user "请输入新用户名 (Username):"
+        add $net
+        ;;
+    esac
+}
+
+# delete config.
+del() {
+    # dont get ip
+    is_dont_get_ip=1
+    [[ $is_conf_dir_empty ]] && return # not found any json file.
+    # get a config file
+    [[ ! $is_config_file ]] && get info $1
+    if [[ $is_config_file ]]; then
+        if [[ $is_main_start && ! $is_no_del_msg ]]; then
+            msg "\n是否删除配置文件?: $is_config_file"
+            pause
+        fi
+        rm -rf $is_conf_dir/"$is_config_file"
+        [[ ! $is_new_json ]] && manage restart &
+        [[ ! $is_no_del_msg ]] && _green "\n已删除: $is_config_file\n"
+
+        [[ $is_caddy ]] && {
+            is_del_host=$host
+            [[ $is_change ]] && {
+                [[ ! $old_host ]] && return # no host exist or not set new host;
+                is_del_host=$old_host
+            }
+            [[ $is_del_host && $host != $old_host && -f $is_caddy_conf/$is_del_host.conf ]] && {
+                rm -rf $is_caddy_conf/$is_del_host.conf $is_caddy_conf/$is_del_host.conf.add
+                [[ ! $is_new_json ]] && manage restart caddy &
+            }
+        }
+    fi
+    if [[ ! $(ls $is_conf_dir | grep .json) && ! $is_change ]]; then
+        warn "当前配置目录为空! 因为你刚刚删除了最后一个配置文件."
+        is_conf_dir_empty=1
+    fi
+    unset is_dont_get_ip
+    [[ $is_dont_auto_exit ]] && unset is_config_file
+}
+
+# uninstall
+uninstall() {
+    if [[ $is_caddy ]]; then
+        is_tmp_list=("卸载 $is_core_name" "卸载 ${is_core_name} & Caddy")
+        ask list is_do_uninstall
+    else
+        ask string y "是否卸载 ${is_core_name}? [y]:"
+    fi
+    manage stop &>/dev/null
+    manage disable &>/dev/null
+    rm -rf $is_core_dir $is_log_dir $is_sh_bin ${is_sh_bin/$is_core/sb} /lib/systemd/system/$is_core.service
+    sed -i "/alias $is_core=/d" /root/.bashrc
+    # uninstall caddy; 2 is ask result
+    if [[ $REPLY == '2' ]]; then
+        manage stop caddy &>/dev/null
+        manage disable caddy &>/dev/null
+        rm -rf $is_caddy_dir $is_caddy_bin /lib/systemd/system/caddy.service
+    fi
+    [[ $is_install_sh ]] && return # reinstall
+    _green "\n卸载完成!"
+    msg "脚本哪里需要完善? 请反馈"
+    msg "反馈问题) $(msg_ul https://github.com/${is_sh_repo}/issues)\n"
+}
+
+# manage run status
+manage() {
+    [[ $is_dont_auto_exit ]] && return
+    case $1 in
+    1 | start)
+        is_do=start
+        is_do_msg=启动
+        is_test_run=1
+        ;;
+    2 | stop)
+        is_do=stop
+        is_do_msg=停止
+        ;;
+    3 | r | restart)
+        is_do=restart
+        is_do_msg=重启
+        is_test_run=1
+        ;;
+    *)
+        is_do=$1
+        is_do_msg=$1
+        ;;
+    esac
+    case $2 in
+    caddy)
+        is_do_name=$2
+        is_run_bin=$is_caddy_bin
+        is_do_name_msg=Caddy
+        ;;
+    *)
+        is_do_name=$is_core
+        is_run_bin=$is_core_bin
+        is_do_name_msg=$is_core_name
+        ;;
+    esac
+    systemctl $is_do $is_do_name
+    [[ $is_test_run && ! $is_new_install ]] && {
+        sleep 2
+        if [[ ! $(pgrep -f $is_run_bin) ]]; then
+            is_run_fail=${is_do_name_msg,,}
+            [[ ! $is_no_manage_msg ]] && {
+                msg
+                warn "($is_do_msg) $is_do_name_msg 失败"
+                _yellow "检测到运行失败, 自动执行测试运行."
+                get test-run
+                _yellow "测试结束, 请按 Enter 退出."
+            }
+        fi
+    }
+}
+
+# add a config
+add() {
+    is_lower=${1,,}
+    if [[ $is_lower ]]; then
+        case $is_lower in
+        ws | tcp | quic | http)
+            is_new_protocol=VMess-${is_lower^^}
+            ;;
+        wss | h2 | hu | vws | vh2 | vhu | tws | th2 | thu)
+            is_new_protocol=$(sed -E "s/^V/VLESS-/;s/^T/Trojan-/;/^(W|H)/{s/^/VMess-/};s/WSS/WS/;s/HU/HTTPUpgrade/" <<<${is_lower^^})-TLS
+            ;;
+        r | reality)
+            is_new_protocol=VLESS-REALITY
+            ;;
+        rh2)
+            is_new_protocol=VLESS-HTTP2-REALITY
+            ;;
+        ss)
+            is_new_protocol=Shadowsocks
+            ;;
+        door | direct)
+            is_new_protocol=Direct
+            ;;
+        tuic)
+            is_new_protocol=TUIC
+            ;;
+        hy | hy2 | hysteria*)
+            is_new_protocol=Hysteria2
+            ;;
+        trojan)
+            is_new_protocol=Trojan
+            ;;
+        socks)
+            is_new_protocol=Socks
+            ;;
+        *)
+            for v in ${protocol_list[@]}; do
+                [[ $(grep -E -i "^$is_lower$" <<<$v) ]] && is_new_protocol=$v && break
+            done
+
+            [[ ! $is_new_protocol ]] && err "无法识别 ($1), 请使用: $is_core add [protocol] [args... | auto]"
+            ;;
+        esac
+    fi
+
+    # no prefer protocol
+    [[ ! $is_new_protocol ]] && ask set_protocol
+
+    case ${is_new_protocol,,} in
+    *-tls)
+        is_use_tls=1
+        is_use_host=$2
+        is_use_uuid=$3
+        is_use_path=$4
+        is_add_opts="[host] [uuid] [/path]"
+        ;;
+    vmess* | tuic*)
+        is_use_port=$2
+        is_use_uuid=$3
+        is_add_opts="[port] [uuid]"
+        ;;
+    trojan* | hysteria*)
+        is_use_port=$2
+        is_use_pass=$3
+        is_add_opts="[port] [password]"
+        ;;
+    *reality*)
+        is_reality=1
+        is_use_port=$2
+        is_use_uuid=$3
+        is_use_servername=$4
+        is_add_opts="[port] [uuid] [sni]"
+        ;;
+    shadowsocks)
+        is_use_port=$2
+        is_use_pass=$3
+        is_use_method=$4
+        is_add_opts="[port] [password] [method]"
+        ;;
+    direct)
+        is_use_port=$2
+        is_use_door_addr=$3
+        is_use_door_port=$4
+        is_add_opts="[port] [remote_addr] [remote_port]"
+        ;;
+    socks)
+        is_socks=1
+        is_use_port=$2
+        is_use_socks_user=$3
+        is_use_socks_pass=$4
+        is_add_opts="[port] [username] [password]"
+        ;;
+    esac
+
+    [[ $1 && ! $is_change ]] && {
+        msg "\n使用协议: $is_new_protocol"
+        # err msg tips
+        is_err_tips="\n\n请使用: $(_green $is_core add $1 $is_add_opts) 来添加 $is_new_protocol 配置"
+    }
+
+    # remove old protocol args
+    if [[ $is_set_new_protocol ]]; then
+        case $is_old_net in
+        h2 | ws | httpupgrade)
+            old_host=$host
+            [[ ! $is_use_tls ]] && unset host is_no_auto_tls
+            ;;
+        reality)
+            net_type=
+            [[ ! $(grep -i reality <<<$is_new_protocol) ]] && is_reality=
+            ;;
+        ss)
+            [[ $(is_test uuid $ss_password) ]] && uuid=$ss_password
+            ;;
+        esac
+        [[ ! $(is_test uuid $uuid) ]] && uuid=
+        [[ $(is_test uuid $password) ]] && uuid=$password
+    fi
+
+    # no-auto-tls only use h2,ws,grpc
+    if [[ $is_no_auto_tls && ! $is_use_tls ]]; then
+        err "$is_new_protocol 不支持手动配置 tls."
+    fi
+
+    # prefer args.
+    if [[ $2 ]]; then
+        for v in is_use_port is_use_uuid is_use_host is_use_path is_use_pass is_use_method is_use_door_addr is_use_door_port; do
+            [[ ${!v} == 'auto' ]] && unset $v
+        done
+
+        if [[ $is_use_port ]]; then
+            [[ ! $(is_test port ${is_use_port}) ]] && {
+                err "($is_use_port) 不是一个有效的端口. $is_err_tips"
+            }
+            [[ $(is_test port_used $is_use_port) && ! $is_gen ]] && {
+                err "无法使用 ($is_use_port) 端口. $is_err_tips"
+            }
+            port=$is_use_port
+        fi
+        if [[ $is_use_door_port ]]; then
+            [[ ! $(is_test port ${is_use_door_port}) ]] && {
+                err "(${is_use_door_port}) 不是一个有效的目标端口. $is_err_tips"
+            }
+            door_port=$is_use_door_port
+        fi
+        if [[ $is_use_uuid ]]; then
+            [[ ! $(is_test uuid $is_use_uuid) ]] && {
+                err "($is_use_uuid) 不是一个有效的 UUID. $is_err_tips"
+            }
+            uuid=$is_use_uuid
+        fi
+        if [[ $is_use_path ]]; then
+            [[ ! $(is_test path $is_use_path) ]] && {
+                err "($is_use_path) 不是有效的路径. $is_err_tips"
+            }
+            path=$is_use_path
+        fi
+        if [[ $is_use_method ]]; then
+            is_tmp_use_name=加密方式
+            is_tmp_list=${ss_method_list[@]}
+            for v in ${is_tmp_list[@]}; do
+                [[ $(grep -E -i "^${is_use_method}$" <<<$v) ]] && is_tmp_use_type=$v && break
+            done
+            [[ ! ${is_tmp_use_type} ]] && {
+                warn "(${is_use_method}) 不是一个可用的${is_tmp_use_name}."
+                msg "${is_tmp_use_name}可用如下: "
+                for v in ${is_tmp_list[@]}; do
+                    msg "\t\t$v"
+                done
+                msg "$is_err_tips\n"
+                exit 1
+            }
+            ss_method=$is_tmp_use_type
+        fi
+        [[ $is_use_pass ]] && ss_password=$is_use_pass && password=$is_use_pass
+        [[ $is_use_host ]] && host=$is_use_host
+        [[ $is_use_door_addr ]] && door_addr=$is_use_door_addr
+        [[ $is_use_servername ]] && is_servername=$is_use_servername
+        [[ $is_use_socks_user ]] && is_socks_user=$is_use_socks_user
+        [[ $is_use_socks_pass ]] && is_socks_pass=$is_use_socks_pass
+    fi
+
+    if [[ $is_use_tls ]]; then
+        if [[ ! $is_no_auto_tls && ! $is_caddy && ! $is_gen && ! $is_dont_test_host ]]; then
+            # test auto tls
+            [[ $(is_test port_used 80) || $(is_test port_used 443) ]] && {
+                get_port
+                is_http_port=$tmp_port
+                get_port
+                is_https_port=$tmp_port
+                warn "端口 (80 或 443) 已经被占用, 你也可以考虑使用 no-auto-tls"
+                msg "\e[41m no-auto-tls 帮助(help)\e[0m: $(msg_ul https://233boy.com/$is_core/no-auto-tls/)\n"
+                msg "\n Caddy 将使用非标准端口实现自动配置 TLS, HTTP:$is_http_port HTTPS:$is_https_port\n"
+                msg "请确定是否继续???"
+                pause
+            }
+            is_install_caddy=1
+        fi
+        # set host
+        [[ ! $host ]] && ask string host "请输入域名:"
+        # test host dns
+        get host-test
+    else
+        # for main menu start, dont auto create args
+        if [[ $is_main_start ]]; then
+
+            # set port
+            [[ ! $port ]] && ask string port "请输入端口:"
+
+            case ${is_new_protocol,,} in
+            socks)
+                # set user
+                [[ ! $is_socks_user ]] && ask string is_socks_user "请设置用户名:"
+                # set password
+                [[ ! $is_socks_pass ]] && ask string is_socks_pass "请设置密码:"
+                ;;
+            shadowsocks)
+                # set method
+                [[ ! $ss_method ]] && ask set_ss_method
+                # set password
+                [[ ! $ss_password ]] && ask string ss_password "请设置密码:"
+                ;;
+            esac
+
+        fi
+    fi
+
+    # Dokodemo-Door
+    if [[ $is_new_protocol == 'Direct' ]]; then
+        # set remote addr
+        [[ ! $door_addr ]] && ask string door_addr "请输入目标地址:"
+        # set remote port
+        [[ ! $door_port ]] && ask string door_port "请输入目标端口:"
+    fi
+
+    # Shadowsocks 2022
+    if [[ $(grep 2022 <<<$ss_method) ]]; then
+        # test ss2022 password
+        [[ $ss_password ]] && {
+            is_test_json=1
+            create server Shadowsocks
+            [[ ! $tmp_uuid ]] && get_uuid
+            is_test_json_save=$is_conf_dir/tmp-test-$tmp_uuid
+            cat <<<"$is_new_json" >$is_test_json_save
+            $is_core_bin check -c $is_test_json_save &>/dev/null
+            if [[ $? != 0 ]]; then
+                warn "Shadowsocks 协议 ($ss_method) 不支持使用密码 ($(_red_bg $ss_password))\n\n你可以使用命令: $(_green $is_core ss2022) 生成支持的密码.\n\n脚本将自动创建可用密码:)"
+                ss_password=
+                # create new json.
+                json_str=
+            fi
+            is_test_json=
+            rm -f $is_test_json_save
+        }
+
+    fi
+
+    # install caddy
+    if [[ $is_install_caddy ]]; then
+        get install-caddy
+    fi
+
+    # create json
+    create server $is_new_protocol
+
+    # show config info.
+    info
+}
+
+# get config info
+# or somes required args
+get() {
+    case $1 in
+    addr)
+        is_addr=$host
+        [[ ! $is_addr ]] && {
+            get_ip
+            is_addr=$ip
+            [[ $(grep ":" <<<$ip) ]] && is_addr="[$ip]"
+        }
+        ;;
+    new)
+        [[ ! $host ]] && get_ip
+        [[ ! $port ]] && get_port && port=$tmp_port
+        [[ ! $uuid ]] && get_uuid && uuid=$tmp_uuid
+        ;;
+    file)
+        is_file_str=$2
+        [[ ! $is_file_str ]] && is_file_str='.json$'
+        # is_all_json=("$(ls $is_conf_dir | grep -E $is_file_str)")
+        readarray -t is_all_json <<<"$(ls $is_conf_dir | grep -E -i "$is_file_str" | sed '/dynamic-port-.*-link/d' | head -233)" # limit max 233 lines for show.
+        [[ ! $is_all_json ]] && err "无法找到相关的配置文件: $2"
+        [[ ${#is_all_json[@]} -eq 1 ]] && is_config_file=$is_all_json && is_auto_get_config=1
+        [[ ! $is_config_file ]] && {
+            [[ $is_dont_auto_exit ]] && return
+            ask get_config_file
+        }
+        ;;
+    info)
+        get file $2
+        if [[ $is_config_file ]]; then
+            is_json_str=$(cat $is_conf_dir/"$is_config_file" | sed s#//.*##)
+            is_json_data=$(jq '(.inbounds[0]|.type,.listen_port,(.users[0]|.uuid,.password,.username),.method,.password,.override_port,.override_address,(.transport|.type,.path,.headers.host),(.tls|.server_name,.reality.private_key)),(.outbounds[1].tag)' <<<$is_json_str)
+            [[ $? != 0 ]] && err "无法读取此文件: $is_config_file"
+            is_up_var_set=(null is_protocol port uuid password username ss_method ss_password door_port door_addr net_type path host is_servername is_private_key is_public_key)
+            [[ $is_debug ]] && msg "\n------------- debug: $is_config_file -------------"
+            i=0
+            for v in $(sed 's/""/null/g;s/"//g' <<<"$is_json_data"); do
+                ((i++))
+                [[ $is_debug ]] && msg "$i-${is_up_var_set[$i]}: $v"
+                export ${is_up_var_set[$i]}="${v}"
+            done
+            for v in ${is_up_var_set[@]}; do
+                [[ ${!v} == 'null' ]] && unset $v
+            done
+
+            if [[ $is_private_key ]]; then
+                is_reality=1
+                net_type+=reality
+                is_public_key=${is_public_key/public_key_/}
+            fi
+            is_socks_user=$username
+            is_socks_pass=$password
+
+            is_config_name=$is_config_file
+
+            if [[ $is_caddy && $host && -f $is_caddy_conf/$host.conf ]]; then
+                is_tmp_https_port=$(grep -E -o "$host:[1-9][0-9]?+" $is_caddy_conf/$host.conf | sed s/.*://)
+            fi
+            if [[ $host && ! -f $is_caddy_conf/$host.conf ]]; then
+                is_no_auto_tls=1
+            fi
+            [[ $is_tmp_https_port ]] && is_https_port=$is_tmp_https_port
+            [[ $is_client && $host ]] && port=$is_https_port
+            get protocol $is_protocol-$net_type
+        fi
+        ;;
+    protocol)
+        get addr # get host or server ip
+        is_lower=${2,,}
+        net=
+        is_users="users:[{uuid:\"$uuid\"}]"
+        is_tls_json='tls:{enabled:true,alpn:["h3"],key_path:"'$is_tls_key'",certificate_path:"'$is_tls_cer'"}'
+        case $is_lower in
+        vmess*)
+            is_protocol=vmess
+            [[ $is_lower =~ "tcp" || ! $net_type && $is_up_var_set ]] && net=tcp && json_str=$is_users
+            ;;
+        vless*)
+            is_protocol=vless
+            ;;
+        tuic*)
+            net=tuic
+            is_protocol=$net
+            [[ ! $password ]] && password=$uuid
+            is_users="users:[{uuid:\"$uuid\",password:\"$password\"}]"
+            json_str="$is_users,congestion_control:\"bbr\",$is_tls_json"
+            ;;
+        trojan*)
+            is_protocol=trojan
+            [[ ! $password ]] && password=$uuid
+            is_users="users:[{password:\"$password\"}]"
+            [[ ! $host ]] && {
+                net=trojan
+                json_str="$is_users,${is_tls_json/alpn\:\[\"h3\"\],/}"
+            }
+            ;;
+        hysteria2*)
+            net=hysteria2
+            is_protocol=$net
+            [[ ! $password ]] && password=$uuid
+            json_str="users:[{password:\"$password\"}],$is_tls_json"
+            ;;
+        shadowsocks*)
+            net=ss
+            is_protocol=shadowsocks
+            [[ ! $ss_method ]] && ss_method=$is_random_ss_method
+            [[ ! $ss_password ]] && {
+                ss_password=$uuid
+                [[ $(grep 2022 <<<$ss_method) ]] && ss_password=$(get ss2022)
+            }
+            json_str="method:\"$ss_method\",password:\"$ss_password\""
+            ;;
+        direct*)
+            net=direct
+            is_protocol=$net
+            json_str="override_port:$door_port,override_address:\"$door_addr\""
+            ;;
+        socks*)
+            net=socks
+            is_protocol=$net
+            [[ ! $is_socks_user ]] && is_socks_user=233boy
+            [[ ! $is_socks_pass ]] && is_socks_pass=$uuid
+            json_str="users:[{username: \"$is_socks_user\", password: \"$is_socks_pass\"}]"
+            ;;
+        *)
+            err "无法识别协议: $is_config_file"
+            ;;
+        esac
+        [[ $net ]] && return # if net exist, dont need more json args
+        [[ $host && $is_lower =~ "tls" ]] && {
+            [[ ! $path ]] && path="/$uuid"
+            is_path_host_json=",path:\"$path\",headers:{host:\"$host\"}"
+        }
+        case $is_lower in
+        *quic*)
+            net=quic
+            is_json_add="$is_tls_json,transport:{type:\"$net\"}"
+            ;;
+        *ws*)
+            net=ws
+            is_json_add="transport:{type:\"$net\"$is_path_host_json,early_data_header_name:\"Sec-WebSocket-Protocol\"}"
+            ;;
+        *reality*)
+            net=reality
+            [[ ! $is_servername ]] && is_servername=$is_random_servername
+            [[ ! $is_private_key ]] && get_pbk
+            is_json_add="tls:{enabled:true,server_name:\"$is_servername\",reality:{enabled:true,handshake:{server:\"$is_servername\",server_port:443},private_key:\"$is_private_key\",short_id:[\"\"]}}"
+            [[ $is_lower =~ "http" ]] && {
+                is_json_add="$is_json_add,transport:{type:\"http\"}"
+            } || {
+                is_users=${is_users/uuid/flow:\"xtls-rprx-vision\",uuid}
+            }
+            ;;
+        *http* | *h2*)
+            net=http
+            [[ $is_lower =~ "up" ]] && net=httpupgrade
+            is_json_add="transport:{type:\"$net\"$is_path_host_json}"
+            [[ $is_lower =~ "h2" || ! $is_lower =~ "httpupgrade" && $host ]] && {
+                net=h2
+                is_json_add="${is_tls_json/alpn\:\[\"h3\"\],/},$is_json_add"
+            }
+            ;;
+        *)
+            err "无法识别传输协议: $is_config_file"
+            ;;
+        esac
+        json_str="$is_users,$is_json_add"
+        ;;
+    host-test) # test host dns record; for auto *tls required.
+        [[ $is_no_auto_tls || $is_gen || $is_dont_test_host ]] && return
+        get_ip
+        get ping
+        if [[ ! $(grep $ip <<<$is_host_dns) ]]; then
+            msg "\n请将 ($(_red_bg $host)) 解析到 ($(_red_bg $ip))"
+            msg "\n如果使用 Cloudflare, 在 DNS 那; 关闭 (Proxy status / 代理状态), 即是 (DNS only / 仅限 DNS)"
+            ask string y "我已经确定解析 [y]:"
+            get ping
+            if [[ ! $(grep $ip <<<$is_host_dns) ]]; then
+                _cyan "\n测试结果: $is_host_dns"
+                err "域名 ($host) 没有解析到 ($ip)"
+            fi
+        fi
+        ;;
+    ssss | ss2022)
+        if [[ $(grep 128 <<<$ss_method) ]]; then
+            $is_core_bin generate rand 16 --base64
+        else
+            $is_core_bin generate rand 32 --base64
+        fi
+        ;;
+    ping)
+        # is_ip_type="-4"
+        # [[ $(grep ":" <<<$ip) ]] && is_ip_type="-6"
+        # is_host_dns=$(ping $host $is_ip_type -c 1 -W 2 | head -1)
+        is_dns_type="a"
+        [[ $(grep ":" <<<$ip) ]] && is_dns_type="aaaa"
+        is_host_dns=$(_wget -qO- --header="accept: application/dns-json" "https://one.one.one.one/dns-query?name=$host&type=$is_dns_type")
+        ;;
+    install-caddy)
+        _green "\n安装 Caddy 实现自动配置 TLS.\n"
+        load download.sh
+        download caddy
+        load systemd.sh
+        install_service caddy &>/dev/null
+        is_caddy=1
+        _green "安装 Caddy 成功.\n"
+        ;;
+    reinstall)
+        is_install_sh=$(cat $is_sh_dir/install.sh)
+        uninstall
+        bash <<<$is_install_sh
+        ;;
+    test-run)
+        systemctl list-units --full -all &>/dev/null
+        [[ $? != 0 ]] && {
+            _yellow "\n无法执行测试, 请检查 systemctl 状态.\n"
+            return
+        }
+        is_no_manage_msg=1
+        if [[ ! $(pgrep -f $is_core_bin) ]]; then
+            _yellow "\n测试运行 $is_core_name ..\n"
+            manage start &>/dev/null
+            if [[ $is_run_fail == $is_core ]]; then
+                _red "$is_core_name 运行失败信息:"
+                $is_core_bin run -c $is_config_json -C $is_conf_dir
+            else
+                _green "\n测试通过, 已启动 $is_core_name ..\n"
+            fi
+        else
+            _green "\n$is_core_name 正在运行, 跳过测试\n"
+        fi
+        if [[ $is_caddy ]]; then
+            if [[ ! $(pgrep -f $is_caddy_bin) ]]; then
+                _yellow "\n测试运行 Caddy ..\n"
+                manage start caddy &>/dev/null
+                if [[ $is_run_fail == 'caddy' ]]; then
+                    _red "Caddy 运行失败信息:"
+                    $is_caddy_bin run --config $is_caddyfile
+                else
+                    _green "\n测试通过, 已启动 Caddy ..\n"
+                fi
+            else
+                _green "\nCaddy 正在运行, 跳过测试\n"
+            fi
+        fi
+        ;;
+    esac
+}
+
+# show info
+info() {
+    if [[ ! $is_protocol ]]; then
+        get info $1
+    fi
+    # is_color=$(shuf -i 41-45 -n1)
+    is_color=44
+    case $net in
+    ws | tcp | h2 | quic | http*)
+        if [[ $host ]]; then
+            is_color=45
+            is_can_change=(0 1 2 3 5)
+            is_info_show=(0 1 2 3 4 6 7 8)
+            [[ $is_protocol == 'vmess' ]] && {
+                is_vmess_url=$(jq -c '{v:2,ps:'\"233boy-$net-$host\"',add:'\"$is_addr\"',port:'\"$is_https_port\"',id:'\"$uuid\"',aid:"0",net:'\"$net\"',host:'\"$host\"',path:'\"$path\"',tls:'\"tls\"'}' <<<{})
+                is_url=vmess://$(echo -n $is_vmess_url | base64 -w 0)
+            } || {
+                [[ $is_protocol == "trojan" ]] && {
+                    uuid=$password
+                    # is_info_str=($is_protocol $is_addr $is_https_port $password $net $host $path 'tls')
+                    is_can_change=(0 1 2 3 4)
+                    is_info_show=(0 1 2 10 4 6 7 8)
+                }
+                is_url="$is_protocol://$uuid@$host:$is_https_port?encryption=none&security=tls&type=$net&host=$host&path=$path#233boy-$net-$host"
+            }
+            [[ $is_caddy ]] && is_can_change+=(11)
+            is_info_str=($is_protocol $is_addr $is_https_port $uuid $net $host $path 'tls')
+        else
+            is_type=none
+            is_can_change=(0 1 5)
+            is_info_show=(0 1 2 3 4)
+            is_info_str=($is_protocol $is_addr $port $uuid $net)
+            [[ $net == "http" ]] && {
+                net=tcp
+                is_type=http
+                is_tcp_http=1
+                is_info_show+=(5)
+                is_info_str=(${is_info_str[@]/http/tcp http})
+            }
+            [[ $net == "quic" ]] && {
+                is_insecure=1
+                is_info_show+=(8 9 20)
+                is_info_str+=(tls h3 true)
+                is_quic_add=",tls:\"tls\",alpn:\"h3\"" # cant add allowInsecure
+            }
+            is_vmess_url=$(jq -c "{v:2,ps:\"233boy-${net}-$is_addr\",add:\"$is_addr\",port:\"$port\",id:\"$uuid\",aid:\"0\",net:\"$net\",type:\"$is_type\"$is_quic_add}" <<<{})
+            is_url=vmess://$(echo -n $is_vmess_url | base64 -w 0)
+        fi
+        ;;
+    ss)
+        is_can_change=(0 1 4 6)
+        is_info_show=(0 1 2 10 11)
+        is_url="ss://$(echo -n ${ss_method}:${ss_password} | base64 -w 0)@${is_addr}:${port}#233boy-$net-${is_addr}"
+        is_info_str=($is_protocol $is_addr $port $ss_password $ss_method)
+        ;;
+    trojan)
+        is_insecure=1
+        is_can_change=(0 1 4)
+        is_info_show=(0 1 2 10 4 8 20)
+        is_url="$is_protocol://$password@$is_addr:$port?type=tcp&security=tls&allowInsecure=1#233boy-$net-$is_addr"
+        is_info_str=($is_protocol $is_addr $port $password tcp tls true)
+        ;;
+    hy*)
+        is_can_change=(0 1 4)
+        is_info_show=(0 1 2 10 8 9 20)
+        is_url="$is_protocol://$password@$is_addr:$port?alpn=h3&insecure=1#233boy-$net-$is_addr"
+        is_info_str=($is_protocol $is_addr $port $password tls h3 true)
+        ;;
+    tuic)
+        is_insecure=1
+        is_can_change=(0 1 4 5)
+        is_info_show=(0 1 2 3 10 8 9 20 21)
+        is_url="$is_protocol://$uuid:$password@$is_addr:$port?alpn=h3&allow_insecure=1&congestion_control=bbr#233boy-$net-$is_addr"
+        is_info_str=($is_protocol $is_addr $port $uuid $password tls h3 true bbr)
+        ;;
+    reality)
+        is_color=41
+        is_can_change=(0 1 5 9 10)
+        is_info_show=(0 1 2 3 15 4 8 16 17 18)
+        is_flow=xtls-rprx-vision
+        is_net_type=tcp
+        [[ $net_type =~ "http" || ${is_new_protocol,,} =~ "http" ]] && {
+            is_flow=
+            is_net_type=h2
+            is_info_show=(${is_info_show[@]/15/})
+        }
+        is_info_str=($is_protocol $is_addr $port $uuid $is_flow $is_net_type reality $is_servername chrome $is_public_key)
+        is_url="$is_protocol://$uuid@$is_addr:$port?encryption=none&security=reality&flow=$is_flow&type=$is_net_type&sni=$is_servername&pbk=$is_public_key&fp=chrome#233boy-$net-$is_addr"
+        ;;
+    direct)
+        is_can_change=(0 1 7 8)
+        is_info_show=(0 1 2 13 14)
+        is_info_str=($is_protocol $is_addr $port $door_addr $door_port)
+        ;;
+    socks)
+        is_can_change=(0 1 12 4)
+        is_info_show=(0 1 2 19 10)
+        is_info_str=($is_protocol $is_addr $port $is_socks_user $is_socks_pass)
+        is_url="socks://$(echo -n ${is_socks_user}:${is_socks_pass} | base64 -w 0)@${is_addr}:${port}#233boy-$net-${is_addr}"
+        ;;
+    esac
+    [[ $is_dont_show_info || $is_gen || $is_dont_auto_exit ]] && return # dont show info
+    msg "-------------- $is_config_name -------------"
+    for ((i = 0; i < ${#is_info_show[@]}; i++)); do
+        a=${info_list[${is_info_show[$i]}]}
+        if [[ ${#a} -eq 11 || ${#a} -ge 13 ]]; then
+            tt='\t'
+        else
+            tt='\t\t'
+        fi
+        msg "$a $tt= \e[${is_color}m${is_info_str[$i]}\e[0m"
+    done
+    if [[ $is_new_install ]]; then
+        warn "首次安装请查看脚本帮助文档: $(msg_ul https://233boy.com/$is_core/$is_core-script/)"
+    fi
+    if [[ $is_url ]]; then
+        msg "------------- ${info_list[12]} -------------"
+        msg "\e[4;${is_color}m${is_url}\e[0m"
+        [[ $is_insecure ]] && {
+            warn "某些客户端如(V2rayN 等)导入URL需手动将: 跳过证书验证(allowInsecure) 设置为 true, 或打开: 允许不安全的连接"
+        }
+    fi
+    if [[ $is_no_auto_tls ]]; then
+        msg "------------- no-auto-tls INFO -------------"
+        msg "端口(port): $port"
+        msg "路径(path): $path"
+        msg "\e[41m帮助(help)\e[0m: $(msg_ul https://233boy.com/$is_core/no-auto-tls/)"
+    fi
+    footer_msg
+}
+
+# footer msg
+footer_msg() {
+    [[ $is_core_stop && ! $is_new_json ]] && warn "$is_core_name 当前处于停止状态."
+    [[ $is_caddy_stop && $host ]] && warn "Caddy 当前处于停止状态."
+    ####### 要点13脸吗只会改我链接的小人 #######
+    unset c n m s b
+    msg "------------- END -------------"
+    msg "关注(tg): $(msg_ul https://t.me/tg2333)"
+    msg "文档(doc): $(msg_ul https://233boy.com/$is_core/$is_core-script/)"
+    msg "推广(ads): 机场推荐($is_core_name services): $(msg_ul https://g${c}e${n}t${m}j${s}m${b}s.com/)\n"
+    ####### 要点13脸吗只会改我链接的小人 #######
+}
+
+# URL or qrcode
+url_qr() {
+    is_dont_show_info=1
+    info $2
+    if [[ $is_url ]]; then
+        [[ $1 == 'url' ]] && {
+            msg "\n------------- $is_config_name & URL 链接 -------------"
+            msg "\n\e[${is_color}m${is_url}\e[0m\n"
+            footer_msg
+        } || {
+            link="https://233boy.github.io/tools/qr.html#${is_url}"
+            msg "\n------------- $is_config_name & QR code 二维码 -------------"
+            msg
+            if [[ $(type -P qrencode) ]]; then
+                qrencode -t ANSI "${is_url}"
+            else
+                msg "请安装 qrencode: $(_green "$cmd update -y; $cmd install qrencode -y")"
+            fi
+            msg
+            msg "如果无法正常显示或识别, 请使用下面的链接来生成二维码:"
+            msg "\n\e[4;${is_color}m${link}\e[0m\n"
+            footer_msg
+        }
+    else
+        [[ $1 == 'url' ]] && {
+            err "($is_config_name) 无法生成 URL 链接."
+        } || {
+            err "($is_config_name) 无法生成 QR code 二维码."
+        }
+    fi
+}
+
+# update core, sh, caddy
+update() {
+    case $1 in
+    1 | core | $is_core)
+        is_update_name=core
+        is_show_name=$is_core_name
+        is_run_ver=v${is_core_ver##* }
+        is_update_repo=$is_core_repo
+        ;;
+    2 | sh)
+        is_update_name=sh
+        is_show_name="$is_core_name 脚本"
+        is_run_ver=$is_sh_ver
+        is_update_repo=$is_sh_repo
+        ;;
+    3 | caddy)
+        [[ ! $is_caddy ]] && err "不支持更新 Caddy."
+        is_update_name=caddy
+        is_show_name="Caddy"
+        is_run_ver=$is_caddy_ver
+        is_update_repo=$is_caddy_repo
+        ;;
+    *)
+        err "无法识别 ($1), 请使用: $is_core update [core | sh | caddy] [ver]"
+        ;;
+    esac
+    [[ $2 ]] && is_new_ver=v${2#v}
+    [[ $is_run_ver == $is_new_ver ]] && {
+        msg "\n自定义版本和当前 $is_show_name 版本一样, 无需更新.\n"
+        exit
+    }
+    load download.sh
+    if [[ $is_new_ver ]]; then
+        msg "\n使用自定义版本更新 $is_show_name: $(_green $is_new_ver)\n"
+    else
+        get_latest_version $is_update_name
+        [[ $is_run_ver == $latest_ver ]] && {
+            msg "\n$is_show_name 当前已经是最新版本了.\n"
+            exit
+        }
+        msg "\n发现 $is_show_name 新版本: $(_green $latest_ver)\n"
+        is_new_ver=$latest_ver
+    fi
+    download $is_update_name $is_new_ver
+    msg "更新成功, 当前 $is_show_name 版本: $(_green $is_new_ver)\n"
+    msg "$(_green 请查看更新说明: https://github.com/$is_update_repo/releases/tag/$is_new_ver)\n"
+    [[ $is_update_name != 'sh' ]] && manage restart $is_update_name &
+}
+
+# main menu; if no prefer args.
+is_main_menu() {
+    msg "\n------------- $is_core_name script $is_sh_ver by $author -------------"
+    msg "$is_core_name $is_core_ver: $is_core_status"
+    msg "群组(Chat): $(msg_ul https://t.me/tg233boy)"
+    is_main_start=1
+    ask mainmenu
+    case $REPLY in
+    1)
+        add
+        ;;
+    2)
+        change
+        ;;
+    3)
+        info
+        ;;
+    4)
+        del
+        ;;
+    5)
+        ask list is_do_manage "启动 停止 重启"
+        manage $REPLY &
+        msg "\n管理状态执行: $(_green $is_do_manage)\n"
+        ;;
+    6)
+        is_tmp_list=("更新$is_core_name" "更新脚本")
+        [[ $is_caddy ]] && is_tmp_list+=("更新Caddy")
+        ask list is_do_update null "\n请选择更新:\n"
+        update $REPLY
+        ;;
+    7)
+        uninstall
+        ;;
+    8)
+        msg
+        load help.sh
+        show_help
+        ;;
+    9)
+        ask list is_do_other "启用BBR 查看日志 测试运行 重装脚本 设置DNS"
+        case $REPLY in
+        1)
+            load bbr.sh
+            _try_enable_bbr
+            ;;
+        2)
+            load log.sh
+            log_set
+            ;;
+        3)
+            get test-run
+            ;;
+        4)
+            get reinstall
+            ;;
+        5)
+            load dns.sh
+            dns_set
+            ;;
+        esac
+        ;;
+    10)
+        load help.sh
+        about
+        ;;
+    esac
+}
+
+# check prefer args, if not exist prefer args and show main menu
+main() {
+    case $1 in
+    a | add | gen | no-auto-tls)
+        [[ $1 == 'gen' ]] && is_gen=1
+        [[ $1 == 'no-auto-tls' ]] && is_no_auto_tls=1
+        add ${@:2}
+        ;;
+    bin | pbk | check | completion | format | generate | geoip | geosite | merge | rule-set | run | tools)
+        is_run_command=$1
+        if [[ $1 == 'bin' ]]; then
+            $is_core_bin ${@:2}
+        else
+            [[ $is_run_command == 'pbk' ]] && is_run_command="generate reality-keypair"
+            $is_core_bin $is_run_command ${@:2}
+        fi
+        ;;
+    bbr)
+        load bbr.sh
+        _try_enable_bbr
+        ;;
+    c | config | change)
+        change ${@:2}
+        ;;
+    # client | genc)
+    #     create client $2
+    #     ;;
+    d | del | rm)
+        del $2
+        ;;
+    dd | ddel | fix | fix-all)
+        case $1 in
+        fix)
+            [[ $2 ]] && {
+                change $2 full
+            } || {
+                is_change_id=full && change
+            }
+            return
+            ;;
+        fix-all)
+            is_dont_auto_exit=1
+            msg
+            for v in $(ls $is_conf_dir | grep .json$ | sed '/dynamic-port-.*-link/d'); do
+                msg "fix: $v"
+                change $v full
+            done
+            _green "\nfix 完成.\n"
+            ;;
+        *)
+            is_dont_auto_exit=1
+            [[ ! $2 ]] && {
+                err "无法找到需要删除的参数"
+            } || {
+                for v in ${@:2}; do
+                    del $v
+                done
+            }
+            ;;
+        esac
+        is_dont_auto_exit=
+        manage restart &
+        [[ $is_del_host ]] && manage restart caddy &
+        ;;
+    dns)
+        load dns.sh
+        dns_set ${@:2}
+        ;;
+    debug)
+        is_debug=1
+        get info $2
+        warn "如果需要复制; 请把 *uuid, *password, *host, *key 的值改写, 以避免泄露."
+        ;;
+    fix-config.json)
+        create config.json
+        ;;
+    fix-caddyfile)
+        if [[ $is_caddy ]]; then
+            load caddy.sh
+            caddy_config new
+            manage restart caddy &
+            _green "\nfix 完成.\n"
+        else
+            err "无法执行此操作"
+        fi
+        ;;
+    i | info)
+        info $2
+        ;;
+    ip)
+        get_ip
+        msg $ip
+        ;;
+    in | import)
+        load import.sh
+        ;;
+    log)
+        load log.sh
+        log_set $2
+        ;;
+    url | qr)
+        url_qr $@
+        ;;
+    un | uninstall)
+        uninstall
+        ;;
+    u | up | update | U | update.sh)
+        is_update_name=$2
+        is_update_ver=$3
+        [[ ! $is_update_name ]] && is_update_name=core
+        [[ $1 == 'U' || $1 == 'update.sh' ]] && {
+            is_update_name=sh
+            is_update_ver=
+        }
+        update $is_update_name $is_update_ver
+        ;;
+    ssss | ss2022)
+        get $@
+        ;;
+    s | status)
+        msg "\n$is_core_name $is_core_ver: $is_core_status\n"
+        [[ $is_caddy ]] && msg "Caddy $is_caddy_ver: $is_caddy_status\n"
+        ;;
+    start | stop | r | restart)
+        [[ $2 && $2 != 'caddy' ]] && err "无法识别 ($2), 请使用: $is_core $1 [caddy]"
+        manage $1 $2 &
+        ;;
+    t | test)
+        get test-run
+        ;;
+    reinstall)
+        get $1
+        ;;
+    get-port)
+        get_port
+        msg $tmp_port
+        ;;
+    main)
+        is_main_menu
+        ;;
+    v | ver | version)
+        [[ $is_caddy_ver ]] && is_caddy_ver="/ $(_blue Caddy $is_caddy_ver)"
+        msg "\n$(_green $is_core_name $is_core_ver) / $(_cyan $is_core_name script $is_sh_ver) $is_caddy_ver\n"
+        ;;
+    h | help | --help)
+        load help.sh
+        show_help ${@:2}
+        ;;
+    *)
+        is_try_change=1
+        change test $1
+        if [[ $is_change_id ]]; then
+            unset is_try_change
+            [[ $2 ]] && {
+                change $2 $1 ${@:3}
+            } || {
+                change
+            }
+        else
+            err "无法识别 ($1), 获取帮助请使用: $is_core help"
+        fi
+        ;;
+    esac
+}

+ 83 - 0
src/dns.sh

@@ -0,0 +1,83 @@
+is_dns_list=(
+    119.29.29.29
+    1.1.1.1
+    8.8.8.8
+    h3://dns.google/dns-query
+    h3://cloudflare-dns.com/dns-query
+    h3://family.cloudflare-dns.com/dns-query
+    set
+    none
+)
+dns_set() {
+    if [[ $(echo -e "1.11.99\n$is_core_ver" | sort -V | head -n1) == '1.11.99' ]]; then
+        is_dns_new=1
+    fi
+    if [[ $1 ]]; then
+        case ${1,,} in
+        11 | 1111)
+            is_dns_use=${is_dns_list[0]}
+            ;;
+        88 | 8888)
+            is_dns_use=${is_dns_list[1]}
+            ;;
+        gg | google)
+            is_dns_use=${is_dns_list[2]}
+            ;;
+        cf | cloudflare)
+            is_dns_use=${is_dns_list[3]}
+            ;;
+        nosex | family)
+            is_dns_use=${is_dns_list[4]}
+            ;;
+        set)
+            if [[ $2 ]]; then
+                is_dns_use=${2,,}
+            else
+                ask string is_dns_use "请输入 DNS: "
+            fi
+            ;;
+        none)
+            is_dns_use=none
+            ;;
+        *)
+            err "无法识别 DNS 参数: $@"
+            ;;
+        esac
+    else
+        is_tmp_list=(${is_dns_list[@]})
+        ask list is_dns_use null "\n请选择 DNS:\n"
+        if [[ $is_dns_use == "set" ]]; then
+            ask string is_dns_use "请输入 DNS: "
+        fi
+    fi
+    is_dns_use_bak=$is_dns_use
+    if [[ $is_dns_use == "none" ]]; then
+        cat <<<$(jq '.dns={}' $is_config_json) >$is_config_json
+    else
+        if [[ $is_dns_new ]]; then
+            dns_set_server $is_dns_use
+            cat <<<$(jq '.dns.servers=[{type:"'$is_dns_type'",server:"'$is_dns_use'",domain_resolver:"local"},{tag:"local",type:"local"}]' $is_config_json) >$is_config_json
+        else
+            cat <<<$(jq '.dns.servers=[{address:"'$is_dns_use'",address_resolver:"local"},{tag:"local",address:"local"}]' $is_config_json) >$is_config_json
+        fi
+    fi
+    manage restart &
+    msg "\n已更新 DNS 为: $(_green $is_dns_use_bak)\n"
+}
+dns_set_server() {
+    if [[ $(grep '://' <<<$1) ]]; then
+        is_tmp_dns_set=($(awk -F '://|/' '{print $1, $2}' <<<${1,,}))
+        case ${is_tmp_dns_set[0]} in
+        tcp | udp | tls | https | quic | h3)
+            is_dns_use=${is_tmp_dns_set[1]}
+            is_dns_type=${is_tmp_dns_set[0]}
+            ;;
+        *)
+            err "无法识别 DNS 类型!"
+            ;;
+        esac
+    else
+        is_dns_use=$1
+        is_dns_type=udp
+    fi
+}

+ 67 - 0
src/download.sh

@@ -0,0 +1,67 @@
+get_latest_version() {
+    case $1 in
+    core)
+        name=$is_core_name
+        url="https://api.github.com/repos/${is_core_repo}/releases/latest?v=$RANDOM"
+        ;;
+    sh)
+        name="$is_core_name 脚本"
+        url="https://api.github.com/repos/$is_sh_repo/releases/latest?v=$RANDOM"
+        ;;
+    caddy)
+        name="Caddy"
+        url="https://api.github.com/repos/$is_caddy_repo/releases/latest?v=$RANDOM"
+        ;;
+    esac
+    latest_ver=$(_wget -qO- $url | grep tag_name | grep -E -o 'v([0-9.]+)')
+    [[ ! $latest_ver ]] && {
+        err "获取 ${name} 最新版本失败."
+    }
+    unset name url
+}
+download() {
+    latest_ver=$2
+    [[ ! $latest_ver ]] && get_latest_version $1
+    # tmp dir
+    tmpdir=$(mktemp -u)
+    [[ ! $tmpdir ]] && {
+        tmpdir=/tmp/tmp-$RANDOM
+    }
+    mkdir -p $tmpdir
+    case $1 in
+    core)
+        name=$is_core_name
+        tmpfile=$tmpdir/$is_core.tar.gz
+        link="https://github.0030703.xyz/${is_core_repo}/releases/download/${latest_ver}/${is_core}-${latest_ver:1}-linux-${is_arch}.tar.gz"
+        download_file
+        tar zxf $tmpfile --strip-components 1 -C $is_core_dir/bin
+        chmod +x $is_core_bin
+        ;;
+    sh)
+        name="$is_core_name 脚本"
+        tmpfile=$tmpdir/sh.tar.gz
+        link="https://github.0030703.xyz/${is_sh_repo}/releases/download/${latest_ver}/code.tar.gz"
+        download_file
+        tar zxf $tmpfile -C $is_sh_dir
+        chmod +x $is_sh_bin ${is_sh_bin/$is_core/sb}
+        ;;
+    caddy)
+        name="Caddy"
+        tmpfile=$tmpdir/caddy.tar.gz
+        # https://github.0030703.xyz/caddyserver/caddy/releases/download/v2.6.4/caddy_2.6.4_linux_amd64.tar.gz
+        link="https://github.0030703.xyz/${is_caddy_repo}/releases/download/${latest_ver}/caddy_${latest_ver:1}_linux_${is_arch}.tar.gz"
+        download_file
+        tar zxf $tmpfile -C $tmpdir
+        cp -f $tmpdir/caddy $is_caddy_bin
+        chmod +x $is_caddy_bin
+        ;;
+    esac
+    rm -rf $tmpdir
+    unset latest_ver
+}
+download_file() {
+    if ! _wget -t 5 -c $link -O $tmpfile; then
+        rm -rf $tmpdir
+        err "\n下载 ${name} 失败.\n"
+    fi
+}

+ 94 - 0
src/help.sh

@@ -0,0 +1,94 @@
+show_help() {
+    case $1 in
+    api | x25519 | tls | run | uuid | version)
+        $is_core_bin help $1 ${@:2}
+        ;;
+    *)
+        [[ $1 ]] && warn "未知选项 '$1'"
+        msg "$is_core_name script $is_sh_ver by $author"
+        msg "Usage: $is_core [options]... [args]... "
+        msg
+        help_info=(
+            "基本:"
+            "   v, version                                      显示当前版本"
+            "   ip                                              返回当前主机的 IP"
+            "   pbk                                             同等于 $is_core generate reality-keypair"
+            "   get-port                                        返回一个可用的端口"
+            "   ss2022                                          返回一个可用于 Shadowsocks 2022 的密码\n"
+            "一般:"
+            "   a, add [protocol] [args... | auto]              添加配置"
+            "   c, change [name] [option] [args... | auto]      更改配置"
+            "   d, del [name]                                   删除配置**"
+            "   i, info [name]                                  查看配置"
+            "   qr [name]                                       二维码信息"
+            "   url [name]                                      URL 信息"
+            "   log                                             查看日志"
+            # "   logerr                                          查看错误日志\n"
+            "更改:"
+            # "   dp, dynamicport [name] [start | auto] [end]     更改动态端口"
+            "   full [name] [...]                               更改多个参数"
+            "   id [name] [uuid | auto]                         更改 UUID"
+            "   host [name] [domain]                            更改域名"
+            "   port [name] [port | auto]                       更改端口"
+            "   path [name] [path | auto]                       更改路径"
+            "   passwd [name] [password | auto]                 更改密码"
+            "   key [name] [Private key | atuo] [Public key]    更改密钥"
+            # "   type [name] [type | auto]                       更改伪装类型"
+            "   method [name] [method | auto]                   更改加密方式"
+            "   sni [name] [ ip | domain]                       更改 serverName"
+            # "   seed [name] [seed | auto]                       更改 mKCP seed"
+            "   new [name] [...]                                更改协议"
+            "   web [name] [domain]                             更改伪装网站\n"
+            "进阶:"
+            "   dns [...]                                       设置 DNS"
+            "   dd, ddel [name...]                              删除多个配置**"
+            "   fix [name]                                      修复一个配置"
+            "   fix-all                                         修复全部配置"
+            "   fix-caddyfile                                   修复 Caddyfile"
+            "   fix-config.json                                 修复 config.json"
+            "   import                                          导入 xray/v2ray 脚本配置\n"
+            "管理:"
+            "   un, uninstall                                   卸载"
+            "   u, update [core | sh | caddy] [ver]             更新"
+            "   U, update.sh                                    更新脚本"
+            "   s, status                                       运行状态"
+            "   start, stop, restart [caddy]                    启动, 停止, 重启"
+            "   t, test                                         测试运行"
+            "   reinstall                                       重装脚本\n"
+            "测试:"
+            # "   client, genc [name]                             显示用于客户端 JSON, 仅供参考"
+            "   debug [name]                                    显示一些 debug 信息, 仅供参考"
+            "   gen [...]                                       同等于 add, 但只显示 JSON 内容, 不创建文件, 测试使用"
+            "   no-auto-tls [...]                               同等于 add, 但禁止自动配置 TLS, 可用于 *TLS 相关协议"
+            # "   xapi [...]                                      同等于 $is_core api, 但 API 后端使用当前运行的 $is_core_name 服务\n"
+            "其他:"
+            "   bbr                                             启用 BBR, 如果支持"
+            "   bin [...]                                       运行 $is_core_name 命令, 例如: $is_core bin help"
+            "   [...] [...]                                     兼容绝大多数的 $is_core_name 命令, 例如: $is_core_name generate uuid"
+            "   h, help                                         显示此帮助界面\n"
+        )
+        for v in "${help_info[@]}"; do
+            msg "$v"
+        done
+        msg "谨慎使用 del, ddel, 此选项会直接删除配置; 无需确认"
+        msg "反馈问题) $(msg_ul https://github.com/${is_sh_repo}/issues) "
+        msg "文档(doc) $(msg_ul https://233boy.com/$is_core/$is_core-script/)"
+        ;;
+
+    esac
+}
+
+about() {
+    ####### 要点13脸吗只会改我链接的小人 #######
+    unset c n m s b
+    msg
+    msg "网站: $(msg_ul https://233boy.com)"
+    msg "频道: $(msg_ul https://t.me/tg2333)"
+    msg "群组: $(msg_ul https://t.me/tg233boy)"
+    msg "Github: $(msg_ul https://github.com/${is_sh_repo})"
+    msg "Twitter: $(msg_ul https://twitter.com/ai233boy)"
+    msg "$is_core_name site: $(msg_ul https://sing-box.sagernet.org/)"
+    msg "$is_core_name core: $(msg_ul https://github.com/${is_core_repo})"
+    msg
+    ####### 要点13脸吗只会改我链接的小人 #######
+}

+ 79 - 0
src/import.sh

@@ -0,0 +1,79 @@
+is_xray_sh=/etc/xray/sh/src/core.sh
+is_v2ray_sh=/etc/v2ray/sh/src/core.sh
+is_xray_conf=/etc/xray/conf
+is_v2ray_conf=/etc/v2ray/conf
+in_conf() {
+    is_conf_args=$(jq '.inbounds[0]|.protocol,.port,(.settings|(.clients[0]|.id,.password),.method,.password,.port,.address,(.accounts[0]|.user,.pass)),(.streamSettings|.network,.security,.tcpSettings.header.type,(.wsSettings|.path,.headers.Host),(.httpSettings|.path,.host[0]),(.realitySettings|.serverNames[0],.publicKey,.privateKey))' $1)
+    [[ $? != 0 ]] && warn "无法读取此文件: $1" && return
+    is_up_var_set=(null is_protocol port uuid trojan_password ss_method ss_password door_port door_addr is_socks_user is_socks_pass net is_reality net_type ws_path ws_host h2_path h2_host is_servername is_public_key is_private_key)
+    i=0
+    for v in $(sed 's/""/null/g;s/"//g' <<<"$is_conf_args"); do
+        ((i++))
+        export ${is_up_var_set[$i]}="${v}"
+    done
+    for v in ${is_up_var_set[@]}; do
+        [[ ${!v} == 'null' ]] && unset $v
+    done
+    path="${ws_path}${h2_path}"
+    host="${ws_host}${h2_host}"
+    [[ ! $uuid ]] && uuid=$trojan_password
+    if [[ $host ]]; then
+        if [[ $is_caddy && -f $is_caddy_conf/$host.conf ]]; then
+            tmp_tlsport=$(grep -E -o "$host:[1-9][0-9]?+" $is_caddy_conf/$host.conf | sed s/.*://)
+        fi
+        [[ $tmp_tlsport ]] && https_port=$tmp_tlsport
+        add $is_protocol-$net-tls
+    else
+        case $is_protocol in
+        vmess | vless)
+            [[ $net_type == "http" ]] && {
+                net=http
+                is_tips_msg="新配置文件名: (VMess-HTTP-$port.json)"
+            }
+            [[ $is_reality == "reality" ]] && net=reality
+            add $net
+            ;;
+        dokodemo-door)
+            add door
+            is_tips_msg="新配置文件名: (Direct-$port.json)"
+            ;;
+        *socks*)
+            add $is_protocol
+            ;;
+        *)
+            is_not_in_conf=1
+            msg "不支持导入 $1"
+            ;;
+
+        esac
+    fi
+    [[ ! $is_not_in_conf ]] && msg "导入: $1 $is_tips_msg" && rm $1
+}
+is_change=1
+is_dont_auto_exit=1
+is_dont_test_host=1
+if [[ -f $is_xray_sh && -d $is_xray_conf ]]; then
+    is_list=($(ls $is_xray_conf | grep .json | grep -E -iv 'kcp|grpc|dynamic|quic' | sed "s#^#$is_xray_conf/#"))
+fi
+if [[ -f $is_v2ray_sh && -d $is_v2ray_conf ]]; then
+    is_list+=($(ls $is_v2ray_conf | grep .json | grep -E -iv 'kcp|grpc|dynamic|quic' | sed "s#^#$is_v2ray_conf/#"))
+fi
+[[ ${is_list[@]} =~ "xray" ]] && is_xray_in=1
+[[ ${is_list[@]} =~ "v2ray" ]] && is_v2ray_in=1
+[[ $is_xray_in ]] && xray stop
+[[ $is_v2ray_in ]] && v2ray stop
+if [[ ${is_list[@]} ]]; then
+    msg "开始导入配置..."
+    for i in ${is_list[@]}; do
+        in_conf $i &
+    done
+    wait
+    is_dont_auto_exit=
+    manage restart &
+    [[ $is_xray_in ]] && xray restart &
+    [[ $is_v2ray_in ]] && v2ray restart &
+    [[ ${is_list[@],,} =~ "tls" && $is_caddy ]] && manage restart caddy &
+
+else
+    err "没有找到可导入的配置..."
+fi

+ 141 - 0
src/init.sh

@@ -0,0 +1,141 @@
+#!/bin/bash
+
+author=233boy
+# github=https://github.com/233boy/sing-box
+
+# bash fonts colors
+red='\e[31m'
+yellow='\e[33m'
+gray='\e[90m'
+green='\e[92m'
+blue='\e[94m'
+magenta='\e[95m'
+cyan='\e[96m'
+none='\e[0m'
+
+_red() { echo -e ${red}$@${none}; }
+_blue() { echo -e ${blue}$@${none}; }
+_cyan() { echo -e ${cyan}$@${none}; }
+_green() { echo -e ${green}$@${none}; }
+_yellow() { echo -e ${yellow}$@${none}; }
+_magenta() { echo -e ${magenta}$@${none}; }
+_red_bg() { echo -e "\e[41m$@${none}"; }
+
+_rm() {
+    rm -rf "$@"
+}
+_cp() {
+    cp -rf "$@"
+}
+_sed() {
+    sed -i "$@"
+}
+_mkdir() {
+    mkdir -p "$@"
+}
+
+is_err=$(_red_bg 错误!)
+is_warn=$(_red_bg 警告!)
+
+err() {
+    echo -e "\n$is_err $@\n"
+    [[ $is_dont_auto_exit ]] && return
+    exit 1
+}
+
+warn() {
+    echo -e "\n$is_warn $@\n"
+}
+
+# load bash script.
+load() {
+    . $is_sh_dir/src/$1
+}
+
+# wget add --no-check-certificate
+_wget() {
+    # [[ $proxy ]] && export https_proxy=$proxy
+    wget --no-check-certificate "$@"
+}
+
+# yum or apt-get
+cmd=$(type -P apt-get || type -P yum)
+
+# x64
+case $(arch) in
+amd64 | x86_64)
+    is_arch="amd64"
+    ;;
+*aarch64* | *armv8*)
+    is_arch="arm64"
+    ;;
+*)
+    err "此脚本仅支持 64 位系统..."
+    ;;
+esac
+
+is_core=sing-box
+is_core_name=sing-box
+is_core_dir=/develop/$is_core
+is_core_bin=$is_core_dir/bin/$is_core
+is_core_repo=SagerNet/$is_core
+is_conf_dir=$is_core_dir/conf
+is_log_dir=/var/log/$is_core
+is_sh_bin=/usr/local/bin/$is_core
+is_sh_dir=$is_core_dir/sh
+is_sh_repo=$author/$is_core
+is_pkg="wget unzip tar qrencode"
+is_config_json=$is_core_dir/config.json
+is_caddy_bin=/usr/local/bin/caddy
+is_caddy_dir=/etc/caddy
+is_caddy_repo=caddyserver/caddy
+is_caddyfile=$is_caddy_dir/Caddyfile
+is_caddy_conf=$is_caddy_dir/$author
+is_caddy_service=$(systemctl list-units --full -all | grep caddy.service)
+is_http_port=80
+is_https_port=443
+
+# core ver
+is_core_ver=$($is_core_bin version | head -n1 | cut -d " " -f3)
+
+# tmp tls key
+is_tls_cer=$is_core_dir/bin/tls.cer
+is_tls_key=$is_core_dir/bin/tls.key
+[[ ! -f $is_tls_cer || ! -f $is_tls_key ]] && {
+    is_tls_tmp=${is_tls_key/key/tmp}
+    $is_core_bin generate tls-keypair tls -m 456 >$is_tls_tmp
+    awk '/BEGIN PRIVATE KEY/,/END PRIVATE KEY/' $is_tls_tmp >$is_tls_key
+    awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/' $is_tls_tmp >$is_tls_cer
+    rm $is_tls_tmp
+}
+
+if [[ $(pgrep -f $is_core_bin) ]]; then
+    is_core_status=$(_green running)
+else
+    is_core_status=$(_red_bg stopped)
+    is_core_stop=1
+fi
+if [[ -f $is_caddy_bin && -d $is_caddy_dir && $is_caddy_service ]]; then
+    is_caddy=1
+    # fix caddy run; ver >= 2.8.2
+    [[ ! $(grep '\-\-adapter caddyfile' /lib/systemd/system/caddy.service) ]] && {
+        load systemd.sh
+        install_service caddy
+        systemctl restart caddy &
+    }
+    is_caddy_ver=$($is_caddy_bin version | head -n1 | cut -d " " -f1)
+    is_tmp_http_port=$(grep -E '^ {2,}http_port|^http_port' $is_caddyfile | grep -E -o [0-9]+)
+    is_tmp_https_port=$(grep -E '^ {2,}https_port|^https_port' $is_caddyfile | grep -E -o [0-9]+)
+    [[ $is_tmp_http_port ]] && is_http_port=$is_tmp_http_port
+    [[ $is_tmp_https_port ]] && is_https_port=$is_tmp_https_port
+    if [[ $(pgrep -f $is_caddy_bin) ]]; then
+        is_caddy_status=$(_green running)
+    else
+        is_caddy_status=$(_red_bg stopped)
+        is_caddy_stop=1
+    fi
+fi
+
+load core.sh
+[[ ! $args ]] && args=main
+main $args

+ 44 - 0
src/log.sh

@@ -0,0 +1,44 @@
+is_log_level_list=(
+    trace
+    debug
+    info
+    warn
+    error
+    fatal
+    panic
+    none
+    del
+)
+log_set() {
+    if [[ $1 ]]; then
+        for v in ${is_log_level_list[@]}; do
+            [[ $(grep -E -i "^${1,,}$" <<<$v) ]] && is_log_level_use=$v && break
+        done
+        [[ ! $is_log_level_use ]] && {
+            err "无法识别 log 参数: $@ \n请使用 $is_core log [${is_log_level_list[@]}] 进行相关设定.\n备注: del 参数仅临时删除 log 文件; none 参数将不会生成 log 文件."
+        }
+        case $is_log_level_use in
+        del)
+            rm -rf $is_log_dir/*.log
+            msg "\n $(_green 已临时删除 log 文件, 如果你想要完全禁止生成 log 文件请使用: $is_core log none)\n"
+            ;;
+        none)
+            rm -rf $is_log_dir/*.log
+            cat <<<$(jq '.log={"disabled":true}' $is_config_json) >$is_config_json
+            ;;
+        *)
+            cat <<<$(jq '.log={output:"/var/log/'$is_core'/access.log",level:"'$is_log_level_use'","timestamp":true}' $is_config_json) >$is_config_json
+            ;;
+        esac
+
+        manage restart &
+        [[ $1 != 'del' ]] && msg "\n已更新 Log 设定为: $(_green $is_log_level_use)\n"
+    else
+        if [[ -f $is_log_dir/access.log ]]; then
+            msg "\n 提醒: 按 $(_green Ctrl + C) 退出\n"
+            tail -f $is_log_dir/access.log
+        else
+            err "无法找到 log 文件."
+        fi
+    fi
+}

+ 58 - 0
src/systemd.sh

@@ -0,0 +1,58 @@
+install_service() {
+    case $1 in
+    $is_core)
+        is_doc_site=https://sing-box.sagernet.org/
+        cat >/lib/systemd/system/$is_core.service <<<"
+[Unit]
+Description=$is_core_name Service
+Documentation=$is_doc_site
+After=network.target nss-lookup.target
+
+[Service]
+#User=nobody
+User=root
+NoNewPrivileges=true
+ExecStart=$is_core_bin run -c $is_config_json -C $is_conf_dir
+Restart=on-failure
+RestartPreventExitStatus=23
+LimitNPROC=10000
+LimitNOFILE=1048576
+PrivateTmp=true
+ProtectSystem=full
+#CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
+#AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
+
+[Install]
+WantedBy=multi-user.target"
+        ;;
+    caddy)
+        cat >/lib/systemd/system/caddy.service <<<"
+#https://github.com/caddyserver/dist/blob/master/init/caddy.service
+[Unit]
+Description=Caddy
+Documentation=https://caddyserver.com/docs/
+After=network.target network-online.target
+Requires=network-online.target
+
+[Service]
+Type=notify
+User=root
+Group=root
+ExecStart=$is_caddy_bin run --environ --config $is_caddyfile --adapter caddyfile
+ExecReload=$is_caddy_bin reload --config $is_caddyfile --adapter caddyfile
+TimeoutStopSec=5s
+LimitNPROC=10000
+LimitNOFILE=1048576
+PrivateTmp=true
+ProtectSystem=full
+#AmbientCapabilities=CAP_NET_BIND_SERVICE
+
+[Install]
+WantedBy=multi-user.target"
+        ;;
+    esac
+
+    # enable, reload
+    systemctl enable $1
+    systemctl daemon-reload
+}